diff --git a/CMakeLists.txt b/CMakeLists.txt index f6735892f2..d989aee505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,10 +43,7 @@ set_build_output_directories() configure_openspace_version(0 1 0 "prerelease-5") option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) - option(OPENSPACE_DISABLE_EXTERNAL_WARNINGS "Disable warnings in external libraries" ON) -option(OPENSPACE_BUILD_GUI_APPLICATIONS "Build GUI Applications" OFF) - include(src/CMakeLists.txt) create_openspace_target() diff --git a/apps/Launcher/CMakeLists.txt b/apps/Launcher/CMakeLists.txt index 2cf98ae128..df27f03040 100644 --- a/apps/Launcher/CMakeLists.txt +++ b/apps/Launcher/CMakeLists.txt @@ -25,24 +25,32 @@ set(APPLICATION_NAME Launcher) set(APPLICATION_LINK_TO_OPENSPACE ON) +include (${OPENSPACE_CMAKE_EXT_DIR}/handle_external_library.cmake) + +set(application_path ${OPENSPACE_APPS_DIR}/Launcher) + set(SOURCE_FILES - ${OPENSPACE_APPS_DIR}/Launcher/main.cpp - ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.cpp - ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.cpp - ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.cpp + ${application_path}/main.cpp + ${application_path}/infowidget.cpp + ${application_path}/mainwindow.cpp + ${application_path}/shortcutwidget.cpp + ${application_path}/syncwidget.cpp ) set(HEADER_FILES - ${OPENSPACE_APPS_DIR}/Launcher/mainwindow.h - ${OPENSPACE_APPS_DIR}/Launcher/shortcutwidget.h - ${OPENSPACE_APPS_DIR}/Launcher/syncwidget.h + ${application_path}/infowidget.h + ${application_path}/mainwindow.h + ${application_path}/shortcutwidget.h + ${application_path}/syncwidget.h ) find_package(Qt5Widgets) find_package(Qt5Network) +set(MOC_FILES "") qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) -qt5_add_resources(RESOURCE_FILES ${OPENSPACE_APPS_DIR}/Launcher/files.qrc) +set(RESOURCE_FILES "") +qt5_add_resources(RESOURCE_FILES ${application_path}/files.qrc) add_executable(${APPLICATION_NAME} MACOSX_BUNDLE ${SOURCE_FILES} @@ -57,8 +65,12 @@ target_link_libraries(${APPLICATION_NAME} ) if (APPLE) -INSTALL(CODE " - include(BundleUtilities) - fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/Launcher.app/Contents/MacOS/Launcher\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") - " COMPONENT Runtime) + INSTALL(CODE " + include(BundleUtilities) + fixup_bundle(\"/Users/alex/Development/OpenSpace/bin/openspace/Debug/Launcher.app/Contents/MacOS/Launcher\" \"/Users/alex/Development/OpenSpace/bin/openspace/Debug/TimelineView.app/Contents/plugins/platforms/libqcocoa.dylib\" \"\") + " COMPONENT Runtime) endif () + +# Libtorrent +include_external_library(${APPLICATION_NAME} libtorrent ${application_path}/ext/libtorrent) +target_include_directories(${APPLICATION_NAME} PUBLIC SYSTEM ${application_path}/ext/libtorrent/include) diff --git a/apps/Launcher/ext/libtorrent/AUTHORS b/apps/Launcher/ext/libtorrent/AUTHORS new file mode 100644 index 0000000000..b26128709f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/AUTHORS @@ -0,0 +1,26 @@ +Written by Arvid Norberg. Copyright (c) 2003-2014 + +Contributions by: +Andrei Kurushin +Steven Siloti +Thomas Fischer +Massaroddel +Tianhao Qiu. +Shyam +Magnus Jonsson +Daniel Wallin +Cory Nelson +Stas Khirman +Ryan Norton +Andrew Resch + +Building and maintainance of the autotools scripts: +Michael Wojciechowski +Peter Koeleman + +Thanks to Reimond Retz for bugfixes, suggestions and testing + +Thanks to University of UmeŚ for providing development and test hardware. + +Project is hosted by sourceforge. + diff --git a/apps/Launcher/ext/libtorrent/CMakeLists.txt b/apps/Launcher/ext/libtorrent/CMakeLists.txt new file mode 100644 index 0000000000..b320846e13 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/CMakeLists.txt @@ -0,0 +1,239 @@ +cmake_minimum_required(VERSION 2.6) +project(libtorrent) +set (SOVERSION "8") +set (VERSION "1.0.5") + +set(sources + web_connection_base + alert + alert_manager + allocator + asio + assert + bandwidth_limit + bandwidth_manager + bandwidth_queue_entry + bloom_filter + chained_buffer + connection_queue + create_torrent + disk_buffer_holder + entry + error_code + file_storage + lazy_bdecode + escape_string + string_util + file + gzip + hasher + http_connection + http_stream + http_parser + i2p_stream + identify_client + ip_filter + ip_voter + peer_connection + bt_peer_connection + web_peer_connection + http_seed_connection + instantiate_connection + natpmp + packet_buffer + piece_picker + policy + puff + random + rss + session + session_impl + settings + socket_io + socket_type + socks5_stream + stat + storage + time + timestamp_history + torrent + torrent_handle + torrent_info + tracker_manager + http_tracker_connection + utf8 + udp_tracker_connection + udp_socket + upnp + utp_socket_manager + utp_stream + logger + file_pool + lsd + disk_buffer_pool + disk_io_thread + enum_net + broadcast_socket + magnet_uri + parse_url + ConvertUTF + thread + xml_parse + +# -- extensions -- + metadata_transfer + ut_pex + ut_metadata + smart_ban + lt_trackers +) + +# -- kademlia -- +set(kademlia_sources + dht_tracker + node + refresh + rpc_manager + find_data + node_id + routing_table + traversal_algorithm + logging + item + get_peers + get_item +) + +# -- ed25519 -- +set(ed25519_sources + add_scalar + fe + ge + key_exchange + keypair + sc + seed + sha512 + sign + verify +) + +set(includes include ed25519/src) + +option(LIBTORRENT_pool-allocators "Uses a pool allocator for disk and piece buffers" ON) +option(LIBTORRENT_dht "enable support for Mainline DHT" OFF) +option(LIBTORRENT_unicode "enable unicode support" ON) + +set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release FORCE) +endif () + +# add_definitions() doesn't seem to let you say wich build type to apply it to +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DTORRENT_DEBUG") +if (UNIX) + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +endif () + +list(APPEND sources sha1) + +foreach(s ${sources}) + list(APPEND sources2 src/${s}) +endforeach () + +if (LIBTORRENT_dht) + foreach(s ${kademlia_sources}) + list(APPEND sources2 src/kademlia/${s}) + endforeach(s) + foreach(s ${ed25519_sources}) + list(APPEND sources2 ed25519/src/${s}) + endforeach(s) +endif () + +add_library(libtorrent STATIC ${sources2}) + +target_include_directories(libtorrent PUBLIC ${includes}) +target_compile_definitions(libtorrent PUBLIC + TORRENT_DISABLE_ENCRYPTION + TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_DISABLE_GEO_IP + BOOST_ASIO_SEPARATE_COMPILATION + BOOST_EXCEPTION_DISABLE + BOOST_ASIO_ENABLE_CANCELIO + _FILE_OFFSET_BITS=64 +) + +if (NOT LIBTORRENT_dht) + target_compile_definitions(libtorrent PUBLIC TORRENT_DISABLE_DHT) +endif () + +if (NOT LIBTORRENT_pool-allocators) + target_compile_definitions(libtorrent PUBLIC TORRENT_DISABLE_POOL_ALLOCATOR) +endif() + +if (LIBTORRENT_unicode) + add_definitions(-DUNICODE -D_UNICODE) +endif() + + +if (NOT MSVC) + target_compile_options(libtorrent PUBLIC "-fvisibility=hidden" "-fvisibility-inlines-hidden") + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + # set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility-inlines-hidden") +endif () + +# Boost +set(Boost_USE_STATIC_LIBS ON) +if (NOT DEFINED Boost_INCLUDE_DIR OR NOT DEFINED Boost_LIBRARIES) + find_package(Boost COMPONENTS system thread date_time chrono) +endif () +target_include_directories(libtorrent PUBLIC ${Boost_INCLUDE_DIR}) +target_link_libraries(libtorrent ${Boost_LIBRARIES}) + +if (WIN32) + target_link_libraries(libtorrent wsock32 ws2_32 Iphlpapi.lib) + target_compile_definitions(libtorrent PUBLIC "_WIN32_WINNT=0x0600") +endif () + +# this works around a bug in asio in boost-1.39 +#add_definitions(-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 -D__USE_W32_SOCKETS -DWIN32_LEAN_AND_MEAN ) + +if (MSVC) + target_compile_options(libtorrent PUBLIC "/EHsc" "/Zc:wchar_t" "/Zc:forScope" "/MP") + target_compile_definitions(libtorrent PUBLIC _SCL_SECURE_NO_DEPRECATE _CRT_SECURE_NO_DEPRECATE) +else () + target_compile_options(libtorrent PUBLIC "-fexceptions" "-Wno-c++11-extensions") +endif () + +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + target_compile_options(libtorrent PUBLIC "-fcolor-diagnostics") +endif () + +set_target_properties(libtorrent PROPERTIES + SOVERSION ${SOVERSION}) + +get_property(COMPILETIME_OPTIONS_LIST + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIRECTORY} + PROPERTY COMPILE_DEFINITIONS + ) +foreach (s ${COMPILETIME_OPTIONS_LIST}) + set (COMPILETIME_OPTIONS "${COMPILETIME_OPTIONS} -D${s}") +endforeach () + +configure_file(libtorrent-rasterbar-cmake.pc.in libtorrent-rasterbar.pc) + +string (COMPARE EQUAL "${CMAKE_SIZEOF_VOID_P}" "8" IS64BITS) + +if (IS64BITS AND RESPECTLIB64) + set(LIBDIR "lib64") +else() + set(LIBDIR "lib") +endif() + +install(TARGETS libtorrent DESTINATION ${LIBDIR}) +install(DIRECTORY include/libtorrent + DESTINATION include + PATTERN ".svn" EXCLUDE) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtorrent-rasterbar.pc DESTINATION ${LIBDIR}/pkgconfig) diff --git a/apps/Launcher/ext/libtorrent/COPYING b/apps/Launcher/ext/libtorrent/COPYING new file mode 100644 index 0000000000..2dcc295795 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of Rasterbar Software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/apps/Launcher/ext/libtorrent/ChangeLog b/apps/Launcher/ext/libtorrent/ChangeLog new file mode 100644 index 0000000000..19c97bc575 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ChangeLog @@ -0,0 +1,1305 @@ +1.0.5 release + + * improve ip_voter to avoid flapping + * fixed bug when max_peerlist_size was set to 0 + * fix issues with missing exported symbols when building dll + * fix division by zero bug in edge case while connecting peers + +1.0.4 release + + * fix bug in python binding for file_progress on torrents with no metadata + * fix assert when removing a connected web seed + * fix bug in tracker timeout logic + * switch UPnP post back to HTTP 1.1 + * support conditional DHT get + * OpenSSL build fixes + * fix DHT scrape bug + +1.0.3 release + + * python binding build fix for boost-1.57.0 + * add --enable-export-all option to configure script, to export all symbols + from libtorrent + * fix if_nametoindex build error on windows + * handle overlong utf-8 sequences + * fix link order bug in makefile for python binding + * fix bug in interest calculation, causing premature disconnects + * tweak flag_override_resume_data semantics to make more sense (breaks + backwards compatibility of edge-cases) + * improve DHT bootstrapping and periodic refresh + * improve DHT maintanence performance (by pinging instead of full lookups) + * fix bug in DHT routing table node-id prefix optimization + * fix incorrect behavior of flag_use_resume_save_path + * fix protocol race-condition in super seeding mode + * support read-only DHT nodes + * remove unused partial hash DHT lookups + * remove potentially privacy leaking extension (non-anonymous mode) + * peer-id connection ordering fix in anonymous mode + * mingw fixes + +1.0.2 release + + * added missing force_proxy to python binding + * anonymous_mode defaults to false + * make DHT DOS detection more forgiving to bursts + * support IPv6 multicast in local service discovery + * simplify CAS function in DHT put + * support IPv6 traffic class (via the TOS setting) + * made uTP re-enter slow-start after time-out + * fixed uTP upload performance issue + * fix missing support for DHT put salt + +1.0.1 release + + * fix alignment issue in bitfield + * improved error handling of gzip + * fixed crash when web seeds redirect + * fix compiler warnings + +1.0 release + + * fix bugs in convert_to/from_native() on windows + * fix support for web servers not supporting keepalive + * support storing save_path in resume data + * don't use full allocation on network drives (on windows) + * added clear_piece_deadlines() to remove all piece deadlines + * improve queuing logic of inactive torrents (dont_count_slow_torrents) + * expose optimistic unchoke logic to plugins + * fix issue with large UDP packets on windows + * remove set_ratio() feature + * improve piece_deadline/streaming + * honor pieces with priority 7 in sequential download mode + * simplified building python bindings + * make ignore_non_routers more forgiving in the case there are no UPnP + devices at a known router. Should improve UPnP compatibility. + * include reason in peer_blocked_alert + * support magnet links wrapped in .torrent files + * rate limiter optimization + * rate limiter overflow fix (for very high limits) + * non-auto-managed torrents no longer count against the torrent limits + * handle DHT error responses correctly + * allow force_announce to only affect a single tracker + * add moving_storage field to torrent_status + * expose UPnP and NAT-PMP mapping in session object + * DHT refactoring and support for storing arbitrary data with put and get + * support building on android + * improved support for web seeds that don't support keep-alive + * improve DHT routing table to return better nodes (lower RTT and closer + to target) + * don't use pointers to resume_data and file_priorities in + add_torrent_params + * allow moving files to absolute paths, out of the download directory + * make move_storage more generic to allow both overwriting files as well + as taking existing ones + * fix choking issue at high upload rates + * optimized rate limiter + * make disk cache pool allocator configurable + * fix library ABI to not depend on logging being enabled + * use hex encoding instead of base32 in create_magnet_uri + * include name, save_path and torrent_file in torrent_status, for + improved performance + * separate anonymous mode and force-proxy mode, and tighten it up a bit + * add per-tracker scrape information to announce_entry + * report errors in read_piece_alert + * DHT memory optimization + * improve DHT lookup speed + * improve support for windows XP and earlier + * introduce global connection priority for improved swarm performance + * make files deleted alert non-discardable + * make built-in sha functions not conflict with libcrypto + * improve web seed hash failure case + * improve DHT lookup times + * uTP path MTU discovery improvements + * optimized the torrent creator optimizer to scale significantly better + with more files + * fix uTP edge case where udp socket buffer fills up + * fix nagle implementation in uTP + + * fix bug in error handling in protocol encryption + +0.16.18 release + + * fix uninitialized values in DHT DOS mitigation + * fix error handling in file::phys_offset + * fix bug in HTTP scrape response parsing + * enable TCP keepalive for socks5 connection for UDP associate + * fix python3 support + * fix bug in lt_donthave extension + * expose i2p_alert to python. cleaning up of i2p connection code + * fixed overflow and download performance issue when downloading at high rates + * fixed bug in add_torrent_alert::message for magnet links + * disable optimistic disconnects when connection limit is low + * improved error handling of session::listen_on + * suppress initial 'completed' announce to trackers added with replace_trackers + after becoming a seed + * SOCKS4 fix for trying to connect over IPv6 + * fix saving resume data when removing all trackers + * fix bug in udp_socket when changing socks5 proxy quickly + +0.16.17 release + + * don't fall back on wildcard port in UPnP + * fix local service discovery for magnet links + * fix bitfield issue in file_storage + * added work-around for MingW issue in file I/O + * fixed sparse file detection on windows + * fixed bug in gunzip + * fix to use proxy settings when adding .torrent file from URL + * fix resume file issue related to daylight savings time on windows + * improve error checking in lazy_bdecode + +0.16.16 release + + * add missing add_files overload to the python bindings + * improve error handling in http gunzip + * fix debug logging for banning web seeds + * improve support for de-selected files in full allocation mode + * fix dht_bootstrap_alert being posted + * SetFileValidData fix on windows (prevents zero-fill) + * fix minor lock_files issue on unix + +0.16.15 release + + * fix mingw time_t 64 bit issue + * fix use of SetFileValidData on windows + * fix crash when using full allocation storage mode + * improve error_code and error_category support in python bindings + * fix python binding for external_ip_alert + +0.16.14 release + + * make lt_tex more robust against bugs and malicious behavior + * HTTP chunked encoding fix + * expose file_granularity flag to python bindings + * fix DHT memory error + * change semantics of storage allocation to allocate on first write rather + than on startup (behaves better with changing file priorities) + * fix resend logic in response to uTP SACK messages + * only act on uTP RST packets with correct ack_nr + * make uTP errors log in normal log mode (not require verbose) + * deduplicate web seed entries from torrent files + * improve error reporting from lazy_decode() + +0.16.13 release + + * fix auto-manage issue when pausing session + * fix bug in non-sparse mode on windows, causing incorrect file errors to + be generated + * fix set_name() on file_storage actually affecting save paths + * fix large file support issue on mingw + * add some error handling to set_piece_hashes() + * fix completed-on timestamp to not be clobbered on each startup + * fix deadlock caused by some UDP tracker failures + * fix potential integer overflow issue in timers on windows + * minor fix to peer_proportional mixed_mode algorithm (TCP limit could go + too low) + * graceful pause fix + * i2p fixes + * fix issue when loading certain malformed .torrent files + * pass along host header with http proxy requests and possible + http_connection shutdown hang + +0.16.12 release + + * fix building with C++11 + * fix IPv6 support in UDP socket (uTP) + * fix mingw build issues + * increase max allowed outstanding piece requests from peers + * uTP performance improvement. only fast retransmit one packet at a time + * improve error message for 'file too short' + * fix piece-picker stat bug when only selecting some files for download + * fix bug in async_add_torrent when settings file_priorities + * fix boost-1.42 support for python bindings + * fix memory allocation issue (virtual addres space waste) on windows + +0.16.11 release + + * fix web seed URL double escape issue + * fix string encoding issue in alert messages + * fix SSL authentication issue + * deprecate std::wstring overloads. long live utf-8 + * improve time-critical pieces feature (streaming) + * introduce bandwidth exhaustion attack-mitigation in allowed-fast pieces + * python binding fix issue where torrent_info objects where destructing when + their torrents were deleted + * added missing field to scrape_failed_alert in python bindings + * GCC 4.8 fix + * fix proxy failure semantics with regards to anonymous mode + * fix round-robin seed-unchoke algorithm + * add bootstrap.sh to generage configure script and run configure + * fix bug in SOCK5 UDP support + * fix issue where torrents added by URL would not be started immediately + +0.16.10 release + + * fix encryption level handle invalid values + * add a number of missing functions to the python binding + * fix typo in Jamfile for building shared libraries + * prevent tracker exchange for magnet links before metadata is received + * fix crash in make_magnet_uri when generating links longer than 1024 + characters + * fix hanging issue when closing files on windows (completing a download) + * fix piece picking edge case that could cause torrents to get stuck at + hash failure + * try unencrypted connections first, and fall back to encryption if it + fails (performance improvement) + * add missing functions to python binding (flush_cache(), remap_files() + and orig_files()) + * improve handling of filenames that are invalid on windows + * support 'implied_port' in DHT announce_peer + * don't use pool allocator for disk blocks (cache may now return pages + to the kernel) + +0.16.9 release + + * fix long filename truncation on windows + * distinguish file open mode when checking files and downloading/seeding + with bittorrent. updates storage interface + * improve file_storage::map_file when dealing with invalid input + * improve handling of invalid utf-8 sequences in strings in torrent files + * handle more cases of broken .torrent files + * fix bug filename collision resolver + * fix bug in filename utf-8 verification + * make need_save_resume() a bit more robust + * fixed sparse flag manipulation on windows + * fixed streaming piece picking issue + +0.16.8 release + + * make rename_file create missing directories for new filename + * added missing python function: parse_magnet_uri + * fix alerts.all_categories in python binding + * fix torrent-abort issue which would cancel name lookups of other torrents + * make torrent file parser reject invalid path elements earlier + * fixed piece picker bug when using pad-files + * fix read-piece response for cancelled deadline-pieces + * fixed file priority vector-overrun + * fix potential packet allocation alignment issue in utp + * make 'close_redudnant_connections' cover more cases + * set_piece_deadline() also unfilters the piece (if its priority is 0) + * add work-around for bug in windows vista and earlier in + GetOverlappedResult + * fix traversal algorithm leak in DHT + * fix string encoding conversions on windows + * take torrent_handle::query_pieces into account in torrent_handle::statue() + * honor trackers responding with 410 + * fixed merkle tree torrent creation bug + * fixed crash with empty url-lists in torrent files + * added missing max_connections() function to python bindings + +0.16.7 release + + * fix string encoding in error messages + * handle error in read_piece and set_piece_deadline when torrent is removed + * DHT performance improvement + * attempt to handle ERROR_CANT_WAIT disk error on windows + * improve peers exchanged over PEX + * fixed rare crash in ut_metadata extension + * fixed files checking issue + * added missing pop_alerts() to python bindings + * fixed typos in configure script, inversing some feature-enable/disable flags + * added missing flag_update_subscribe to python bindings + * active_dht_limit, active_tracker_limit and active_lsd_limit now + interpret -1 as infinite + +0.16.6 release + + * fixed verbose log error for NAT holepunching + * fix a bunch of typos in python bindings + * make get_settings available in the python binding regardless of + deprecated functions + * fix typo in python settings binding + * fix possible dangling pointer use in peer list + * fix support for storing arbitrary data in the DHT + * fixed bug in uTP packet circle buffer + * fix potential crash when using torrent_handle::add_piece + * added missing add_torrent_alert to python binding + +0.16.5 release + + * udp socket refcounter fix + * added missing async_add_torrent to python bindings + * raised the limit for bottled http downloads to 2 MiB + * add support for magnet links and URLs in python example client + * fixed typo in python bindings' add_torrent_params + * introduce a way to add built-in plugins from python + * consistently disconnect the same peer when two peers simultaneously connect + * fix local endpoint queries for uTP connections + * small optimization to local peer discovery to ignore our own broadcasts + * try harder to bind the udp socket (uTP, DHT, UDP-trackers, LSD) to the + same port as TCP + * relax file timestamp requirements for accepting resume data + * fix performance issue in web seed downloader (coalescing of blocks + sometimes wouldn't work) + * web seed fixes (better support for torrents without trailing / in + web seeds) + * fix some issues with SSL over uTP connections + * fix UDP trackers trying all endpoints behind the hostname + +0.16.4 release + + * raise the default number of torrents allowed to announce to trackers + to 1600 + * improve uTP slow start behavior + * fixed UDP socket error causing it to fail on Win7 + * update use of boost.system to not use deprecated functions + * fix GIL issue in python bindings. Deprecated extension support in python + * fixed bug where setting upload slots to -1 would not mean infinite + * extend the UDP tracker protocol to include the request string from the + tracker URL + * fix mingw build for linux crosscompiler + +0.16.3 release + + * fix python binding backwards compatibility in replace_trackers + * fix possible starvation in metadata extension + * fix crash when creating torrents and optimizing file order with pad files + * disable support for large MTUs in uTP until it is more reliable + * expose post_torrent_updates and state_update_alert to python bindings + * fix incorrect SSL error messages + * fix windows build of shared library with openssl + * fix race condition causing shutdown hang + +0.16.2 release + + * fix permissions issue on linux with noatime enabled for non-owned files + * use random peer IDs in anonymous mode + * fix move_storage bugs + * fix unnecessary dependency on boost.date_time when building boost.asio as separate compilation + * always use SO_REUSEADDR and deprecate the flag to turn it on + * add python bindings for SSL support + * minor uTP tweaks + * fix end-game mode issue when some files are selected to not be downloaded + * improve uTP slow start + * make uTP less aggressive resetting cwnd when idle + +0.16.1 release + + * fixed crash when providing corrupt resume data + * fixed support for boost-1.44 + * fixed reversed semantics of queue_up() and queue_down() + * added missing functions to python bindings (file_priority(), set_dht_settings()) + * fixed low_prio_disk support on linux + * fixed time critical piece accounting in the request queue + * fixed semantics of rate_limit_utp to also ignore per-torrent limits + * fixed piece sorting bug of deadline pieces + * fixed python binding build on Mac OS and BSD + * fixed UNC path normalization (on windows, unless UNC paths are disabled) + * fixed possible crash when enabling multiple connections per IP + * fixed typo in win vista specific code, breaking the build + * change default of rate_limit_utp to true + * fixed DLL export issue on windows (when building a shared library linking statically against boost) + * fixed FreeBSD build + * fixed web seed performance issue with pieces > 1 MiB + * fixed unchoke logic when using web seeds + * fixed compatibility with older versions of boost (down to boost 1.40) + +0.16 release + + * support torrents with more than 262000 pieces + * make tracker back-off configurable + * don't restart the swarm after downloading metadata from magnet links + * lower the default tracker retry intervals + * support banning web seeds sending corrupt data + * don't let hung outgoing connection attempts block incoming connections + * improve SSL torrent support by using SNI and a single SSL listen socket + * improved peer exchange performance by sharing incoming connections which advertize listen port + * deprecate set_ratio(), and per-peer rate limits + * add web seed support for torrents with pad files + * introduced a more scalable API for torrent status updates (post_torrent_updates()) and updated client_test to use it + * updated the API to add_torrent_params turning all bools into flags of a flags field + * added async_add_torrent() function to significantly improve performance when + adding many torrents + * change peer_states to be a bitmask (bw_limit, bw_network, bw_disk) + * changed semantics of send_buffer_watermark_factor to be specified as a percentage + * add incoming_connection_alert for logging all successful incoming connections + * feature to encrypt peer connections with a secret AES-256 key stored in .torrent file + * deprecated compact storage allocation + * close files in separate thread on systems where close() may block (Mac OS X for instance) + * don't create all directories up front when adding torrents + * support DHT scrape + * added support for fadvise/F_RDADVISE for improved disk read performance + * introduced pop_alerts() which pops the entire alert queue in a single call + * support saving metadata in resume file, enable it by default for magnet links + * support for receiving multi announce messages for local peer discovery + * added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 + * added option to not recheck on missing or incomplete resume data + * extended stats logging with statistics=on builds + * added new session functions to more efficiently query torrent status + * added alerts for added and removed torrents + * expanded plugin interface to support session wide states + * made the metadata block requesting algorithm more robust against hash check failures + * support a separate option to use proxies for peers or not + * pausing the session now also pauses checking torrents + * moved alert queue size limit into session_settings + * added support for DHT rss feeds (storing only) + * added support for RSS feeds + * fixed up some edge cases in DHT routing table and improved unit test of it + * added error category and error codes for HTTP errors + * made the DHT implementation slightly more robust against routing table poisoning and node ID spoofing + * support chunked encoding in http downloads (http_connection) + * support adding torrents by url to the .torrent file + * support CDATA tags in xml parser + * use a python python dictionary for settings instead of session_settings object (in python bindings) + * optimized metadata transfer (magnet link) startup time (shaved off about 1 second) + * optimized swarm startup time (shaved off about 1 second) + * support DHT name lookup + * optimized memory usage of torrent_info and file_storage, forcing some API changes + around file_storage and file_entry + * support trackerid tracker extension + * graceful peer disconnect mode which finishes transactions before disconnecting peers + * support chunked encoding for web seeds + * uTP protocol support + * resistance towards certain flood attacks + * support chunked encoding for web seeds (only for BEP 19, web seeds) + * optimized session startup time + * support SSL for web seeds, through all proxies + * support extending web seeds with custom authorization and extra headers + * settings that are not changed from the default values are not saved + in the session state + * made seeding choking algorithm configurable + * deprecated setters for max connections, max half-open, upload and download + rates and unchoke slots. These are now set through session_settings + * added functions to query an individual peer's upload and download limit + * full support for BEP 21 (event=paused) + * added share-mode feature for improving share ratios + * merged all proxy settings into a single one + * improved SOCKS5 support by proxying hostname lookups + * improved support for multi-homed clients + * added feature to not count downloaded bytes from web seeds in stats + * added alert for incoming local service discovery messages + * added option to set file priorities when adding torrents + * removed the session mutex for improved performance + * added upload and download activity timer stats for torrents + * made the reuse-address flag configurable on the listen socket + * moved UDP trackers over to use a single socket + * added feature to make asserts log to a file instead of breaking the process + (production asserts) + * optimized disk I/O cache clearing + * added feature to ask a torrent if it needs to save its resume data or not + * added setting to ignore file modification time when loading resume files + * support more fine-grained torrent states between which peer sources it + announces to + * supports calculating sha1 file-hashes when creating torrents + * made the send_buffer_watermark performance warning more meaningful + * supports complete_ago extension + * dropped zlib as a dependency and builds using puff.c instead + * made the default cache size depend on available physical RAM + * added flags to torrent::status() that can filter which values are calculated + * support 'explicit read cache' which keeps a specific set of pieces + in the read cache, without implicitly caching other pieces + * support sending suggest messages based on what's in the read cache + * clear sparse flag on files that complete on windows + * support retry-after header for web seeds + * replaced boost.filesystem with custom functions + * replaced dependency on boost.thread by asio's internal thread primitives + * added support for i2p torrents + * cleaned up usage of MAX_PATH and related macros + * made it possible to build libtorrent without RTTI support + * added support to build with libgcrypt and a shipped version of libtommath + * optimized DHT routing table memory usage + * optimized disk cache to work with large caches + * support variable number of optimistic unchoke slots and to dynamically + adjust based on the total number of unchoke slots + * support for BitTyrant choker algorithm + * support for automatically start torrents when they receive an + incoming connection + * added more detailed instrumentation of the disk I/O thread + +0.15.11 release + + * fixed web seed bug, sometimes causing infinite loops + * fixed race condition when setting session_settings immediately after creating session + * give up immediately when failing to open a listen socket (report the actual error) + * restored ABI compatibility with 0.15.9 + * added missing python bindings for create_torrent and torrent_info + +0.15.10 release + + * fix 'parameter incorrect' issue when using unbuffered IO on windows + * fixed UDP socket error handling on windows + * fixed peer_tos (type of service) setting + * fixed crash when loading resume file with more files than the torrent in it + * fix invalid-parameter error on windows when disabling filesystem disk cache + * fix connection queue issue causing shutdown delays + * fixed mingw build + * fix overflow bug in progress_ppm field + * don't filter local peers received from a non-local tracker + * fix python deadlock when using python extensions + * fixed small memory leak in DHT + +0.15.9 release + + * added some functions missing from the python binding + * fixed rare piece picker bug + * fixed invalid torrent_status::finished_time + * fixed bugs in dont-have and upload-only extension messages + * don't open files in random-access mode (speeds up hashing) + +0.15.8 release + + * allow NULL to be passed to create_torrent::set_comment and create_torrent::set_creator + * fix UPnP issue for routers with multiple PPPoE connections + * fix issue where event=stopped announces wouldn't be sent when closing session + * fix possible hang in file::readv() on windows + * fix CPU busy loop issue in tracker announce logic + * honor IOV_MAX when using writev and readv + * don't post 'operation aborted' UDP errors when changing listen port + * fix tracker retry logic, where in some configurations the next tier would not be tried + * fixed bug in http seeding logic (introduced in 0.15.7) + * add support for dont-have extension message + * fix for set_piece_deadline + * add reset_piece_deadline function + * fix merkle tree torrent assert + +0.15.7 release + + * exposed set_peer_id to python binding + * improve support for merkle tree torrent creation + * exposed comparison operators on torrent_handle to python + * exposed alert error_codes to python + * fixed bug in announce_entry::next_announce_in and min_announce_in + * fixed sign issue in set_alert_mask signature + * fixed unaligned disk access for unbuffered I/O in windows + * support torrents whose name is empty + * fixed connection limit to take web seeds into account as well + * fixed bug when receiving a have message before having the metadata + * fixed python bindings build with disabled DHT support + * fixed BSD file allocation issue + * fixed bug in session::delete_files option to remove_torrent + +0.15.6 release + + * fixed crash in udp trackers when using SOCKS5 proxy + * fixed reconnect delay when leaving upload only mode + * fixed default values being set incorrectly in add_torrent_params through add_magnet_uri in python bindings + * implemented unaligned write (for unbuffered I/O) + * fixed broadcast_lsd option + * fixed udp-socket race condition when using a proxy + * end-game mode optimizations + * fixed bug in udp_socket causing it to issue two simultaneous async. read operations + * fixed mingw build + * fixed minor bug in metadata block requester (for magnet links) + * fixed race condition in iconv string converter + * fixed error handling in torrent_info constructor + * fixed bug in torrent_info::remap_files + * fix python binding for wait_for_alert + * only apply privileged port filter to DHT-only peers + +0.15.5 release + + * support DHT extension to report external IPs + * fixed rare crash in http_connection's error handling + * avoid connecting to peers listening on ports < 1024 + * optimized piece picking to not cause busy loops in some end-game modes + * fixed python bindings for tcp::endpoint + * fixed edge case of pad file support + * limit number of torrents tracked by DHT + * fixed bug when allow_multiple_connections_per_ip was enabled + * potential WOW64 fix for unbuffered I/O (windows) + * expose set_alert_queue_size_limit to python binding + * support dht nodes in magnet links + * support 100 Continue HTTP responses + * changed default choker behavior to use 8 unchoke slots (instead of being rate based) + * fixed error reporting issue in disk I/O thread + * fixed file allocation issues on linux + * fixed filename encoding and decoding issue on platforms using iconv + * reports redundant downloads to tracker, fixed downloaded calculation to + be more stable when not including redundant. Improved redundant data accounting + to be more accurate + * fixed bugs in http seed connection and added unit test for it + * fixed error reporting when fallocate fails + * deprecate support for separate proxies for separate kinds of connections + +0.15.4 release + + * fixed piece picker issue triggered by hash failure and timed out requests to the piece + * fixed optimistic unchoke issue when setting per torrent unchoke limits + * fixed UPnP shutdown issue + * fixed UPnP DeletePortmapping issue + * fixed NAT-PMP issue when adding the same mapping multiple times + * no peers from tracker when stopping is no longer an error + * improved web seed retry behavior + * fixed announce issue + +0.15.3 release + + * fixed announce bug where event=completed would not be sent if it violated the + min-announce of the tracker + * fixed limitation in rate limiter + * fixed build error with boost 1.44 + +0.15.2 release + + * updated compiler to msvc 2008 for python binding + * restored default fail_limit to unlimited on all trackers + * fixed rate limit bug for DHT + * fixed SOCKS5 bug for routing UDP packets + * fixed bug on windows when verifying resume data for a torrent where + one of its directories had been removed + * fixed race condition in peer-list with DHT + * fix force-reannounce and tracker retry issue + +0.15.1 release + + * fixed rare crash when purging the peer list + * fixed race condition around m_abort in session_impl + * fixed bug in web_peer_connection which could cause a hang when downloading + from web servers + * fixed bug in metadata extensions combined with encryption + * refactored socket reading code to not use async. operations unnecessarily + * some timer optimizations + * removed the reuse-address flag on the listen socket + * fixed bug where local peer discovery and DHT wouldn't be announced to without trackers + * fixed bug in bdecoder when decoding invalid messages + * added build warning when building with UNICODE but the standard library + doesn't provide std::wstring + * fixed add_node python binding + * fixed issue where trackers wouldn't tried immediately when the previous one failed + * fixed synchronization issue between download queue and piece picker + * fixed bug in udp tracker scrape response parsing + * fixed bug in the disk thread that could get triggered under heavy load + * fixed bug in add_piece() that would trigger asserts + * fixed vs 2010 build + * recognizes more clients in identify_client() + * fixed bug where trackers wouldn't be retried if they failed + * slight performance fix in disk elevator algorithm + * fixed potential issue where a piece could be checked twice + * fixed build issue on windows related to GetCompressedSize() + * fixed deadlock when starting torrents with certain invalid tracker URLs + * fixed iterator bug in disk I/O thread + * fixed FIEMAP support on linux + * fixed strict aliasing warning on gcc + * fixed inconsistency when creating torrents with symlinks + * properly detect windows version to initialize half-open connection limit + * fixed bug in url encoder where $ would not be encoded + +0.15 release + + * introduced a session state save mechanism. load_state() and save_state(). + this saves all session settings and state (except torrents) + * deprecated dht_state functions and merged it with the session state + * added support for multiple trackers in magnet links + * added support for explicitly flushing the disk cache + * added torrent priority to affect bandwidth allocation for its peers + * reduced the number of floating point operations (to better support + systems without FPU) + * added new alert when individual files complete + * added support for storing symbolic links in .torrent files + * added support for uTorrent interpretation of multi-tracker torrents + * handle torrents with duplicate filenames + * piece timeouts are adjusted to download rate limits + * encodes urls in torrent files that needs to be encoded + * fixed not passing &supportcrypto=1 when encryption is disabled + * introduced an upload mode, which torrents are switched into when + it hits a disk write error, instead of stopping the torrent. + this lets libtorrent keep uploading the parts it has when it + encounters a disk-full error for instance + * improved disk error handling and expanded use of error_code in + error reporting. added a bandwidth state, bw_disk, when waiting + for the disk io thread to catch up writing buffers + * improved read cache memory efficiency + * added another cache flush algorithm to write the largest + contiguous blocks instead of the least recently used + * introduced a mechanism to be lighter on the disk when checking torrents + * applied temporary memory storage optimization to when checking + a torrent as well + * removed hash_for_slot() from storage_interface. It is now implemented + by using the readv() function from the storage implementation + * improved IPv6 support by announcing twice when necessary + * added feature to set a separate global rate limit for local peers + * added preset settings for low memory environments and seed machines + min_memory_usage() and high_performance_seeder() + * optimized overall memory usage for DHT nodes and requests, peer + entries and disk buffers + * change in API for block_info in partial_piece_info, instead of + accessing 'peer', call 'peer()' + * added support for fully automatic unchoker (no need to specify + number of upload slots). This is on by default + * added support for changing socket buffer sizes through + session_settings + * added support for merkle hash tree torrents (.merkle.torrent) + * added 'seed mode', which assumes that all files are complete + and checks hashes lazily, as blocks are requested + * added new extension for file attributes (executable and hidden) + * added support for unbuffered I/O for aligned files + * added workaround for sparse file issue on Windows Vista + * added new lt_trackers extension to exchange trackers between + peers + * added support for BEP 17 http seeds + * added read_piece() to read pieces from torrent storage + * added option for udp tracker preference + * added super seeding + * added add_piece() function to inject data from external sources + * add_tracker() function added to torrent_handle + * if there is no working tracker, current_tracker is the + tracker that is currently being tried + * torrents that are checking can now be paused, which will + pause the checking + * introduced another torrent state, checking_resume_data, which + the torrent is in when it's first added, and is comparing + the files on disk with the resume data + * DHT bandwidth usage optimizations + * rate limited DHT send socket + * tracker connections are now also subject to IP filtering + * improved optimistic unchoke logic + * added monitoring of the DHT lookups + * added bandwidth reports for estimated TCP/IP overhead and DHT + * includes DHT traffic in the rate limiter + * added support for bitcomet padding files + * improved support for sparse files on windows + * added ability to give seeding torrents preference to active slots + * added torrent_status::finished_time + * automatically caps files and connections by default to rlimit + * added session::is_dht_running() function + * added torrent_handle::force_dht_announce() + * added torrent_info::remap_files() + * support min_interval tracker extension + * added session saving and loading functions + * added support for min-interval in tracker responses + * only keeps one outstanding duplicate request per peer + reduces waste download, specifically when streaming + * added support for storing per-peer rate limits across reconnects + * improved fallocate support + * fixed magnet link issue when using resume data + * support disk I/O priority settings + * added info_hash to torrent_deleted_alert + * improved LSD performance and made the interval configurable + * improved UDP tracker support by caching connect tokens + * fast piece optimization + +release 0.14.10 + + * fixed udp tracker race condition + * added support for torrents with odd piece sizes + * fixed issue with disk read cache not being cleared when removing torrents + * made the DHT socket bind to the same interface as the session + * fixed issue where an http proxy would not be used on redirects + * Solaris build fixes + * disabled buggy disconnect_peers feature + +release 0.14.9 + + * disabled feature to drop requests after having been skipped too many times + * fixed range request bug for files larger than 2 GB in web seeds + * don't crash when trying to create torrents with 0 files + * fixed big_number __init__ in python bindings + * fixed optimistic unchoke timer + * fixed bug where torrents with incorrectly formatted web seed URLs would be + connected multiple times + * fixed MinGW support + * fixed DHT bootstrapping issue + * fixed UDP over SOCKS5 issue + * added support for "corrupt" tracker announce + * made end-game mode less aggressive + +release 0.14.8 + + * ignore unkown metadata messages + * fixed typo that would sometimes prevent queued torrents to be checked + * fixed bug in auto-manager where active_downloads and active_seeds would + sometimes be used incorrectly + * force_recheck() no longer crashes on torrents with no metadata + * fixed broadcast socket regression from 0.14.7 + * fixed hang in NATPMP when shut down while waiting for a response + * fixed some more error handling in bdecode + +release 0.14.7 + + * fixed deadlock in natpmp + * resume data alerts are always posted, regardless of alert mask + * added wait_for_alert to python binding + * improved invalid filename character replacement + * improved forward compatibility in DHT + * added set_piece_hashes that takes a callback to the python binding + * fixed division by zero in get_peer_info() + * fixed bug where pieces may have been requested before the metadata + was received + * fixed incorrect error when deleting files from a torrent where + not all files have been created + * announces torrents immediately to the DHT when it's started + * fixed bug in add_files that would fail to recurse if the path + ended with a / + * fixed bug in error handling when parsing torrent files + * fixed file checking bug when renaming a file before checking the torrent + * fixed race conditon when receiving metadata from swarm + * fixed assert in ut_metadata plugin + * back-ported some fixes for building with no exceptions + * fixed create_torrent when passing in a path ending with / + * fixed move_storage when source doesn't exist + * fixed DHT state save bug for node-id + * fixed typo in python binding session_status struct + * broadcast sockets now join every network interface (used for UPnP and + local peer discovery) + +release 0.14.6 + + * various missing include fixes to be buildable with boost 1.40 + * added missing functions to python binding related to torrent creation + * fixed to add filename on web seed urls that lack it + * fixed BOOST_ASIO_HASH_MAP_BUCKETS define for boost 1.39 + * fixed checking of fast and suggest messages when used with magnet links + * fixed bug where web seeds would not disconnect if being resolved when + the torrent was paused + * fixed download piece performance bug in piece picker + * fixed bug in connect candidate counter + * replaces invalid filename characters with . + * added --with-libgeoip option to configure script to allow building and + linking against system wide library + * fixed potential pure virtual function call in extensions on shutdown + * fixed disk buffer leak in smart_ban extension + +release 0.14.5 + + * fixed bug when handling malformed webseed urls and an http proxy + * fixed bug when setting unlimited upload or download rates for torrents + * fix to make torrent_status::list_peers more accurate. + * fixed memory leak in disk io thread when not using the cache + * fixed bug in connect candidate counter + * allow 0 upload slots + * fixed bug in rename_file(). The new name would not always be saved in + the resume data + * fixed resume data compatibility with 0.13 + * fixed rare piece-picker bug + * fixed bug where one allowed-fast message would be sent even when + disabled + * fixed race condition in UPnP which could lead to crash + * fixed inversed seed_time ratio logic + * added get_ip_filter() to session + +release 0.14.4 + + * connect candidate calculation fix + * tightened up disk cache memory usage + * fixed magnet link parser to accept hex-encoded info-hashes + * fixed inverted logic when picking which peers to connect to + (should mean a slight performance improvement) + * fixed a bug where a failed rename_file() would leave the storage + in an error state which would pause the torrent + * fixed case when move_storage() would fail. Added a new alert + to be posted when it does + * fixed crash bug when shutting down while checking a torrent + * fixed handling of web seed urls that didn't end with a + slash for multi-file torrents + * lowered the default connection speed to 10 connection attempts + per second + * optimized memory usage when checking files fails + * fixed bug when checking a torrent twice + * improved handling of out-of-memory conditions in disk I/O thread + * fixed bug when force-checking a torrent with partial pieces + * fixed memory leak in disk cache + * fixed torrent file path vulnerability + * fixed upnp + * fixed bug when dealing with clients that drop requests (i.e. BitComet) + fixes assert as well + +release 0.14.3 + + * added python binding for create_torrent + * fixed boost-1.38 build + * fixed bug where web seeds would be connected before the files + were checked + * fixed filename bug when using wide characters + * fixed rare crash in peer banning code + * fixed potential HTTP compatibility issue + * fixed UPnP crash + * fixed UPnP issue where the control url contained the base url + * fixed a replace_trackers bug + * fixed bug where the DHT port mapping would not be removed when + changing DHT port + * fixed move_storage bug when files were renamed to be moved out + of the root directory + * added error handling for set_piece_hashes + * fixed missing include in enum_if.cpp + * fixed dual IP stack issue + * fixed issue where renamed files were sometimes not saved in resume data + * accepts tracker responses with no 'peers' field, as long as 'peers6' + is present + * fixed CIDR-distance calculation in the precense of IPv6 peers + * save partial resume data for torrents that are queued for checking + or checking, to maintain stats and renamed files + * Don't try IPv6 on windows if it's not installed + * move_storage fix + * fixed potential crash on shutdown + * fixed leaking exception from bdecode on malformed input + * fixed bug where connection would hang when receiving a keepalive + * fixed bug where an asio exception could be thrown when resolving + peer countries + * fixed crash when shutting down while checking a torrent + * fixed potential crash in connection_queue when a peer_connection + fail to open its socket + +release 0.14.2 + + * added missing functions to the python bindings torrent_info::map_file, + torrent_info::map_block and torrent_info::file_at_offset. + * removed support for boost-1.33 and earlier (probably didn't work) + * fixed potential freezes issues at shutdown + * improved error message for python setup script + * fixed bug when torrent file included announce-list, but no valid + tracker urls + * fixed bug where the files requested from web seeds would be the + renamed file names instead of the original file names in the torrent. + * documentation fix of queing section + * fixed potential issue in udp_socket (affected udp tracker support) + * made name, comment and created by also be subject to utf-8 error + correction (filenames already were) + * fixed dead-lock when settings DHT proxy + * added missing export directives to lazy_entry + * fixed disk cache expiry settings bug (if changed, it would be set + to the cache size) + * fixed bug in http_connection when binding to a particular IP + * fixed typo in python binding (torrent_handle::piece_prioritize should + be torrent_handle::piece_priorities) + * fixed race condition when saving DHT state + * fixed bugs related to lexical_cast being locale dependent + * added support for SunPro C++ compiler + * fixed bug where messeges sometimes could be encrypted in the + wrong order, for encrypted connections. + * fixed race condition where torrents could get stuck waiting to + get checked + * fixed mapped files bug where it wouldn't be properly restored + from resume data properly + * removed locale dependency in xml parser (caused asserts on windows) + * fixed bug when talking to https 1.0 servers + * fixed UPnP bug that could cause stack overflow + +release 0.14.1 + + * added converter for python unicode strings to utf-8 paths + * fixed bug in http downloader where the host field did not + include the port number + * fixed headers to not depend on NDEBUG, which would prohibit + linking a release build of libtorrent against a debug application + * fixed bug in disk I/O thread that would make the thread + sometimes quit when an error occurred + * fixed DHT bug + * fixed potential shutdown crash in disk_io_thread + * fixed usage of deprecated boost.filsystem functions + * fixed http_connection unit test + * fixed bug in DHT when a DHT state was loaded + * made rate limiter change in 0.14 optional (to take estimated + TCP/IP overhead into account) + * made the python plugin buildable through the makefile + * fixed UPnP bug when url base ended with a slash and + path started with a slash + * fixed various potentially leaking exceptions + * fixed problem with removing torrents that are checking + * fixed documentation bug regarding save_resume_data() + * added missing documentation on torrent creation + * fixed bugs in python client examples + * fixed missing dependency in package-config file + * fixed shared geoip linking in Jamfile + * fixed python bindings build on windows and made it possible + to generate a windows installer + * fixed bug in NAT-PMP implementation + +release 0.14 + + * deprecated add_torrent() in favor of a new add_torrent() + that takes a struct with parameters instead. Torrents + are paused and auto managed by default. + * removed 'connecting_to_tracker' torrent state. This changes + the enum values for the other states. + * Improved seeding and choking behavior. + * Fixed rare buffer overrun bug when calling get_download_queue + * Fixed rare bug where torrent could be put back into downloading + state even though it was finished, after checking files. + * Fixed rename_file to work before the file on disk has been + created. + * Fixed bug in tracker connections in case of errors caused + in the connection constructor. + * Updated alert system to be filtered by category instead of + severity level. Alerts can generate a message through + alert::message(). + * Session constructor will now start dht, upnp, natpmp, lsd by + default. Flags can be passed in to the constructor to not + do this, if these features are to be enabled and disabled + at a later point. + * Removed 'connecting_to_tracker' torrent state + * Fix bug where FAST pieces were cancelled on choke + * Fixed problems with restoring piece states when hash failed. + * Minimum peer reconnect time fix. Peers with no failures would + reconnect immediately. + * Improved web seed error handling + * DHT announce fixes and off-by-one loop fix + * Fixed UPnP xml parse bug where it would ignore the port number + for the control url. + * Fixed bug in torrent writer where the private flag was added + outside of the info dictionary + * Made the torrent file parser less strict of what goes in the + announce-list entry + * Fixed type overflow bug where some statistics was incorrectly + reported for file larger than 2 GB + * boost-1.35 support + * Fixed bug in statistics from web server peers where it sometimes + could report too many bytes downloaded. + * Fixed bug where statistics from the last second was lost when + disconnecting a peer. + * receive buffer optimizations (memcpy savings and memory savings) + * Support for specifying the TOS byte for peer traffic. + * Basic support for queueing of torrents. + * Better bias to give connections to downloading torrents + with fewer peers. + * Optimized resource usage (removed the checking thread) + * Support to bind outgoing connections to specific ports + * Disk cache support. + * New, more memory efficient, piece picker with sequential download + support (instead of the more complicated sequential download threshold). + * Auto Upload slots. Automtically opens up more slots if + upload limit is not met. + * Improved NAT-PMP support by querying the default gateway + * Improved UPnP support by ignoring routers not on the clients subnet. + +release 0.13 + + * Added scrape support + * Added add_extension() to torrent_handle. Can instantiate + extensions for torrents while downloading + * Added support for remove_torrent to delete the files as well + * Fixed issue with failing async_accept on windows + * DHT improvements, proper error messages are now returned when + nodes sends bad packets + * Optimized the country table used to resolve country of peers + * Copying optimization for sending data. Data is no longer copied from + the disk I/O buffer to the send buffer. + * Buffer optimization to use a raw buffer instead of std::vector + * Improved file storage to use sparse files + * Updated python bindings + * Added more clients to the identifiable clients list. + * Torrents can now be started in paused state (to better support queuing) + * Improved IPv6 support (support for IPv6 extension to trackers and + listens on both IPv6 and IPv4 interfaces). + * Improved asserts used. Generates a stacktrace on linux + * Piece picker optimizations and improvements + * Improved unchoker, connection limit and rate limiter + * Support for FAST extension + * Fixed invalid calculation in DHT node distance + * Fixed bug in URL parser that failed to parse IPv6 addresses + * added peer download rate approximation + * added port filter for outgoing connection (to prevent + triggering firewalls) + * made most parameters configurable via session_settings + * added encryption support + * added parole mode for peers whose data fails the hash check. + * optimized heap usage in piece-picker and web seed downloader. + * fixed bug in DHT where older write tokens weren't accepted. + * added support for sparse files. + * introduced speed categories for peers and pieces, to separate + slow and fast peers. + * added a half-open tcp connection limit that takes all connections + in to account, not just peer connections. + * added alerts for filtered IPs. + * added support for SOCKS4 and 5 proxies and HTTP CONNECT proxies. + * fixed proper distributed copies calculation. + * added option to use openssl for sha-1 calculations. + * optimized the piece picker in the case where a peer is a seed. + * added support for local peer discovery + * removed the dependency on the compiled boost.date_time library + * deprecated torrent_info::print() + * added UPnP support + * fixed problem where peer interested flags were not updated correctly + when pieces were filtered + * improvements to ut_pex messages, including support for seed flag + * prioritizes upload bandwidth to peers that might send back data + * the following functions have been deprecated: + void torrent_handle::filter_piece(int index, bool filter) const; + void torrent_handle::filter_pieces(std::vector const& pieces) const; + bool torrent_handle::is_piece_filtered(int index) const; + std::vector torrent_handle::filtered_pieces() const; + void torrent_handle::filter_files(std::vector const& files) const; + + instead, use the piece_priority functions. + + * added support for NAT-PMP + * added support for piece priorities. Piece filtering is now set as + a priority + * Fixed crash when last piece was smaller than one block and reading + fastresume data for that piece + * Makefiles should do a better job detecting boost + * Fixed crash when all tracker urls are removed + * Log files can now be created at user supplied path + * Log files failing to create is no longer fatal + * Fixed dead-lock in torrent_handle + * Made it build with boost 1.34 on windows + * Fixed bug in URL parser that failed to parse IPv6 addresses + * Fixed bug in DHT, related to IPv6 nodes + * DHT accepts transaction IDs that have garbage appended to them + * DHT logs messages that it fails to decode + +release 0.12 + + * fixes to make the DHT more compatible + * http seed improvements including error reporting and url encoding issues. + * fixed bug where directories would be left behind when moving storage + in some cases. + * fixed crashing bug when restarting or stopping the DHT. + * added python binding, using boost.python + * improved character conversion on windows when strings are not utf-8. + * metadata extension now respects the private flag in the torrent. + * made the DHT to only be used as a fallback to trackers by default. + * added support for HTTP redirection support for web seeds. + * fixed race condition when accessing a torrent that was checking its + fast resume data. + * fixed a bug in the DHT which could be triggered if the network was + dropped or extremely rare cases. + * if the download rate is limited, web seeds will now only use left-over + bandwidth after all bt peers have used up as much bandwidth as they can. + * added the possibility to have libtorrent resolve the countries of + the peers in torrents. + * improved the bandwidth limiter (it now implements a leaky bucket/node bucket). + * improved the HTTP seed downloader to report accurate progress. + * added more client peer-id signatures to be recognized. + * added support for HTTP servers that skip the CR before the NL at line breaks. + * fixed bug in the HTTP code that only accepted headers case sensitive. + * fixed bug where one of the session constructors didn't initialize boost.filesystem. + * fixed bug when the initial checking of a torrent fails with an exception. + * fixed bug in DHT code which would send incorrect announce messages. + * fixed bug where the http header parser was case sensitive to the header + names. + * Implemented an optmization which frees the piece_picker once a torrent + turns into a seed. + * Added support for uT peer exchange extension, implemented by Massaroddel. + * Modified the quota management to offer better bandwidth balancing + between peers. + * logging now supports multiple sessions (different sessions now log + to different directories). + * fixed random number generator seed problem, generating the same + peer-id for sessions constructed the same second. + * added an option to accept multiple connections from the same IP. + * improved tracker logging. + * moved the file_pool into session. The number of open files is now + limited per session. + * fixed uninitialized private flag in torrent_info + * fixed long standing issue with file.cpp on windows. Replaced the low level + io functions used on windows. + * made it possible to associate a name with torrents without metadata. + * improved http-downloading performance by requesting entire pieces via + http. + * added plugin interface for extensions. And changed the interface for + enabling extensions. + +release 0.11 + + * added support for incorrectly encoded paths in torrent files + (assumes Latin-1 encoding and converts to UTF-8). + * added support for destructing session objects asynchronously. + * fixed bug with file_progress() with files = 0 bytes + * fixed a race condition bug in udp_tracker_connection that could + cause a crash. + * fixed bug occuring when increasing the sequenced download threshold + with max availability lower than previous threshold. + * fixed an integer overflow bug occuring when built with gcc 4.1.x + * fixed crasing bug when closing while checking a torrent + * fixed bug causing a crash with a torrent with piece length 0 + * added an extension to the DHT network protocol to support the + exchange of nodes with IPv6 addresses. + * modified the ip_filter api slightly to support IPv6 + * modified the api slightly to make sequenced download threshold + a per torrent-setting. + * changed the address type to support IPv6 + * fixed bug in piece picker which would not behave as + expected with regard to sequenced download threshold. + * fixed bug with file_progress() with files > 2 GB. + * added --enable-examples option to configure script. + * fixed problem with the resource distribution algorithm + (controlling e.g upload/download rates). + * fixed incorrect asserts in storage related to torrents with + zero-sized files. + * added support for trackerless torrents (with kademlia DHT). + * support for torrents with the private flag set. + * support for torrents containing bootstrap nodes for the + DHT network. + * fixed problem with the configure script on FreeBSD. + * limits the pipelining used on url-seeds. + * fixed problem where the shutdown always would delay for + session_settings::stop_tracker_timeout seconds. + * session::listen_on() won't reopen the socket in case the port and + interface is the same as the one currently in use. + * added http proxy support for web seeds. + * fixed problem where upload and download stats could become incorrect + in case of high cpu load. + * added more clients to the identifiable list. + * fixed fingerprint parser to cope with latest Mainline versions. + +release 0.10 + + * fixed a bug where the requested number of peers in a tracker request could + be too big. + * fixed a bug where empty files were not created in full allocation mode. + * fixed a bug in storage that would, in rare cases, fail to do a + complete check. + * exposed more settings for tweaking parameters in the piece-picker, + downloader and uploader (http_settings replaced by session_settings). + * tweaked default settings to improve high bandwidth transfers. + * improved the piece picker performance and made it possible to download + popular pieces in sequence to improve disk performance. + * added the possibility to control upload and download limits per peer. + * fixed problem with re-requesting skipped pieces when peer was sending pieces + out of fifo-order. + * added support for http seeding (the GetRight protocol) + * renamed identifiers called 'id' in the public interface to support linking + with Objective.C++ + * changed the extensions protocol to use the new one, which is also + implemented by uTorrent. + * factorized the peer_connection and added web_peer_connection which is + able to download from http-sources. + * converted the network code to use asio (resulted in slight api changes + dealing with network addresses). + * made libtorrent build in vc7 (patches from Allen Zhao) + * fixed bug caused when binding outgoing connections to a non-local interface. + * add_torrent() will now throw if called while the session object is + being closed. + * added the ability to limit the number of simultaneous half-open + TCP connections. Flags in peer_info has been added. + +release 0.9.1 + + * made the session disable file name checks within the boost.filsystem library + * fixed race condition in the sockets + * strings that are invalid utf-8 strings are now decoded with the + local codepage on windows + * added the ability to build libtorrent both as a shared library + * client_test can now monitor a directory for torrent files and automatically + start and stop downloads while running + * fixed problem with file_size() when building on windows with unicode support + * added a new torrent state, allocating + * added a new alert, metadata_failed_alert + * changed the interface to session::add_torrent for some speed optimizations. + * greatly improved the command line control of the example client_test. + * fixed bug where upload rate limit was not being applied. + * files that are being checked will no longer stall files that don't need + checking. + * changed the way libtorrent identifies support for its excentions + to look for 'ext' at the end of the peer-id. + * improved performance by adding a circle buffer for the send buffer. + * fixed bugs in the http tracker connection when using an http proxy. + * fixed problem with storage's file pool when creating torrents and then + starting to seed them. + * hard limit on remote request queue and timeout on requests (a timeout + triggers rerequests). This makes libtorrent work much better with + "broken" clients like BitComet which may ignore requests. + +Initial release 0.9 + + * multitracker support + * serves multiple torrents on a single port and a single thread + * supports http proxies and proxy authentication + * gzipped tracker-responses + * block level piece picker + * queues torrents for file check, instead of checking all of them in parallel + * uses separate threads for checking files and for main downloader + * upload and download rate limits + * piece-wise, unordered, incremental file allocation + * fast resume support + * supports files > 2 gigabytes + * supports the no_peer_id=1 extension + * support for udp-tracker protocol + * number of connections limit + * delays sending have messages + * can resume pieces downloaded in any order + * adjusts the length of the request queue depending on download rate + * supports compact=1 + * selective downloading + * ip filter + diff --git a/apps/Launcher/ext/libtorrent/LICENSE b/apps/Launcher/ext/libtorrent/LICENSE new file mode 100644 index 0000000000..2c361d159f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/LICENSE @@ -0,0 +1,103 @@ +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ + +puff.c +Copyright (C) 2002, 2003 Mark Adler +For conditions of distribution and use, see copyright notice in puff.h +version 1.7, 3 Mar 2003 + +puff.c is a simple inflate written to be an unambiguous way to specify the +deflate format. It is not written for speed but rather simplicity. As a +side benefit, this code might actually be useful when small code is more +important than speed, such as bootstrap applications. For typical deflate +data, zlib's inflate() is about four times as fast as puff(). zlib's +inflate compiles to around 20K on my machine, whereas puff.c compiles to +around 4K on my machine (a PowerPC using GNU cc). If the faster decode() +function here is used, then puff() is only twice as slow as zlib's +inflate(). + +All dynamically allocated memory comes from the stack. The stack required +is less than 2K bytes. This code is compatible with 16-bit int's and +assumes that long's are at least 32 bits. puff.c uses the short data type, +assumed to be 16 bits, for arrays in order to to conserve memory. The code +works whether integers are stored big endian or little endian. + +In the comments below are "Format notes" that describe the inflate process +and document some of the less obvious aspects of the format. This source +code is meant to supplement RFC 1951, which formally describes the deflate +format: + + http://www.zlib.org/rfc-deflate.html + +------------------------------------------------------------------------------ + +GeoIP.c + +Copyright (C) 2006 MaxMind LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +------------------------------------------------------------------------------ + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/apps/Launcher/ext/libtorrent/NEWS b/apps/Launcher/ext/libtorrent/NEWS new file mode 100644 index 0000000000..58378c1553 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/NEWS @@ -0,0 +1 @@ +See ChangeLog diff --git a/apps/Launcher/ext/libtorrent/README b/apps/Launcher/ext/libtorrent/README new file mode 100644 index 0000000000..089d37122b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/README @@ -0,0 +1,16 @@ +libtorrent is an open source C++ library implementing the BitTorrent protocol, +along with most popular extensions, making it suitable for real world +deployment. It is configurable to be able to fit both servers and embedded +devices. + +The main goals of libtorrent are to be efficient and easy to use. + +See docs/index.html for more detailed build and usage instructions. + +To build with boost-build, run: + + b2 + +See docs/building.html for more details on how to build and which configuration +options are available. + diff --git a/apps/Launcher/ext/libtorrent/ed25519/readme.md b/apps/Launcher/ext/libtorrent/ed25519/readme.md new file mode 100644 index 0000000000..b202892773 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/readme.md @@ -0,0 +1,165 @@ +Ed25519 +======= + +This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based +on the SUPERCOP "ref10" implementation. Additionally there is key exchanging +and scalar addition included to further aid building a PKI using Ed25519. All +code is in the public domain. + +All code is pure ANSI C without any dependencies, except for the random seed +generation which uses standard OS cryptography APIs (`CryptGenRandom` on +Windows, `/dev/urandom` on nix). If you wish to be entirely portable define +`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your +application requires key generation you must supply your own seeding function +(which is simply a 256 bit (32 byte) cryptographic random number generator). + + +Performance +----------- + +On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following +speeds (running on only one a single core): + + Seed generation: 64us (15625 per second) + Key generation: 88us (11364 per second) + Message signing (short message): 87us (11494 per second) + Message verifying (short message): 228us (4386 per second) + Scalar addition: 100us (10000 per second) + Key exchange: 220us (4545 per second) + +The speeds on other machines may vary. Sign/verify times will be higher with +longer messages. The implementation significantly benefits from 64 bit +architectures, if possible compile as 64 bit. + + +Usage +----- + +Simply add all .c and .h files in the `src/` folder to your project and include +`ed25519.h` in any file you want to use the API. If you prefer to use a shared +library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A +windows DLL is pre-built. + +There are no defined types for seeds, private keys, public keys, shared secrets +or signatures. Instead simple `unsigned char` buffers are used with the +following sizes: + +```c +unsigned char seed[32]; +unsigned char signature[64]; +unsigned char public_key[32]; +unsigned char private_key[64]; +unsigned char scalar[32]; +unsigned char shared_secret[32]; +``` + +API +--- + +```c +int ed25519_create_seed(unsigned char *seed); +``` + +Creates a 32 byte random seed in `seed` for key generation. `seed` must be a +writable 32 byte buffer. Returns 0 on success, and nonzero on failure. + +```c +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +``` + +Creates a new key pair from the given seed. `public_key` must be a writable 32 +byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be +a 32 byte buffer. + +```c +void ed25519_sign(unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Creates a signature of the given message with the given key pair. `signature` +must be a writable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. + +```c +int ed25519_verify(const unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key); +``` + +Verifies the signature on the given message using `public_key`. `signature` +must be a readable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. Returns 1 if the signature matches, 0 otherwise. + +```c +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, + const unsigned char *scalar); +``` + +Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly +generated with `ed25519_create_seed`), generating a new key pair. You can +calculate the public key sum without knowing the private key and vice versa by +passing in `NULL` for the key you don't know. This is useful for enforcing +randomness on a key pair by a third party while only knowing the public key, +among other things. Warning: the last bit of the scalar is ignored - if +comparing scalars make sure to clear it with `scalar[31] &= 127`. + + +```c +void ed25519_key_exchange(unsigned char *shared_secret, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Performs a key exchange on the given public key and private key, producing a +shared secret. It is recommended to hash the shared secret before using it. +`shared_secret` must be a 32 byte writable buffer where the shared secret will +be stored. + +Example +------- + +```c +unsigned char seed[32], public_key[32], private_key[64], signature[64]; +unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; +const unsigned char message[] = "TEST MESSAGE"; + +/* create a random seed, and a key pair out of that seed */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(public_key, private_key, seed); + +/* create signature on the message with the key pair */ +ed25519_sign(signature, message, strlen(message), public_key, private_key); + +/* verify the signature */ +if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); +} else { + printf("invalid signature\n"); +} + +/* create a dummy keypair to use for a key exchange, normally you'd only have +the public key and receive it through some communication channel */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(other_public_key, other_private_key, seed); + +/* do a key exchange with other_public_key */ +ed25519_key_exchange(shared_secret, other_public_key, private_key); + +/* + the magic here is that ed25519_key_exchange(shared_secret, public_key, + other_private_key); would result in the same shared_secret +*/ + +``` + +License +------- +All code is in the public domain. diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp new file mode 100644 index 0000000000..72cc852b93 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/add_scalar.cpp @@ -0,0 +1,56 @@ +#include "libtorrent/ed25519.hpp" +#include "ge.h" +#include "sc.h" + + +/* see http://crypto.stackexchange.com/a/6215/4697 */ +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { + const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ + + unsigned char n[32]; + ge_p3 nB; + ge_p1p1 A_p1p1; + ge_p3 A; + ge_p3 public_key_unpacked; + ge_cached T; + + int i; + + /* copy the scalar and clear highest bit */ + for (i = 0; i < 31; ++i) { + n[i] = scalar[i]; + } + n[31] = scalar[31] & 127; + + /* private key: a = n + t */ + if (private_key) { + sc_muladd(private_key, SC_1, n, private_key); + } + + /* public key: A = nB + T */ + if (public_key) { + /* if we know the private key we don't need a point addition, which is faster */ + /* using a "timing attack" you could find out wether or not we know the private + key, but this information seems rather useless - if this is important pass + public_key and private_key seperately in 2 function calls */ + if (private_key) { + ge_scalarmult_base(&A, private_key); + } else { + /* unpack public key into T */ + ge_frombytes_negate_vartime(&public_key_unpacked, public_key); + fe_neg(public_key_unpacked.X, public_key_unpacked.X); // undo negate + fe_neg(public_key_unpacked.T, public_key_unpacked.T); // undo negate + ge_p3_to_cached(&T, &public_key_unpacked); + + /* calculate n*B */ + ge_scalarmult_base(&nB, n); + + /* A = n*B + T */ + ge_add(&A_p1p1, &nB, &T); + ge_p1p1_to_p3(&A, &A_p1p1); + } + + /* pack public key */ + ge_p3_tobytes(public_key, &A); + } +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp new file mode 100644 index 0000000000..b5889daef8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fe.cpp @@ -0,0 +1,1501 @@ +#include "fixedint.h" +#include "fe.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4146 ) /* warning C4146: unary minus operator applied to unsigned type, result still unsigned */ +#pragma warning(disable : 4244 ) /* warning C4244: '=' : conversion from 'int64_t' to 'int32_t', possible loss of data */ +#endif // _MSC_VER + +/* + helper functions +*/ +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + + + +/* + h = 0 +*/ + +void fe_0(fe h) { + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = 1 +*/ + +void fe_1(fe h) { + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = f + g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 + g0; + int32_t h1 = f1 + g1; + int32_t h2 = f2 + g2; + int32_t h3 = f3 + g3; + int32_t h4 = f4 + g4; + int32_t h5 = f5 + g5; + int32_t h6 = f6 + g6; + int32_t h7 = f7 + g7; + int32_t h8 = f8 + g8; + int32_t h9 = f9 + g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f, const fe g, unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/* + Replace (f,g) with (g,f) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cswap(fe f,fe g,unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + b = -b; // warning C4146: unary minus operator applied to unsigned type, result still unsigned + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; + g[0] = g0 ^ x0; + g[1] = g1 ^ x1; + g[2] = g2 ^ x2; + g[3] = g3 ^ x3; + g[4] = g4 ^ x4; + g[5] = g5 ^ x5; + g[6] = g6 ^ x6; + g[7] = g7 ^ x7; + g[8] = g8 ^ x8; + g[9] = g9 ^ x9; +} + + + +/* + h = f +*/ + +void fe_copy(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + + + +/* + Ignores top bit of h. +*/ + +void fe_frombytes(fe h, const unsigned char *s) { + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + + +void fe_invert(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 20; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 100; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(out, t1, t0); +} + + + +/* + return 1 if f is in {1,3,5,...,q-2} + return 0 if f is in {0,2,4,...,q-1} + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) { + unsigned char s[32]; + + fe_tobytes(s, f); + + return s[0] & 1; +} + + + +/* + return 1 if f == 0 + return 0 if f != 0 + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnonzero(const fe f) { + unsigned char s[32]; + unsigned char r; + + fe_tobytes(s, f); + + r = s[0]; + #define F(i) r |= s[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return r != 0; +} + + + +/* + h = f * g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + + /* + Notes on implementation strategy: + + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + + With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f * 121666 +Can overlap h with f. + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_mul121666(fe h, fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int64_t h0 = f0 * (int64_t) 121666; + int64_t h1 = f1 * (int64_t) 121666; + int64_t h2 = f2 * (int64_t) 121666; + int64_t h3 = f3 * (int64_t) 121666; + int64_t h4 = f4 * (int64_t) 121666; + int64_t h5 = f5 * (int64_t) 121666; + int64_t h6 = f6 * (int64_t) 121666; + int64_t h7 = f7 * (int64_t) 121666; + int64_t h8 = f8 * (int64_t) 121666; + int64_t h9 = f9 * (int64_t) 121666; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t h0 = -f0; + int32_t h1 = -f1; + int32_t h2 = -f2; + int32_t h3 = -f3; + int32_t h4 = -f4; + int32_t h5 = -f5; + int32_t h6 = -f6; + int32_t h7 = -f7; + int32_t h8 = -f8; + int32_t h9 = -f9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +void fe_pow22523(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + int i; + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t0, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 20; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 100; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t0, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t0, t0); + } + + fe_mul(out, t0, z); + return; +} + + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 - g0; + int32_t h1 = f1 - g1; + int32_t h2 = f2 - g2; + int32_t h3 = f3 - g3; + int32_t h4 = f4 - g4; + int32_t h5 = f5 - g5; + int32_t h6 = f6 - g6; + int32_t h7 = f7 - g7; + int32_t h8 = f8 - g8; + int32_t h9 = f9 - g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + carry0 = h0 >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry1 = h1 >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry2 = h2 >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry3 = h3 >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry4 = h4 >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry5 = h5 >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry6 = h6 >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry7 = h7 >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry8 = h8 >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = h9 >> 25; + h9 -= carry9 << 25; + + /* h10 = carry9 */ + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + s[0] = (unsigned char) ((h0 >> 0) & 0xff); + s[1] = (unsigned char) ((h0 >> 8) & 0xff); + s[2] = (unsigned char) ((h0 >> 16) & 0xff); + s[3] = (unsigned char) (((h0 >> 24) | (h1 << 2)) & 0xff); + s[4] = (unsigned char) ((h1 >> 6) & 0xff); + s[5] = (unsigned char) ((h1 >> 14) & 0xff); + s[6] = (unsigned char) (((h1 >> 22) | (h2 << 3)) & 0xff); + s[7] = (unsigned char) ((h2 >> 5) & 0xff); + s[8] = (unsigned char) ((h2 >> 13) & 0xff); + s[9] = (unsigned char) (((h2 >> 21) | (h3 << 5)) & 0xff); + s[10] = (unsigned char) ((h3 >> 3) & 0xff); + s[11] = (unsigned char) ((h3 >> 11) & 0xff); + s[12] = (unsigned char) (((h3 >> 19) | (h4 << 6)) & 0xff); + s[13] = (unsigned char) ((h4 >> 2) & 0xff); + s[14] = (unsigned char) ((h4 >> 10) & 0xff); + s[15] = (unsigned char) ((h4 >> 18) & 0xff); + s[16] = (unsigned char) ((h5 >> 0) & 0xff); + s[17] = (unsigned char) ((h5 >> 8) & 0xff); + s[18] = (unsigned char) ((h5 >> 16) & 0xff); + s[19] = (unsigned char) (((h5 >> 24) | (h6 << 1)) & 0xff); + s[20] = (unsigned char) ((h6 >> 7) & 0xff); + s[21] = (unsigned char) ((h6 >> 15) & 0xff); + s[22] = (unsigned char) (((h6 >> 23) | (h7 << 3)) & 0xff); + s[23] = (unsigned char) ((h7 >> 5) & 0xff); + s[24] = (unsigned char) ((h7 >> 13) & 0xff); + s[25] = (unsigned char) (((h7 >> 21) | (h8 << 4)) & 0xff); + s[26] = (unsigned char) ((h8 >> 4) & 0xff); + s[27] = (unsigned char) ((h8 >> 12) & 0xff); + s[28] = (unsigned char) (((h8 >> 20) | (h9 << 6)) & 0xff); + s[29] = (unsigned char) ((h9 >> 2) & 0xff); + s[30] = (unsigned char) ((h9 >> 10) & 0xff); + s[31] = (unsigned char) ((h9 >> 18) & 0xff); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fe.h b/apps/Launcher/ext/libtorrent/ed25519/src/fe.h new file mode 100644 index 0000000000..b4b62d2828 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fe.h @@ -0,0 +1,41 @@ +#ifndef FE_H +#define FE_H + +#include "fixedint.h" + + +/* + fe means field element. + Here the field is \Z/(2^255-19). + An element t, entries t[0]...t[9], represents the integer + t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. + Bounds on each t[i] vary depending on context. +*/ + + +typedef int32_t fe[10]; + + +void fe_0(fe h); +void fe_1(fe h); + +void fe_frombytes(fe h, const unsigned char *s); +void fe_tobytes(unsigned char *s, const fe h); + +void fe_copy(fe h, const fe f); +int fe_isnegative(const fe f); +int fe_isnonzero(const fe f); +void fe_cmov(fe f, const fe g, unsigned int b); +void fe_cswap(fe f, fe g, unsigned int b); + +void fe_neg(fe h, const fe f); +void fe_add(fe h, const fe f, const fe g); +void fe_invert(fe out, const fe z); +void fe_sq(fe h, const fe f); +void fe_sq2(fe h, const fe f); +void fe_mul(fe h, const fe f, const fe g); +void fe_mul121666(fe h, fe f); +void fe_pow22523(fe out, const fe z); +void fe_sub(fe h, const fe f, const fe g); + +#endif diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h b/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h new file mode 100644 index 0000000000..c7f608c4b8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/fixedint.h @@ -0,0 +1,6 @@ +#include + +using boost::uint64_t; +using boost::int64_t; +using boost::int32_t; + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp new file mode 100644 index 0000000000..3c342b1d21 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/ge.cpp @@ -0,0 +1,467 @@ +#include "ge.h" +#include "precomp_data.h" + + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YplusX); + fe_mul(r->Y, r->Y, q->YminusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +static void slide(signed char *r, const unsigned char *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; + r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + + r[k] = 0; + } + } else { + break; + } + } + } + } +} + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + slide(aslide, a); + slide(bslide, b); + ge_p3_to_cached(&Ai[0], A); + ge_p3_dbl(&t, A); + ge_p1p1_to_p3(&A2, &t); + ge_add(&t, &A2, &Ai[0]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[1], &u); + ge_add(&t, &A2, &Ai[1]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[2], &u); + ge_add(&t, &A2, &Ai[2]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[3], &u); + ge_add(&t, &A2, &Ai[3]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[4], &u); + ge_add(&t, &A2, &Ai[4]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[5], &u); + ge_add(&t, &A2, &Ai[5]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[6], &u); + ge_add(&t, &A2, &Ai[6]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[7], &u); + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + + +static const fe d = { + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 +}; + +static const fe sqrtm1 = { + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 +}; + +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { + fe u; + fe v; + fe v3; + fe vxx; + fe check; + fe_frombytes(h->Y, s); + fe_1(h->Z); + fe_sq(u, h->Y); + fe_mul(v, u, d); + fe_sub(u, u, h->Z); /* u = y^2-1 */ + fe_add(v, v, h->Z); /* v = dy^2+1 */ + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(h->X, v3); + fe_mul(h->X, h->X, v); + fe_mul(h->X, h->X, u); /* x = uv^7 */ + fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X, h->X, v3); + fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + fe_sq(vxx, h->X); + fe_mul(vxx, vxx, v); + fe_sub(check, vxx, u); /* vx^2-u */ + + if (fe_isnonzero(check)) { + fe_add(check, vxx, u); /* vx^2+u */ + + if (fe_isnonzero(check)) { + return -1; + } + + fe_mul(h->X, h->X, sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) { + fe_neg(h->X, h->X); + } + + fe_mul(h->T, h->X, h->Y); + return 0; +} + + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yplusx); + fe_mul(r->Y, r->Y, q->yminusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yminusx); + fe_mul(r->Y, r->Y, q->yplusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +/* +r = p +*/ + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); +} + + + +/* +r = p +*/ + +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); + fe_mul(r->T, p->X, p->Y); +} + + +void ge_p2_0(ge_p2 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} + + + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe t0; + + fe_sq(r->X, p->X); + fe_sq(r->Z, p->Y); + fe_sq2(r->T, p->Z); + fe_add(r->Y, p->X, p->Y); + fe_sq(t0, r->Y); + fe_add(r->Y, r->Z, r->X); + fe_sub(r->Z, r->Z, r->X); + fe_sub(r->X, t0, r->Y); + fe_sub(r->T, r->T, r->Z); +} + + +void ge_p3_0(ge_p3 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} + + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + + + +/* +r = p +*/ + +static const fe d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 +}; + +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(r->YplusX, p->Y, p->X); + fe_sub(r->YminusX, p->Y, p->X); + fe_copy(r->Z, p->Z); + fe_mul(r->T2d, p->T, d2); +} + + +/* +r = p +*/ + +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(r->X, p->X); + fe_copy(r->Y, p->Y); + fe_copy(r->Z, p->Z); +} + + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + + +static unsigned char equal(signed char b, signed char c) { + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint64_t y = x; /* 0: yes; 1..255: no */ + y -= 1; /* large: yes; 0..254: no */ + y >>= 63; /* 1: yes; 0: no */ + return (unsigned char) y; +} + +static unsigned char negative(signed char b) { + uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (unsigned char) x; +} + +static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) { + fe_cmov(t->yplusx, u->yplusx, b); + fe_cmov(t->yminusx, u->yminusx, b); + fe_cmov(t->xy2d, u->xy2d, b); +} + + +static void select(ge_precomp *t, int pos, signed char b) { + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + fe_1(t->yplusx); + fe_1(t->yminusx); + fe_0(t->xy2d); + cmov(t, &base[pos][0], equal(babs, 1)); + cmov(t, &base[pos][1], equal(babs, 2)); + cmov(t, &base[pos][2], equal(babs, 3)); + cmov(t, &base[pos][3], equal(babs, 4)); + cmov(t, &base[pos][4], equal(babs, 5)); + cmov(t, &base[pos][5], equal(babs, 6)); + cmov(t, &base[pos][6], equal(babs, 7)); + cmov(t, &base[pos][7], equal(babs, 8)); + fe_copy(minust.yplusx, t->yminusx); + fe_copy(minust.yminusx, t->yplusx); + fe_neg(minust.xy2d, t->xy2d); + cmov(t, &minust, bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + carry = 0; + + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + + e[63] += carry; + /* each e[i] is between -8 and 8 */ + ge_p3_0(h); + + for (i = 1; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } + + ge_p3_dbl(&r, h); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } +} + + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YminusX); + fe_mul(r->Y, r->Y, q->YplusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +void ge_tobytes(unsigned char *s, const ge_p2 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/ge.h b/apps/Launcher/ext/libtorrent/ed25519/src/ge.h new file mode 100644 index 0000000000..17fde2df1f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/ge.h @@ -0,0 +1,74 @@ +#ifndef GE_H +#define GE_H + +#include "fe.h" + + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); +void ge_tobytes(unsigned char *s, const ge_p2 *h); +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); +void ge_p2_0(ge_p2 *h); +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); +void ge_p3_0(ge_p3 *h); +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); + +#endif diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp new file mode 100644 index 0000000000..b0b0907582 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/key_exchange.cpp @@ -0,0 +1,80 @@ +#include "libtorrent/ed25519.hpp" +#include "fe.h" + +void ed25519_key_exchange(unsigned char *shared_secret + , const unsigned char *public_key, const unsigned char *private_key) { + unsigned char e[32]; + unsigned int i; + + fe x1; + fe x2; + fe z2; + fe x3; + fe z3; + fe tmp0; + fe tmp1; + + int pos; + unsigned int swap; + unsigned int b; + + /* copy the private key and make sure it's valid */ + for (i = 0; i < 32; ++i) { + e[i] = private_key[i]; + } + + e[0] &= 248; + e[31] &= 63; + e[31] |= 64; + + /* unpack the public key and convert edwards to montgomery */ + /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ + fe_frombytes(x1, public_key); + fe_1(tmp1); + fe_add(tmp0, x1, tmp1); + fe_sub(tmp1, tmp1, x1); + fe_invert(tmp1, tmp1); + fe_mul(x1, tmp0, tmp1); + + fe_1(x2); + fe_0(z2); + fe_copy(x3, x1); + fe_1(z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + swap ^= b; + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + swap = b; + + /* from montgomery.h */ + fe_sub(tmp0, x3, z3); + fe_sub(tmp1, x2, z2); + fe_add(x2, x2, z2); + fe_add(z2, x3, z3); + fe_mul(z3, tmp0, x2); + fe_mul(z2, z2, tmp1); + fe_sq(tmp0, tmp1); + fe_sq(tmp1, x2); + fe_add(x3, z3, z2); + fe_sub(z2, z3, z2); + fe_mul(x2, tmp1, tmp0); + fe_sub(tmp1, tmp1, tmp0); + fe_sq(z2, z2); + fe_mul121666(z3, tmp1); + fe_sq(x3, x3); + fe_add(tmp0, tmp0, z3); + fe_mul(z3, x1, z2); + fe_mul(z2, tmp1, tmp0); + } + + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + + fe_invert(z2, z2); + fe_mul(x2, x2, z2); + fe_tobytes(shared_secret, x2); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp new file mode 100644 index 0000000000..7d2d143384 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/keypair.cpp @@ -0,0 +1,16 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" + + +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { + ge_p3 A; + + sha512(seed, 32, private_key); + private_key[0] &= 248; + private_key[31] &= 63; + private_key[31] |= 64; + + ge_scalarmult_base(&A, private_key); + ge_p3_tobytes(public_key, &A); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h b/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h new file mode 100644 index 0000000000..ea00e4cc66 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/precomp_data.h @@ -0,0 +1,1392 @@ +static ge_precomp Bi[8] = { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, + { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, + { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, + }, + { + { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, + { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, + { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, + }, + { + { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, + { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, + { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, + }, + { + { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, + { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, + { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, + }, +}; + + +/* base[i][j] = (j+1)*256^i*B */ +static ge_precomp base[32][8] = { + { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, + { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, + { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, + { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, + { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, + { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, + { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, + { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, + { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, + }, + }, + { + { + { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, + { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, + { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, + }, + { + { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, + { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, + { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, + }, + { + { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, + { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, + { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, + }, + { + { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, + { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, + { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, + }, + { + { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, + { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, + { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, + }, + { + { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, + { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, + { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, + }, + { + { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, + { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, + { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, + }, + { + { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, + { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, + { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, + }, + }, + { + { + { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, + { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, + { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, + }, + { + { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, + { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, + { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, + }, + { + { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, + { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, + { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, + }, + { + { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, + { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, + { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, + }, + { + { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, + { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, + { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, + }, + { + { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, + { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, + { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, + }, + { + { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, + { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, + { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, + }, + { + { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, + { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, + { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, + }, + }, + { + { + { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, + { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, + { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, + }, + { + { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, + { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, + { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, + }, + { + { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, + { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, + { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, + }, + { + { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, + { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, + { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, + }, + { + { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, + { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, + { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, + }, + { + { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, + { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, + { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, + }, + { + { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, + { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, + { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, + }, + { + { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, + { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, + { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, + }, + }, + { + { + { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, + { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, + { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, + }, + { + { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, + { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, + { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, + }, + { + { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, + { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, + { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, + }, + { + { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, + { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, + { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, + }, + { + { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, + { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, + { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, + }, + { + { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, + { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, + { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, + }, + { + { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, + { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, + { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, + }, + { + { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, + { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, + { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, + }, + }, + { + { + { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, + { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, + { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, + }, + { + { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, + { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, + { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, + }, + { + { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, + { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, + { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, + }, + { + { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, + { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, + { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, + }, + { + { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, + { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, + { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, + }, + { + { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, + { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, + { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, + }, + { + { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, + { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, + { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, + }, + { + { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, + { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, + { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, + }, + }, + { + { + { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, + { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, + { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, + }, + { + { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, + { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, + { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, + }, + { + { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, + { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, + { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, + }, + { + { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, + { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, + { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, + }, + { + { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, + { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, + { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, + }, + { + { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, + { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, + { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, + }, + { + { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, + { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, + { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, + }, + { + { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, + { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, + { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, + }, + }, + { + { + { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, + { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, + { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, + }, + { + { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, + { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, + { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, + }, + { + { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, + { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, + { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, + }, + { + { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, + { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, + { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, + }, + { + { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, + { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, + { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, + }, + { + { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, + { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, + { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, + }, + { + { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, + { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, + { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, + }, + { + { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, + { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, + { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, + }, + }, + { + { + { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, + { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, + { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, + }, + { + { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, + { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, + { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, + }, + { + { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, + { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, + { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, + }, + { + { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, + { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, + { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, + }, + { + { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, + { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, + { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, + }, + { + { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, + { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, + { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, + }, + { + { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, + { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, + { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, + }, + { + { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, + { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, + { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, + }, + }, + { + { + { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, + { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, + { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, + }, + { + { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, + { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, + { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, + }, + { + { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, + { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, + { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, + }, + { + { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, + { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, + { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, + }, + { + { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, + { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, + { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, + }, + { + { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, + { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, + { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, + }, + { + { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, + { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, + { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, + }, + { + { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, + { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, + { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, + }, + }, + { + { + { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, + { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, + { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, + }, + { + { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, + { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, + { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, + }, + { + { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, + { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, + { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, + }, + { + { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, + { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, + { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, + }, + { + { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, + { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, + { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, + }, + { + { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, + { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, + { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, + }, + { + { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, + { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, + { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, + }, + { + { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, + { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, + { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, + }, + }, + { + { + { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, + { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, + { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, + }, + { + { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, + { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, + { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, + }, + { + { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, + { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, + { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, + }, + { + { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, + { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, + { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, + }, + { + { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, + { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, + { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, + }, + { + { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, + { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, + { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, + }, + { + { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, + { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, + { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, + }, + { + { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, + { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, + { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, + }, + }, + { + { + { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, + { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, + { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, + }, + { + { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, + { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, + { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, + }, + { + { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, + { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, + { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, + }, + { + { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, + { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, + { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, + }, + { + { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, + { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, + { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, + }, + { + { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, + { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, + { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, + }, + { + { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, + { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, + { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, + }, + { + { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, + { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, + { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, + }, + }, + { + { + { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, + { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, + { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, + }, + { + { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, + { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, + { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, + }, + { + { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, + { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, + { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, + }, + { + { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, + { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, + { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, + }, + { + { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, + { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, + { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, + }, + { + { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, + { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, + { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, + }, + { + { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, + { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, + { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, + }, + { + { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, + { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, + { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, + }, + }, + { + { + { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, + { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, + { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, + }, + { + { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, + { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, + { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, + }, + { + { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, + { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, + { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, + }, + { + { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, + { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, + { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, + }, + { + { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, + { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, + { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, + }, + { + { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, + { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, + { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, + }, + { + { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, + { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, + { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, + }, + { + { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, + { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, + { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, + }, + }, + { + { + { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, + { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, + { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, + }, + { + { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, + { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, + { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, + }, + { + { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, + { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, + { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, + }, + { + { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, + { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, + { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, + }, + { + { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, + { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, + { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, + }, + { + { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, + { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, + { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, + }, + { + { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, + { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, + { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, + }, + { + { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, + { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, + { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, + }, + }, + { + { + { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, + { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, + { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, + }, + { + { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, + { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, + { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, + }, + { + { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, + { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, + { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, + }, + { + { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, + { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, + { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, + }, + { + { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, + { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, + { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, + }, + { + { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, + { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, + { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, + }, + { + { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, + { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, + { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, + }, + { + { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, + { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, + { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, + }, + }, + { + { + { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, + { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, + { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, + }, + { + { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, + { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, + { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, + }, + { + { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, + { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, + { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, + }, + { + { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, + { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, + { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, + }, + { + { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, + { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, + { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, + }, + { + { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, + { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, + { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, + }, + { + { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, + { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, + { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, + }, + { + { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, + { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, + { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, + }, + }, + { + { + { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, + { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, + { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, + }, + { + { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, + { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, + { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, + }, + { + { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, + { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, + { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, + }, + { + { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, + { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, + { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, + }, + { + { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, + { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, + { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, + }, + { + { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, + { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, + { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, + }, + { + { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, + { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, + { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, + }, + { + { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, + { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, + { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, + }, + }, + { + { + { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, + { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, + { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, + }, + { + { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, + { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, + { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, + }, + { + { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, + { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, + { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, + }, + { + { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, + { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, + { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, + }, + { + { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, + { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, + { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, + }, + { + { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, + { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, + { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, + }, + { + { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, + { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, + { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, + }, + { + { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, + { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, + { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, + }, + }, + { + { + { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, + { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, + { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, + }, + { + { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, + { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, + { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, + }, + { + { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, + { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, + { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, + }, + { + { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, + { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, + { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, + }, + { + { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, + { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, + { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, + }, + { + { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, + { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, + { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, + }, + { + { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, + { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, + { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, + }, + { + { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, + { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, + { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, + }, + }, + { + { + { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, + { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, + { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, + }, + { + { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, + { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, + { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, + }, + { + { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, + { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, + { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, + }, + { + { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, + { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, + { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, + }, + { + { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, + { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, + { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, + }, + { + { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, + { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, + { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, + }, + { + { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, + { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, + { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, + }, + { + { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, + { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, + { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, + }, + }, + { + { + { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, + { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, + { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, + }, + { + { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, + { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, + { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, + }, + { + { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, + { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, + { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, + }, + { + { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, + { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, + { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, + }, + { + { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, + { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, + { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, + }, + { + { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, + { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, + { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, + }, + { + { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, + { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, + { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, + }, + { + { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, + { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, + { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, + }, + }, + { + { + { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, + { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, + { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, + }, + { + { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, + { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, + { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, + }, + { + { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, + { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, + { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, + }, + { + { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, + { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, + { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, + }, + { + { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, + { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, + { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, + }, + { + { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, + { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, + { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, + }, + { + { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, + { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, + { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, + }, + { + { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, + { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, + { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, + }, + }, + { + { + { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, + { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, + { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, + }, + { + { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, + { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, + { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, + }, + { + { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, + { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, + { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, + }, + { + { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, + { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, + { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, + }, + { + { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, + { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, + { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, + }, + { + { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, + { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, + { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, + }, + { + { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, + { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, + { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, + }, + { + { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, + { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, + { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, + }, + }, + { + { + { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, + { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, + { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, + }, + { + { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, + { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, + { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, + }, + { + { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, + { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, + { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, + }, + { + { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, + { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, + { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, + }, + { + { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, + { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, + { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, + }, + { + { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, + { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, + { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, + }, + { + { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, + { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, + { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, + }, + { + { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, + { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, + { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, + }, + }, + { + { + { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, + { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, + { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, + }, + { + { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, + { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, + { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, + }, + { + { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, + { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, + { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, + }, + { + { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, + { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, + { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, + }, + { + { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, + { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, + { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, + }, + { + { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, + { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, + { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, + }, + { + { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, + { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, + { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, + }, + { + { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, + { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, + { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, + }, + }, + { + { + { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, + { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, + { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, + }, + { + { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, + { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, + { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, + }, + { + { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, + { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, + { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, + }, + { + { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, + { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, + { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, + }, + { + { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, + { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, + { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, + }, + { + { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, + { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, + { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, + }, + { + { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, + { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, + { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, + }, + { + { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, + { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, + { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, + }, + }, + { + { + { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, + { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, + { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, + }, + { + { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, + { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, + { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, + }, + { + { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, + { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, + { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, + }, + { + { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, + { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, + { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, + }, + { + { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, + { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, + { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, + }, + { + { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, + { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, + { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, + }, + { + { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, + { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, + { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, + }, + { + { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, + { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, + { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, + }, + }, + { + { + { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, + { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, + { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, + }, + { + { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, + { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, + { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, + }, + { + { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, + { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, + { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, + }, + { + { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, + { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, + { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, + }, + { + { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, + { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, + { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, + }, + { + { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, + { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, + { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, + }, + { + { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, + { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, + { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, + }, + { + { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, + { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, + { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, + }, + }, + { + { + { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, + { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, + { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, + }, + { + { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, + { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, + { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, + }, + { + { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, + { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, + { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, + }, + { + { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, + { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, + { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, + }, + { + { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, + { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, + { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, + }, + { + { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, + { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, + { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, + }, + { + { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, + { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, + { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, + }, + { + { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, + { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, + { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, + }, + }, + { + { + { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, + { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, + { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, + }, + { + { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, + { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, + { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, + }, + { + { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, + { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, + { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, + }, + { + { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, + { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, + { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, + }, + { + { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, + { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, + { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, + }, + { + { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, + { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, + { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, + }, + { + { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, + { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, + { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, + }, + { + { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, + { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, + { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, + }, + }, +}; + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp new file mode 100644 index 0000000000..d04c92d52f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sc.cpp @@ -0,0 +1,809 @@ +#include "fixedint.h" +#include "sc.h" + +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) ((s0 >> 0) & 0xff); + s[1] = (unsigned char) ((s0 >> 8) & 0xff); + s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); + s[3] = (unsigned char) ((s1 >> 3) & 0xff); + s[4] = (unsigned char) ((s1 >> 11) & 0xff); + s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); + s[6] = (unsigned char) ((s2 >> 6) & 0xff); + s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); + s[8] = (unsigned char) ((s3 >> 1) & 0xff); + s[9] = (unsigned char) ((s3 >> 9) & 0xff); + s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); + s[11] = (unsigned char) ((s4 >> 4) & 0xff); + s[12] = (unsigned char) ((s4 >> 12) & 0xff); + s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); + s[14] = (unsigned char) ((s5 >> 7) & 0xff); + s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); + s[16] = (unsigned char) ((s6 >> 2) & 0xff); + s[17] = (unsigned char) ((s6 >> 10) & 0xff); + s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); + s[19] = (unsigned char) ((s7 >> 5) & 0xff); + s[20] = (unsigned char) ((s7 >> 13) & 0xff); + s[21] = (unsigned char) ((s8 >> 0) & 0xff); + s[22] = (unsigned char) ((s8 >> 8) & 0xff); + s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); + s[24] = (unsigned char) ((s9 >> 3) & 0xff); + s[25] = (unsigned char) ((s9 >> 11) & 0xff); + s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); + s[27] = (unsigned char) ((s10 >> 6) & 0xff); + s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); + s[29] = (unsigned char) ((s11 >> 1) & 0xff); + s[30] = (unsigned char) ((s11 >> 9) & 0xff); + s[31] = (unsigned char) ((s11 >> 17) & 0xff); +} + + + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; + s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry18 = (s18 + (1 << 20)) >> 21; + s19 += carry18; + s18 -= carry18 << 21; + carry20 = (s20 + (1 << 20)) >> 21; + s21 += carry20; + s20 -= carry20 << 21; + carry22 = (s22 + (1 << 20)) >> 21; + s23 += carry22; + s22 -= carry22 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + carry17 = (s17 + (1 << 20)) >> 21; + s18 += carry17; + s17 -= carry17 << 21; + carry19 = (s19 + (1 << 20)) >> 21; + s20 += carry19; + s19 -= carry19 << 21; + carry21 = (s21 + (1 << 20)) >> 21; + s22 += carry21; + s21 -= carry21 << 21; + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) ((s0 >> 0) & 0xff); + s[1] = (unsigned char) ((s0 >> 8) & 0xff); + s[2] = (unsigned char) (((s0 >> 16) | (s1 << 5)) & 0xff); + s[3] = (unsigned char) ((s1 >> 3) & 0xff); + s[4] = (unsigned char) ((s1 >> 11) & 0xff); + s[5] = (unsigned char) (((s1 >> 19) | (s2 << 2)) & 0xff); + s[6] = (unsigned char) ((s2 >> 6) & 0xff); + s[7] = (unsigned char) (((s2 >> 14) | (s3 << 7)) & 0xff); + s[8] = (unsigned char) ((s3 >> 1) & 0xff); + s[9] = (unsigned char) ((s3 >> 9) & 0xff); + s[10] = (unsigned char) (((s3 >> 17) | (s4 << 4)) & 0xff); + s[11] = (unsigned char) ((s4 >> 4) & 0xff); + s[12] = (unsigned char) ((s4 >> 12) & 0xff); + s[13] = (unsigned char) (((s4 >> 20) | (s5 << 1)) & 0xff); + s[14] = (unsigned char) ((s5 >> 7) & 0xff); + s[15] = (unsigned char) (((s5 >> 15) | (s6 << 6)) & 0xff); + s[16] = (unsigned char) ((s6 >> 2) & 0xff); + s[17] = (unsigned char) ((s6 >> 10) & 0xff); + s[18] = (unsigned char) (((s6 >> 18) | (s7 << 3)) & 0xff); + s[19] = (unsigned char) ((s7 >> 5) & 0xff); + s[20] = (unsigned char) ((s7 >> 13) & 0xff); + s[21] = (unsigned char) ((s8 >> 0) & 0xff); + s[22] = (unsigned char) ((s8 >> 8) & 0xff); + s[23] = (unsigned char) (((s8 >> 16) | (s9 << 5)) & 0xff); + s[24] = (unsigned char) ((s9 >> 3) & 0xff); + s[25] = (unsigned char) ((s9 >> 11) & 0xff); + s[26] = (unsigned char) (((s9 >> 19) | (s10 << 2)) & 0xff); + s[27] = (unsigned char) ((s10 >> 6) & 0xff); + s[28] = (unsigned char) (((s10 >> 14) | (s11 << 7)) & 0xff); + s[29] = (unsigned char) ((s11 >> 1) & 0xff); + s[30] = (unsigned char) ((s11 >> 9) & 0xff); + s[31] = (unsigned char) ((s11 >> 17) & 0xff); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sc.h b/apps/Launcher/ext/libtorrent/ed25519/src/sc.h new file mode 100644 index 0000000000..0c058e532b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sc.h @@ -0,0 +1,13 @@ +#ifndef SC_H +#define SC_H + +/* +The set of scalars is \Z/l +where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_reduce(unsigned char *s); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp new file mode 100644 index 0000000000..7619c66bcb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/seed.cpp @@ -0,0 +1,41 @@ +#include "libtorrent/ed25519.hpp" + +#ifndef ED25519_NO_SEED + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +int ed25519_create_seed(unsigned char *seed) { +#ifdef _WIN32 + HCRYPTPROV prov; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 1; + } + + if (!CryptGenRandom(prov, 32, seed)) { + CryptReleaseContext(prov, 0); + return 1; + } + + CryptReleaseContext(prov, 0); +#else + FILE *f = fopen("/dev/urandom", "rb"); + + if (f == NULL) { + return 1; + } + + fread(seed, 1, 32, f); + fclose(f); +#endif + + return 0; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp new file mode 100644 index 0000000000..3d659030de --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.cpp @@ -0,0 +1,279 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include "fixedint.h" +#include "sha512.h" + +#ifndef UINT64_C +#define UINT64_C(x) x ## LL +#endif + +/* the K array */ +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* Various logical functions */ + +#define ROR64c(x, y) \ + ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ + ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ + (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ + (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ + (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 1024-bits */ +static int sha512_compress(sha512_context *md, unsigned char *buf) +{ + uint64_t S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + +/* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c);\ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } + + #undef RND + + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha512_init(sha512_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0x6a09e667f3bcc908); + md->state[1] = UINT64_C(0xbb67ae8584caa73b); + md->state[2] = UINT64_C(0x3c6ef372fe94f82b); + md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); + md->state[4] = UINT64_C(0x510e527fade682d1); + md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); + md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); + md->state[7] = UINT64_C(0x5be0cd19137e2179); + + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) +{ + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 128) { + if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 128 * 8; + in += 128; + inlen -= 128; + } else { + n = MIN(inlen, (128 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 128) { + if ((err = sha512_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*128; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return 0 if successful +*/ + int sha512_final(sha512_context * md, unsigned char *out) + { + int i; + + if (md == NULL) return 1; + if (out == NULL) return 1; + + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + + /* increase the length of the message */ + md->length += md->curlen * UINT64_C(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ +while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char)0; +} + + /* store length */ +STORE64H(md->length, md->buf+120); +sha512_compress(md, md->buf); + + /* copy output */ +for (i = 0; i < 8; i++) { + STORE64H(md->state[i], out+(8*i)); +} + +return 0; +} + +int sha512(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha512_context ctx; + int ret; + if ((ret = sha512_init(&ctx))) return ret; + if ((ret = sha512_update(&ctx, message, message_len))) return ret; + if ((ret = sha512_final(&ctx, out))) return ret; + return 0; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h new file mode 100644 index 0000000000..0cdd0a3d58 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sha512.h @@ -0,0 +1,22 @@ +#ifndef SHA512_H +#define SHA512_H + +#include + +#include "fixedint.h" + +/* state */ +typedef struct sha512_context_ { + uint64_t length, state[8]; + size_t curlen; + unsigned char buf[128]; +} sha512_context; + + +int sha512_init(sha512_context * md); +int sha512_final(sha512_context * md, unsigned char *out); +int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); +int sha512(const unsigned char *message, size_t message_len, unsigned char *out); + +#endif + diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp new file mode 100644 index 0000000000..6badd2c031 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/sign.cpp @@ -0,0 +1,31 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + + +void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { + sha512_context hash; + unsigned char hram[64]; + unsigned char r[64]; + ge_p3 R; + + + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, r); + + sc_reduce(r); + ge_scalarmult_base(&R, r); + ge_p3_tobytes(signature, &R); + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, hram); + + sc_reduce(hram); + sc_muladd(signature + 32, hram, private_key, r); +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp b/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp new file mode 100644 index 0000000000..37b4ed573a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/src/verify.cpp @@ -0,0 +1,77 @@ +#include "libtorrent/ed25519.hpp" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + +static int consttime_equal(const unsigned char *x, const unsigned char *y) { + unsigned char r = 0; + + r = x[0] ^ y[0]; + #define F(i) r |= x[i] ^ y[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return !r; +} + +int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { + unsigned char h[64]; + unsigned char checker[32]; + sha512_context hash; + ge_p3 A; + ge_p2 R; + + if (signature[63] & 224) { + return 0; + } + + if (ge_frombytes_negate_vartime(&A, public_key) != 0) { + return 0; + } + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, h); + + sc_reduce(h); + ge_double_scalarmult_vartime(&R, h, &A, signature + 32); + ge_tobytes(checker, &R); + + if (!consttime_equal(checker, signature)) { + return 0; + } + + return 1; +} diff --git a/apps/Launcher/ext/libtorrent/ed25519/test.c b/apps/Launcher/ext/libtorrent/ed25519/test.c new file mode 100644 index 0000000000..a56d202575 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/ed25519/test.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +//#define ED25519_DLL +#include "src/ed25519.h" + +#include "src/ge.h" +#include "src/sc.h" + +const char message[] = "Hello, world!"; + + +int main(int argc, char *argv[]) { + unsigned char public_key[32], private_key[64], seed[32], scalar[32]; + unsigned char other_public_key[32], other_private_key[64]; + unsigned char shared_secret[32], other_shared_secret[32]; + unsigned char signature[64]; + + clock_t start; + clock_t end; + int i; + + /* create a random seed, and a keypair out of that seed */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + + /* create signature on the message with the keypair */ + ed25519_sign(signature, message, strlen(message), public_key, private_key); + + /* verify the signature */ + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* create scalar and add it to the keypair */ + ed25519_create_seed(scalar); + ed25519_add_scalar(public_key, private_key, scalar); + + /* create signature with the new keypair */ + ed25519_sign(signature, message, strlen(message), public_key, private_key); + + /* verify the signature with the new keypair */ + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* make a slight adjustment and verify again */ + signature[44] ^= 0x10; + if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("did not detect signature change\n"); + } else { + printf("correctly detected signature change\n"); + } + + /* generate two keypairs for testing key exchange */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_create_seed(seed); + ed25519_create_keypair(other_public_key, other_private_key, seed); + + /* create two shared secrets - from both perspectives - and check if they're equal */ + ed25519_key_exchange(shared_secret, other_public_key, private_key); + ed25519_key_exchange(other_shared_secret, public_key, other_private_key); + + for (i = 0; i < 32; ++i) { + if (shared_secret[i] != other_shared_secret[i]) { + printf("key exchange was incorrect\n"); + break; + } + } + + if (i == 32) { + printf("key exchange was correct\n"); + } + + /* test performance */ + printf("testing seed generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_seed(seed); + } + end = clock(); + + printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing key generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_keypair(public_key, private_key, seed); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing sign performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_sign(signature, message, strlen(message), public_key, private_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing verify performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_verify(signature, message, strlen(message), public_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing keypair scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, private_key, scalar); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing public key scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, NULL, scalar); + } + end = clock(); + + printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing key exchange performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_key_exchange(shared_secret, other_public_key, private_key); + } + end = clock(); + + printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + return 0; +} diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h b/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h new file mode 100644 index 0000000000..e95a5cdeae --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ConvertUTF.h @@ -0,0 +1,165 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: , , + or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +#ifdef __cplusplus +#include "libtorrent/config.hpp" +// these are standard C types, but they might +// not be available in c++ +#include +typedef boost::uint32_t UTF32; +typedef boost::uint16_t UTF16; +typedef boost::uint8_t UTF8; +extern "C" { +#else +#define TORRENT_EXTRA_EXPORT +#ifdef _MSC_VER +// msvc doesn't seem to have stdint.h +typedef unsigned __int32 UTF32; +typedef unsigned __int16 UTF16; +typedef unsigned __int8 UTF8; +#else +#include +typedef uint32_t UTF32; +typedef uint16_t UTF16; +typedef uint8_t UTF8; +#endif +#endif + +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +TORRENT_EXTRA_EXPORT Boolean isLegalUTF8Sequence(const UTF8 *source, + const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif +/* --------------------------------------------------------------------- */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h b/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h new file mode 100644 index 0000000000..420a22de05 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/GeoIP.h @@ -0,0 +1,179 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* GeoIP.h + * + * Copyright (C) 2006 MaxMind LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GEOIP_H +#define GEOIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include /* for fstat */ + +#define SEGMENT_RECORD_LENGTH 3 +#define STANDARD_RECORD_LENGTH 3 +#define ORG_RECORD_LENGTH 4 +#define MAX_RECORD_LENGTH 4 +#define NUM_DB_TYPES 20 + +typedef struct GeoIPTag { + FILE *GeoIPDatabase; + char *file_path; + unsigned char *cache; + unsigned char *index_cache; + unsigned int *databaseSegments; + char databaseType; + time_t mtime; + int flags; + off_t size; + char record_length; + int charset; /* 0 iso-8859-1 1 utf8 */ + int record_iter; /* used in GeoIP_next_record */ + int netmask; /* netmask of last lookup - set using depth in _GeoIP_seek_record */ +} GeoIP; + + +typedef enum { + GEOIP_CHARSET_ISO_8859_1 = 0, + GEOIP_CHARSET_UTF8 = 1 +} GeoIPCharset; + +typedef struct GeoIPRegionTag { + char country_code[3]; + char region[3]; +} GeoIPRegion; + +typedef enum { + GEOIP_STANDARD = 0, + GEOIP_MEMORY_CACHE = 1, + GEOIP_CHECK_CACHE = 2, + GEOIP_INDEX_CACHE = 4, + GEOIP_MMAP_CACHE = 8 +} GeoIPOptions; + +typedef enum { + GEOIP_COUNTRY_EDITION = 1, + GEOIP_REGION_EDITION_REV0 = 7, + GEOIP_CITY_EDITION_REV0 = 6, + GEOIP_ORG_EDITION = 5, + GEOIP_ISP_EDITION = 4, + GEOIP_CITY_EDITION_REV1 = 2, + GEOIP_REGION_EDITION_REV1 = 3, + GEOIP_PROXY_EDITION = 8, + GEOIP_ASNUM_EDITION = 9, + GEOIP_NETSPEED_EDITION = 10, + GEOIP_DOMAIN_EDITION = 11 +} GeoIPDBTypes; + +typedef enum { + GEOIP_ANON_PROXY = 1, + GEOIP_HTTP_X_FORWARDED_FOR_PROXY = 2, + GEOIP_HTTP_CLIENT_IP_PROXY = 3 +} GeoIPProxyTypes; + +typedef enum { + GEOIP_UNKNOWN_SPEED = 0, + GEOIP_DIALUP_SPEED = 1, + GEOIP_CABLEDSL_SPEED = 2, + GEOIP_CORPORATE_SPEED = 3 +} GeoIPNetspeedValues; + +extern char **GeoIPDBFileName; +extern const char * GeoIPDBDescription[NUM_DB_TYPES]; +extern const char *GeoIPCountryDBFileName; +extern const char *GeoIPRegionDBFileName; +extern const char *GeoIPCityDBFileName; +extern const char *GeoIPOrgDBFileName; +extern const char *GeoIPISPDBFileName; + +extern const char GeoIP_country_code[253][3]; +extern const char GeoIP_country_code3[253][4]; +extern const char * GeoIP_country_name[253]; +extern const char GeoIP_country_continent[253][3]; + +#ifdef DLL +#define GEOIP_API __declspec(dllexport) +#else +#define GEOIP_API +#endif /* DLL */ + +GEOIP_API void GeoIP_setup_custom_directory(char *dir); +GEOIP_API GeoIP* GeoIP_open_type (int type, int flags); +GEOIP_API GeoIP* GeoIP_new(int flags); +GEOIP_API GeoIP* GeoIP_open(const char * filename, int flags); +GEOIP_API int GeoIP_db_avail(int type); +GEOIP_API void GeoIP_delete(GeoIP* gi); +GEOIP_API const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_code_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr); +GEOIP_API const char *GeoIP_country_name_by_name (GeoIP* gi, const char *host); +GEOIP_API const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum); + +/* Deprecated - for backwards compatibility only */ +GEOIP_API int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr); +GEOIP_API int GeoIP_country_id_by_name (GeoIP* gi, const char *host); +GEOIP_API char *GeoIP_org_by_addr (GeoIP* gi, const char *addr); +GEOIP_API char *GeoIP_org_by_name (GeoIP* gi, const char *host); +/* End deprecated */ + +GEOIP_API int GeoIP_id_by_addr (GeoIP* gi, const char *addr); +GEOIP_API int GeoIP_id_by_name (GeoIP* gi, const char *host); +GEOIP_API int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum); + +GEOIP_API GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr); +GEOIP_API GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *host); +GEOIP_API GeoIPRegion * GeoIP_region_by_ipnum (GeoIP *gi, unsigned long ipnum); + +/* Warning - don't call this after GeoIP_assign_region_by_inetaddr calls */ +GEOIP_API void GeoIPRegion_delete (GeoIPRegion *gir); + +GEOIP_API void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *gir); + +/* Used to query GeoIP Organization, ISP and AS Number databases */ +GEOIP_API char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum); +GEOIP_API char *GeoIP_name_by_addr (GeoIP* gi, const char *addr); +GEOIP_API char *GeoIP_name_by_name (GeoIP* gi, const char *host); + +GEOIP_API char *GeoIP_database_info (GeoIP* gi); +GEOIP_API unsigned char GeoIP_database_edition (GeoIP* gi); + +GEOIP_API int GeoIP_charset (GeoIP* gi); +GEOIP_API int GeoIP_set_charset (GeoIP* gi, int charset); + +GEOIP_API int GeoIP_last_netmask (GeoIP* gi); + +/* Convert region code to region name */ +GEOIP_API const char * GeoIP_region_name_by_code(const char *country_code, const char *region_code); + +/* Get timezone from country and region code */ +GEOIP_API const char * GeoIP_time_zone_by_country_and_region(const char *country_code, const char *region_code); + +#ifdef __cplusplus +} +#endif + +#endif /* GEOIP_H */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am new file mode 100644 index 0000000000..1dc28660af --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.am @@ -0,0 +1,155 @@ +includedir = @includedir@/libtorrent + +if WITH_SHIPPED_GEOIP +GEOIP_H = GeoIP.h +endif + +nobase_include_HEADERS = \ + address.hpp \ + add_torrent_params.hpp \ + alert.hpp \ + alert_manager.hpp \ + alert_dispatcher.hpp \ + alert_types.hpp \ + alloca.hpp \ + allocator.hpp \ + assert.hpp \ + bandwidth_limit.hpp \ + bandwidth_manager.hpp \ + bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp \ + bencode.hpp \ + bitfield.hpp \ + bloom_filter.hpp \ + broadcast_socket.hpp \ + bt_peer_connection.hpp \ + buffer.hpp \ + build_config.hpp \ + chained_buffer.hpp \ + config.hpp \ + connection_queue.hpp \ + ConvertUTF.h \ + copy_ptr.hpp \ + create_torrent.hpp \ + deadline_timer.hpp \ + debug.hpp \ + disk_buffer_holder.hpp \ + disk_buffer_pool.hpp \ + disk_io_thread.hpp \ + ed25519.hpp \ + entry.hpp \ + enum_net.hpp \ + error.hpp \ + error_code.hpp \ + escape_string.hpp \ + export.hpp \ + extensions.hpp \ + file.hpp \ + file_pool.hpp \ + file_storage.hpp \ + fingerprint.hpp \ + gzip.hpp \ + hasher.hpp \ + http_connection.hpp \ + http_parser.hpp \ + http_seed_connection.hpp \ + http_stream.hpp \ + http_tracker_connection.hpp \ + i2p_stream.hpp \ + identify_client.hpp \ + instantiate_connection.hpp \ + intrusive_ptr_base.hpp \ + invariant_check.hpp \ + io.hpp \ + io_service.hpp \ + io_service_fwd.hpp \ + ip_filter.hpp \ + ip_voter.hpp \ + lazy_entry.hpp \ + lsd.hpp \ + magnet_uri.hpp \ + max.hpp \ + natpmp.hpp \ + packet_buffer.hpp \ + parse_url.hpp \ + pe_crypto.hpp \ + peer_connection.hpp \ + peer.hpp \ + peer_id.hpp \ + peer_info.hpp \ + peer_request.hpp \ + piece_block_progress.hpp \ + piece_picker.hpp \ + policy.hpp \ + proxy_base.hpp \ + ptime.hpp \ + puff.hpp \ + random.hpp \ + rss.hpp \ + session.hpp \ + session_settings.hpp \ + session_status.hpp \ + settings.hpp \ + sha1_hash.hpp \ + size_type.hpp \ + sliding_average.hpp \ + socket.hpp \ + socket_io.hpp \ + socket_type.hpp \ + socket_type_fwd.hpp \ + socks5_stream.hpp \ + ssl_stream.hpp \ + stat.hpp \ + storage.hpp \ + storage_defs.hpp \ + string_util.hpp \ + thread.hpp \ + time.hpp \ + timestamp_history.hpp \ + torrent_handle.hpp \ + torrent.hpp \ + torrent_info.hpp \ + tracker_manager.hpp \ + udp_socket.hpp \ + udp_tracker_connection.hpp \ + union_endpoint.hpp \ + upnp.hpp \ + utp_socket_manager.hpp \ + utp_stream.hpp \ + utf8.hpp \ + version.hpp \ + web_connection_base.hpp \ + web_peer_connection.hpp \ + xml_parse.hpp \ + \ + $(GEOIP_H) \ + tommath.h \ + tommath_class.h \ + tommath_superclass.h \ + \ + aux_/session_impl.hpp \ + \ + extensions/logger.hpp \ + extensions/lt_trackers.hpp \ + extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp \ + extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp \ + \ + kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp \ + kademlia/find_data.hpp \ + kademlia/logging.hpp \ + kademlia/msg.hpp \ + kademlia/node.hpp \ + kademlia/node_entry.hpp \ + kademlia/node_id.hpp \ + kademlia/observer.hpp \ + kademlia/refresh.hpp \ + kademlia/routing_table.hpp \ + kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp \ + kademlia/item.hpp \ + kademlia/get_item.hpp \ + kademlia/get_peers.hpp + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in new file mode 100644 index 0000000000..fdab33e100 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/Makefile.in @@ -0,0 +1,805 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include/libtorrent +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_python.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_check_geoip.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__nobase_include_HEADERS_DIST) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__nobase_include_HEADERS_DIST = address.hpp add_torrent_params.hpp \ + alert.hpp alert_manager.hpp alert_dispatcher.hpp \ + alert_types.hpp alloca.hpp allocator.hpp assert.hpp \ + bandwidth_limit.hpp bandwidth_manager.hpp bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp bencode.hpp bitfield.hpp \ + bloom_filter.hpp broadcast_socket.hpp bt_peer_connection.hpp \ + buffer.hpp build_config.hpp chained_buffer.hpp config.hpp \ + connection_queue.hpp ConvertUTF.h copy_ptr.hpp \ + create_torrent.hpp deadline_timer.hpp debug.hpp \ + disk_buffer_holder.hpp disk_buffer_pool.hpp disk_io_thread.hpp \ + ed25519.hpp entry.hpp enum_net.hpp error.hpp error_code.hpp \ + escape_string.hpp export.hpp extensions.hpp file.hpp \ + file_pool.hpp file_storage.hpp fingerprint.hpp gzip.hpp \ + hasher.hpp http_connection.hpp http_parser.hpp \ + http_seed_connection.hpp http_stream.hpp \ + http_tracker_connection.hpp i2p_stream.hpp identify_client.hpp \ + instantiate_connection.hpp intrusive_ptr_base.hpp \ + invariant_check.hpp io.hpp io_service.hpp io_service_fwd.hpp \ + ip_filter.hpp ip_voter.hpp lazy_entry.hpp lsd.hpp \ + magnet_uri.hpp max.hpp natpmp.hpp packet_buffer.hpp \ + parse_url.hpp pe_crypto.hpp peer_connection.hpp peer.hpp \ + peer_id.hpp peer_info.hpp peer_request.hpp \ + piece_block_progress.hpp piece_picker.hpp policy.hpp \ + proxy_base.hpp ptime.hpp puff.hpp random.hpp rss.hpp \ + session.hpp session_settings.hpp session_status.hpp \ + settings.hpp sha1_hash.hpp size_type.hpp sliding_average.hpp \ + socket.hpp socket_io.hpp socket_type.hpp socket_type_fwd.hpp \ + socks5_stream.hpp ssl_stream.hpp stat.hpp storage.hpp \ + storage_defs.hpp string_util.hpp thread.hpp time.hpp \ + timestamp_history.hpp torrent_handle.hpp torrent.hpp \ + torrent_info.hpp tracker_manager.hpp udp_socket.hpp \ + udp_tracker_connection.hpp union_endpoint.hpp upnp.hpp \ + utp_socket_manager.hpp utp_stream.hpp utf8.hpp version.hpp \ + web_connection_base.hpp web_peer_connection.hpp xml_parse.hpp \ + GeoIP.h tommath.h tommath_class.h tommath_superclass.h \ + aux_/session_impl.hpp extensions/logger.hpp \ + extensions/lt_trackers.hpp extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp kademlia/find_data.hpp \ + kademlia/logging.hpp kademlia/msg.hpp kademlia/node.hpp \ + kademlia/node_entry.hpp kademlia/node_id.hpp \ + kademlia/observer.hpp kademlia/refresh.hpp \ + kademlia/routing_table.hpp kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp kademlia/item.hpp \ + kademlia/get_item.hpp kademlia/get_peers.hpp +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEOIP_CFLAGS = @GEOIP_CFLAGS@ +GEOIP_LIBS = @GEOIP_LIBS@ +GREP = @GREP@ +ICONV_LIBS = @ICONV_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@/libtorrent +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@WITH_SHIPPED_GEOIP_TRUE@GEOIP_H = GeoIP.h +nobase_include_HEADERS = \ + address.hpp \ + add_torrent_params.hpp \ + alert.hpp \ + alert_manager.hpp \ + alert_dispatcher.hpp \ + alert_types.hpp \ + alloca.hpp \ + allocator.hpp \ + assert.hpp \ + bandwidth_limit.hpp \ + bandwidth_manager.hpp \ + bandwidth_socket.hpp \ + bandwidth_queue_entry.hpp \ + bencode.hpp \ + bitfield.hpp \ + bloom_filter.hpp \ + broadcast_socket.hpp \ + bt_peer_connection.hpp \ + buffer.hpp \ + build_config.hpp \ + chained_buffer.hpp \ + config.hpp \ + connection_queue.hpp \ + ConvertUTF.h \ + copy_ptr.hpp \ + create_torrent.hpp \ + deadline_timer.hpp \ + debug.hpp \ + disk_buffer_holder.hpp \ + disk_buffer_pool.hpp \ + disk_io_thread.hpp \ + ed25519.hpp \ + entry.hpp \ + enum_net.hpp \ + error.hpp \ + error_code.hpp \ + escape_string.hpp \ + export.hpp \ + extensions.hpp \ + file.hpp \ + file_pool.hpp \ + file_storage.hpp \ + fingerprint.hpp \ + gzip.hpp \ + hasher.hpp \ + http_connection.hpp \ + http_parser.hpp \ + http_seed_connection.hpp \ + http_stream.hpp \ + http_tracker_connection.hpp \ + i2p_stream.hpp \ + identify_client.hpp \ + instantiate_connection.hpp \ + intrusive_ptr_base.hpp \ + invariant_check.hpp \ + io.hpp \ + io_service.hpp \ + io_service_fwd.hpp \ + ip_filter.hpp \ + ip_voter.hpp \ + lazy_entry.hpp \ + lsd.hpp \ + magnet_uri.hpp \ + max.hpp \ + natpmp.hpp \ + packet_buffer.hpp \ + parse_url.hpp \ + pe_crypto.hpp \ + peer_connection.hpp \ + peer.hpp \ + peer_id.hpp \ + peer_info.hpp \ + peer_request.hpp \ + piece_block_progress.hpp \ + piece_picker.hpp \ + policy.hpp \ + proxy_base.hpp \ + ptime.hpp \ + puff.hpp \ + random.hpp \ + rss.hpp \ + session.hpp \ + session_settings.hpp \ + session_status.hpp \ + settings.hpp \ + sha1_hash.hpp \ + size_type.hpp \ + sliding_average.hpp \ + socket.hpp \ + socket_io.hpp \ + socket_type.hpp \ + socket_type_fwd.hpp \ + socks5_stream.hpp \ + ssl_stream.hpp \ + stat.hpp \ + storage.hpp \ + storage_defs.hpp \ + string_util.hpp \ + thread.hpp \ + time.hpp \ + timestamp_history.hpp \ + torrent_handle.hpp \ + torrent.hpp \ + torrent_info.hpp \ + tracker_manager.hpp \ + udp_socket.hpp \ + udp_tracker_connection.hpp \ + union_endpoint.hpp \ + upnp.hpp \ + utp_socket_manager.hpp \ + utp_stream.hpp \ + utf8.hpp \ + version.hpp \ + web_connection_base.hpp \ + web_peer_connection.hpp \ + xml_parse.hpp \ + \ + $(GEOIP_H) \ + tommath.h \ + tommath_class.h \ + tommath_superclass.h \ + \ + aux_/session_impl.hpp \ + \ + extensions/logger.hpp \ + extensions/lt_trackers.hpp \ + extensions/metadata_transfer.hpp \ + extensions/smart_ban.hpp \ + extensions/ut_metadata.hpp \ + extensions/ut_pex.hpp \ + \ + kademlia/dht_tracker.hpp \ + kademlia/dht_observer.hpp \ + kademlia/find_data.hpp \ + kademlia/logging.hpp \ + kademlia/msg.hpp \ + kademlia/node.hpp \ + kademlia/node_entry.hpp \ + kademlia/node_id.hpp \ + kademlia/observer.hpp \ + kademlia/refresh.hpp \ + kademlia/routing_table.hpp \ + kademlia/rpc_manager.hpp \ + kademlia/traversal_algorithm.hpp \ + kademlia/item.hpp \ + kademlia/get_item.hpp \ + kademlia/get_peers.hpp + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/libtorrent/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/libtorrent/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp new file mode 100644 index 0000000000..f7fa71eeee --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/add_torrent_params.hpp @@ -0,0 +1,402 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ADD_TORRENT_PARAMS_HPP_INCLUDED +#define TORRENT_ADD_TORRENT_PARAMS_HPP_INCLUDED + +#include +#include +#include + +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/peer_id.hpp" // sha1_hash +#include "libtorrent/version.hpp" + +#ifndef TORRENT_DISABLE_EXTENSIONS +#include "libtorrent/extensions.hpp" +#endif + +namespace libtorrent +{ + class torrent_info; + class torrent; + struct torrent_plugin; + + // The add_torrent_params is a parameter pack for adding torrents to a + // session. The key fields when adding a torrent are: + // + // * ti - when you have a .torrent file + // * url - when you have a magnet link or http URL to the .torrent file + // * info_hash - when all you have is an info-hash (this is similar to a + // magnet link) + // + // one of those fields need to be set. Another mandatory field is + // ``save_path``. The add_torrent_params object is passed into one of the + // ``session::add_torrent()`` overloads or ``session::async_add_torrent()``. + // + // If you only specify the info-hash, the torrent file will be downloaded + // from peers, which requires them to support the metadata extension. For + // the metadata extension to work, libtorrent must be built with extensions + // enabled (``TORRENT_DISABLE_EXTENSIONS`` must not be defined). It also + // takes an optional ``name`` argument. This may be left empty in case no + // name should be assigned to the torrent. In case it's not, the name is + // used for the torrent as long as it doesn't have metadata. See + // ``torrent_handle::name``. + // + struct TORRENT_EXPORT add_torrent_params + { + // The constructor can be used to initialize the storage constructor, + // which determines the storage mechanism for the downloaded or seeding + // data for the torrent. For more information, see the ``storage`` field. + add_torrent_params(storage_constructor_type sc = default_storage_constructor) + : version(LIBTORRENT_VERSION_NUM) +#ifndef TORRENT_NO_DEPRECATE + , tracker_url(0) +#endif + , storage_mode(storage_mode_sparse) + , storage(sc) + , userdata(0) +#ifndef TORRENT_NO_DEPRECATE + , flags(flag_ignore_flags | default_flags) +#else + , flags(default_flags) +#endif + , max_uploads(-1) + , max_connections(-1) + , upload_limit(-1) + , download_limit(-1) +#ifndef TORRENT_NO_DEPRECATE + , seed_mode(false) + , override_resume_data(false) + , upload_mode(false) + , share_mode(false) + , apply_ip_filter(true) + , paused(true) + , auto_managed(true) + , duplicate_is_error(false) + , merge_resume_trackers(false) +#endif + { + } + +#ifndef TORRENT_NO_DEPRECATE + void update_flags() const + { + if (flags != (flag_ignore_flags | default_flags)) return; + + boost::uint64_t& f = const_cast(flags); + f = flag_update_subscribe; + if (seed_mode) f |= flag_seed_mode; + if (override_resume_data) f |= flag_override_resume_data; + if (upload_mode) f |= flag_upload_mode; + if (share_mode) f |= flag_share_mode; + if (apply_ip_filter) f |= flag_apply_ip_filter; + if (paused) f |= flag_paused; + if (auto_managed) f |= flag_auto_managed; + if (duplicate_is_error) f |= flag_duplicate_is_error; + if (merge_resume_trackers) f |= flag_merge_resume_trackers; + } +#endif + + // values for the ``flags`` field + enum flags_t + { + // If ``flag_seed_mode`` is set, libtorrent will assume that all files + // are present for this torrent and that they all match the hashes in + // the torrent file. Each time a peer requests to download a block, + // the piece is verified against the hash, unless it has been verified + // already. If a hash fails, the torrent will automatically leave the + // seed mode and recheck all the files. The use case for this mode is + // if a torrent is created and seeded, or if the user already know + // that the files are complete, this is a way to avoid the initial + // file checks, and significantly reduce the startup time. + // + // Setting ``flag_seed_mode`` on a torrent without metadata (a + // .torrent file) is a no-op and will be ignored. + // + // If resume data is passed in with this torrent, the seed mode saved + // in there will override the seed mode you set here. + flag_seed_mode = 0x001, + + // If ``flag_override_resume_data`` is set, flags set for this torrent + // in this ``add_torrent_params`` object will take precedence over + // whatever states are saved in the resume data. For instance, the + // ``paused``, ``auto_managed``, ``sequential_download``, ``seed_mode``, + // ``super_seeding``, ``max_uploads``, ``max_connections``, + // ``upload_limit`` and ``download_limit`` are all affected by this + // flag. The intention of this flag is to have any field in + // add_torrent_params configuring the torrent override the corresponding + // configuration from the resume file, with the one exception of save + // resume data, which has its own flag (for historic reasons). + flag_override_resume_data = 0x002, + + // If ``flag_upload_mode`` is set, the torrent will be initialized in + // upload-mode, which means it will not make any piece requests. This + // state is typically entered on disk I/O errors, and if the torrent + // is also auto managed, it will be taken out of this state + // periodically. This mode can be used to avoid race conditions when + // adjusting priorities of pieces before allowing the torrent to start + // downloading. + // + // If the torrent is auto-managed (``flag_auto_managed``), the torrent + // will eventually be taken out of upload-mode, regardless of how it + // got there. If it's important to manually control when the torrent + // leaves upload mode, don't make it auto managed. + flag_upload_mode = 0x004, + + // determines if the torrent should be added in *share mode* or not. + // Share mode indicates that we are not interested in downloading the + // torrent, but merley want to improve our share ratio (i.e. increase + // it). A torrent started in share mode will do its best to never + // download more than it uploads to the swarm. If the swarm does not + // have enough demand for upload capacity, the torrent will not + // download anything. This mode is intended to be safe to add any + // number of torrents to, without manual screening, without the risk + // of downloading more than is uploaded. + // + // A torrent in share mode sets the priority to all pieces to 0, + // except for the pieces that are downloaded, when pieces are decided + // to be downloaded. This affects the progress bar, which might be set + // to "100% finished" most of the time. Do not change file or piece + // priorities for torrents in share mode, it will make it not work. + // + // The share mode has one setting, the share ratio target, see + // ``session_settings::share_mode_target`` for more info. + flag_share_mode = 0x008, + + // determines if the IP filter should apply to this torrent or not. By + // default all torrents are subject to filtering by the IP filter + // (i.e. this flag is set by default). This is useful if certain + // torrents needs to be excempt for some reason, being an auto-update + // torrent for instance. + flag_apply_ip_filter = 0x010, + + // specifies whether or not the torrent is to be started in a paused + // state. I.e. it won't connect to the tracker or any of the peers + // until it's resumed. This is typically a good way of avoiding race + // conditions when setting configuration options on torrents before + // starting them. + flag_paused = 0x020, + + // If the torrent is auto-managed (``flag_auto_managed``), the torrent + // may be resumed at any point, regardless of how it paused. If it's + // important to manually control when the torrent is paused and + // resumed, don't make it auto managed. + // + // If ``flag_auto_managed`` is set, the torrent will be queued, + // started and seeded automatically by libtorrent. When this is set, + // the torrent should also be started as paused. The default queue + // order is the order the torrents were added. They are all downloaded + // in that order. For more details, see queuing_. + // + // If you pass in resume data, the auto_managed state of the torrent + // when the resume data was saved will override the auto_managed state + // you pass in here. You can override this by setting + // ``override_resume_data``. + flag_auto_managed = 0x040, + flag_duplicate_is_error = 0x080, + + // defaults to off and specifies whether tracker URLs loaded from + // resume data should be added to the trackers in the torrent or + // replace the trackers. + flag_merge_resume_trackers = 0x100, + + // on by default and means that this torrent will be part of state + // updates when calling post_torrent_updates(). + flag_update_subscribe = 0x200, + + // sets the torrent into super seeding mode. If the torrent is not a + // seed, this flag has no effect. It has the same effect as calling + // ``torrent_handle::super_seeding(true)`` on the torrent handle + // immediately after adding it. + flag_super_seeding = 0x400, + + // sets the sequential download state for the torrent. It has the same + // effect as calling ``torrent_handle::sequential_download(true)`` on + // the torrent handle immediately after adding it. + flag_sequential_download = 0x800, + + // if this flag is set, the save path from the resume data file, if + // present, is honored. This defaults to not being set, in which + // case the save_path specified in add_torrent_params is always used. + flag_use_resume_save_path = 0x1000, + + // internal + default_flags = flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter +#ifndef TORRENT_NO_DEPRECATE + , flag_ignore_flags = 0x80000000 +#endif + }; + + // filled in by the constructor and should be left untouched. It + // is used for forward binary compatibility. + int version; + + // torrent_info object with the torrent to add. Unless the url or + // info_hash is set, this is required to be initiazlied. + boost::intrusive_ptr ti; + +#ifndef TORRENT_NO_DEPRECATE + char const* tracker_url; +#endif + // If the torrent doesn't have a tracker, but relies on the DHT to find + // peers, the ``trackers`` can specify tracker URLs for the torrent. + std::vector trackers; + + // url seeds to be added to the torrent (`BEP 17`_). + std::vector url_seeds; + + // a list of hostname and port pairs, representing DHT nodes to be added + // to the session (if DHT is enabled). The hostname may be an IP address. + std::vector > dht_nodes; + std::string name; + + // the path where the torrent is or will be stored. Note that this may + // alos be stored in resume data. If you want the save path saved in + // the resume data to be used, you need to set the + // flag_use_resume_save_path flag. + // + // .. note:: + // On windows this path (and other paths) are interpreted as UNC + // paths. This means they must use backslashes as directory separators + // and may not contain the special directories "." or "..". + std::string save_path; + + // The optional parameter, ``resume_data`` can be given if up to date + // fast-resume data is available. The fast-resume data can be acquired + // from a running torrent by calling save_resume_data() on + // torrent_handle. See fast-resume_. The ``vector`` that is passed in + // will be swapped into the running torrent instance with + // ``std::vector::swap()``. + std::vector resume_data; + + // One of the values from storage_mode_t. For more information, see + // storage-allocation_. + storage_mode_t storage_mode; + + // can be used to customize how the data is stored. The default storage + // will simply write the data to the files it belongs to, but it could be + // overridden to save everything to a single file at a specific location + // or encrypt the content on disk for instance. For more information + // about the storage_interface that needs to be implemented for a custom + // storage, see storage_interface. + storage_constructor_type storage; + + // The ``userdata`` parameter is optional and will be passed on to the + // extension constructor functions, if any (see `add_extension()`_). + void* userdata; + + // can be set to control the initial file priorities when adding a + // torrent. The semantics are the same as for + // ``torrent_handle::prioritize_files()``. + std::vector file_priorities; + + // torrent extension construction functions can be added to this vector + // to have them be added immediately when the torrent is constructed. + // This may be desired over the torrent_handle::add_extension() in order + // to avoid race conditions. For instance it may be important to have the + // plugin catch events that happen very early on after the torrent is + // created. + std::vector(torrent*, void*)> > + extensions; + + // the default tracker id to be used when announcing to trackers. By + // default this is empty, and no tracker ID is used, since this is an + // optional argument. If a tracker returns a tracker ID, that ID is used + // instead of this. + std::string trackerid; + + // If you specify a ``url``, the torrent will be set in + // ``downloading_metadata`` state until the .torrent file has been + // downloaded. If there's any error while downloading, the torrent will + // be stopped and the torrent error state (``torrent_status::error``) + // will indicate what went wrong. The ``url`` may refer to a magnet link + // or a regular http URL. + // + // If it refers to an HTTP URL, the info-hash for the added torrent will + // not be the true info-hash of the .torrent. Instead a placeholder, + // unique, info-hash is used which is later updated once the .torrent + // file has been downloaded. + // + // Once the info-hash change happens, a torrent_update_alert is posted. + std::string url; + + // if ``uuid`` is specified, it is used to find duplicates. If another + // torrent is already running with the same UUID as the one being added, + // it will be considered a duplicate. This is mainly useful for RSS feed + // items which has UUIDs specified. + std::string uuid; + + // should point to the URL of the RSS feed this torrent comes from, + // if it comes from an RSS feed. + std::string source_feed_url; + + // flags controlling aspects of this torrent and how it's added. See + // flags_t for details. + boost::uint64_t flags; + + // set this to the info hash of the torrent to add in case the info-hash + // is the only known property of the torrent. i.e. you don't have a + // .torrent file nor a magnet link. + sha1_hash info_hash; + + // ``max_uploads``, ``max_connections``, ``upload_limit``, + // ``download_limit`` correspond to the ``set_max_uploads()``, + // ``set_max_connections()``, ``set_upload_limit()`` and + // ``set_download_limit()`` functions on torrent_handle. These values let + // you initialize these settings when the torrent is added, instead of + // calling these functions immediately following adding it. + // + // -1 means unlimited on these settings just like their counterpart + // functions on torrent_handle + int max_uploads; + int max_connections; + int upload_limit; + int download_limit; + +#ifndef TORRENT_NO_DEPRECATE + bool seed_mode; + bool override_resume_data; + bool upload_mode; + bool share_mode; + bool apply_ip_filter; + bool paused; + bool auto_managed; + bool duplicate_is_error; + bool merge_resume_trackers; +#endif + + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp new file mode 100644 index 0000000000..1a7aa4cc32 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/address.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ADDRESS_HPP_INCLUDED +#define TORRENT_ADDRESS_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::ip::address address; + typedef ::asio::ip::address_v4 address_v4; +#if TORRENT_USE_IPV6 + typedef ::asio::ip::address_v6 address_v6; +#endif +#else + typedef boost::asio::ip::address address; + typedef boost::asio::ip::address_v4 address_v4; +#if TORRENT_USE_IPV6 + typedef boost::asio::ip::address_v6 address_v6; +#endif +#endif +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp new file mode 100644 index 0000000000..6d50f031c3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert.hpp @@ -0,0 +1,319 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_HPP_INCLUDED +#define TORRENT_ALERT_HPP_INCLUDED + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +// OVERVIEW +// +// The pop_alerts() function on session is the main interface for retrieving +// alerts (warnings, messages and errors from libtorrent). If no alerts have +// been posted by libtorrent pop_alert() will return an empty list. +// +// By default, only errors are reported. set_alert_mask() can be used to +// specify which kinds of events should be reported. The alert mask is +// comprised by bits from the category_t enum. +// +// Every alert belongs to one or more category. There is a small cost involved +// in posting alerts. Only alerts that belong to an enabled category are +// posted. Setting the alert bitmask to 0 will disable all alerts (except those +// that are non-discardable). +// +// There are other alert base classes that some alerts derive from, all the +// alerts that are generated for a specific torrent are derived from +// torrent_alert, and tracker events derive from tracker_alert. +// + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/ptime.hpp" +#include "libtorrent/config.hpp" + +#ifndef TORRENT_NO_DEPRECATE +#ifndef BOOST_NO_TYPEID +#include +#endif +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_MAX_ALERT_TYPES +#define TORRENT_MAX_ALERT_TYPES 15 +#endif + +namespace libtorrent { + + // The ``alert`` class is the base class that specific messages are derived from. + class TORRENT_EXPORT alert + { + public: + +#ifndef TORRENT_NO_DEPRECATE + // only here for backwards compatibility + enum severity_t { debug, info, warning, critical, fatal, none }; +#endif + + // these are bits for the alert_mask used by the session. See set_alert_mask(). + enum category_t + { + // Enables alerts that report an error. This includes: + // + // * tracker errors + // * tracker warnings + // * file errors + // * resume data failures + // * web seed errors + // * .torrent files errors + // * listen socket errors + // * port mapping errors + error_notification = 0x1, + + // Enables alerts when peers send invalid requests, get banned or + // snubbed. + peer_notification = 0x2, + + // Enables alerts for port mapping events. For NAT-PMP and UPnP. + port_mapping_notification = 0x4, + + // Enables alerts for events related to the storage. File errors and + // synchronization events for moving the storage, renaming files etc. + storage_notification = 0x8, + + // Enables all tracker events. Includes announcing to trackers, + // receiving responses, warnings and errors. + tracker_notification = 0x10, + + // Low level alerts for when peers are connected and disconnected. + debug_notification = 0x20, + + // Enables alerts for when a torrent or the session changes state. + status_notification = 0x40, + + // Alerts for when blocks are requested and completed. Also when + // pieces are completed. + progress_notification = 0x80, + + // Alerts when a peer is blocked by the ip blocker or port blocker. + ip_block_notification = 0x100, + + // Alerts when some limit is reached that might limit the download + // or upload rate. + performance_warning = 0x200, + + // Alerts on events in the DHT node. For incoming searches or + // bootstrapping being done etc. + dht_notification = 0x400, + + // If you enable these alerts, you will receive a stats_alert + // approximately once every second, for every active torrent. + // These alerts contain all statistics counters for the interval since + // the lasts stats alert. + stats_notification = 0x800, + + // Alerts on RSS related events, like feeds being updated, feed error + // conditions and successful RSS feed updates. Enabling this categoty + // will make you receive rss_alert alerts. + rss_notification = 0x1000, + + // The full bitmask, representing all available categories. + // + // since the enum is signed, make sure this isn't + // interpreted as -1. For instance, boost.python + // does that and fails when assigning it to an + // unsigned parameter. + all_categories = 0x7fffffff + }; + + // hidden + alert(); + // hidden + virtual ~alert(); + + // a timestamp is automatically created in the constructor + ptime timestamp() const; + + // returns an integer that is unique to this alert type. It can be + // compared against a specific alert by querying a static constant called ``alert_type`` + // in the alert. It can be used to determine the run-time type of an alert* in + // order to cast to that alert type and access specific members. + // + // e.g: + // + // .. code:: c++ + // + // std::auto_ptr a = ses.pop_alert(); + // switch (a->type()) + // { + // case read_piece_alert::alert_type: + // { + // read_piece_alert* p = (read_piece_alert*)a.get(); + // if (p->ec) { + // // read_piece failed + // break; + // } + // // use p + // break; + // } + // case file_renamed_alert::alert_type: + // { + // // etc... + // } + // } + virtual int type() const = 0; + + // returns a string literal describing the type of the alert. It does + // not include any information that might be bundled with the alert. + virtual char const* what() const = 0; + + // generate a string describing the alert and the information bundled + // with it. This is mainly intended for debug and development use. It is not suitable + // to use this for applications that may be localized. Instead, handle each alert + // type individually and extract and render the information from the alert depending + // on the locale. + virtual std::string message() const = 0; + + // returns a bitmask specifying which categories this alert belong to. + virtual int category() const = 0; + + // determines whether or not an alert is allowed to be discarded + // when the alert queue is full. There are a few alerts which may not be discared, + // since they would break the user contract, such as save_resume_data_alert. + virtual bool discardable() const { return true; } + +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + severity_t severity() const TORRENT_DEPRECATED { return warning; } +#endif + + // returns a pointer to a copy of the alert. + virtual std::auto_ptr clone() const = 0; + + private: + ptime m_timestamp; + }; + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + struct TORRENT_EXPORT unhandled_alert : std::exception + { + unhandled_alert() {} + }; + +#ifndef BOOST_NO_TYPEID + + namespace detail { + + struct void_; + + template + void handle_alert_dispatch( + const std::auto_ptr& alert_, const Handler& handler + , const std::type_info& typeid_ + , T0*, BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p)) + { + if (typeid_ == typeid(T0)) + handler(*static_cast(alert_.get())); + else + handle_alert_dispatch(alert_, handler, typeid_ + , BOOST_PP_ENUM_SHIFTED_PARAMS( + TORRENT_MAX_ALERT_TYPES, p), (void_*)0); + } + + template + void handle_alert_dispatch( + const std::auto_ptr& + , const Handler& + , const std::type_info& + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) + { + throw unhandled_alert(); + } + + } // namespace detail + + template + struct TORRENT_EXPORT handle_alert + { + template + handle_alert(const std::auto_ptr& alert_ + , const Handler& handler) + { + #define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0 + + detail::handle_alert_dispatch(alert_, handler, typeid(*alert_) + , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)); + + #undef ALERT_POINTER_TYPE + } + }; + +#endif // BOOST_NO_TYPEID +#endif // TORRENT_NO_DEPRECATE +#endif // BOOST_NO_EXCEPTIONS + +// When you get an alert, you can use ``alert_cast<>`` to attempt to cast the pointer to a +// more specific alert type, in order to query it for more information. +template +T* alert_cast(alert* a) +{ + if (a == 0) return 0; + if (a->type() == T::alert_type) return static_cast(a); + return 0; +} +template +T const* alert_cast(alert const* a) +{ + if (a == 0) return 0; + if (a->type() == T::alert_type) return static_cast(a); + return 0; +} + +} // namespace libtorrent + +#endif // TORRENT_ALERT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp new file mode 100644 index 0000000000..62b7321733 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_dispatcher.hpp @@ -0,0 +1,50 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_DISPATCHER_HPP_INCLUDED +#define TORRENT_ALERT_DISPATCHER_HPP_INCLUDED + +namespace libtorrent +{ + class alert; + + struct alert_dispatcher + { + // return true if the alert was swallowed (i.e. + // ownership was taken over). In this case, the + // alert will not be passed on to any one else + virtual bool post_alert(alert* a) = 0; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp new file mode 100644 index 0000000000..798e555bbf --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_manager.hpp @@ -0,0 +1,114 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_MANAGER_HPP_INCLUDED +#define TORRENT_ALERT_MANAGER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/thread.hpp" + +#include +#include +#include + +namespace libtorrent { + +#ifndef TORRENT_DISABLE_EXTENSIONS + struct plugin; +#endif + + class TORRENT_EXTRA_EXPORT alert_manager + { + public: + alert_manager(int queue_limit + , boost::uint32_t alert_mask = alert::error_notification); + ~alert_manager(); + + void post_alert(const alert& alert_); + void post_alert_ptr(alert* alert_); + bool pending() const; + std::auto_ptr get(); + void get_all(std::deque* alerts); + + template + bool should_post() const + { + mutex::scoped_lock lock(m_mutex); + if (m_alerts.size() >= m_queue_size_limit) return false; + return (m_alert_mask & T::static_category) != 0; + } + + bool should_post(alert const* a) const + { + return (m_alert_mask & a->category()) != 0; + } + + alert const* wait_for_alert(time_duration max_wait); + + void set_alert_mask(boost::uint32_t m) + { + mutex::scoped_lock lock(m_mutex); + m_alert_mask = m; + } + + int alert_mask() const { return m_alert_mask; } + + size_t alert_queue_size_limit() const { return m_queue_size_limit; } + size_t set_alert_queue_size_limit(size_t queue_size_limit_); + + void set_dispatch_function(boost::function)> const&); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr ext); +#endif + + private: + void post_impl(std::auto_ptr& alert_, mutex::scoped_lock& l); + + std::deque m_alerts; + mutable mutex m_mutex; + condition_variable m_condition; + boost::uint32_t m_alert_mask; + size_t m_queue_size_limit; + boost::function)> m_dispatch; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > ses_extension_list_t; + ses_extension_list_t m_ses_extensions; +#endif + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp new file mode 100644 index 0000000000..7cba5ce991 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alert_types.hpp @@ -0,0 +1,1759 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED +#define TORRENT_ALERT_TYPES_HPP_INCLUDED + +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/rss.hpp" // for feed_handle + +// lines reserved for future includes +// the type-ids of the alert types +// are derived from the line on which +// they are declared + + + + +namespace libtorrent +{ + + // user defined alerts should use IDs greater than this + const static int user_alert_id = 10000; + + // This is a base class for alerts that are associated with a + // specific torrent. It contains a handle to the torrent. + struct TORRENT_EXPORT torrent_alert: alert + { + // internal + torrent_alert(torrent_handle const& h); + + // internal + const static int alert_type = 1; + virtual std::string message() const; + + // The torrent_handle pointing to the torrent this + // alert is associated with. + torrent_handle handle; + }; + + // The peer alert is a base class for alerts that refer to a specific peer. It includes all + // the information to identify the peer. i.e. ``ip`` and ``peer-id``. + struct TORRENT_EXPORT peer_alert: torrent_alert + { + // internal + peer_alert(torrent_handle const& h, tcp::endpoint const& i + , peer_id const& pi); + + const static int alert_type = 2; + const static int static_category = alert::peer_notification; + virtual int category() const { return static_category; } + virtual std::string message() const; + + // The peer's IP address and port. + tcp::endpoint ip; + + // the peer ID, if known. + peer_id pid; + }; + + // This is a base class used for alerts that are associated with a + // specific tracker. It derives from torrent_alert since a tracker + // is also associated with a specific torrent. + struct TORRENT_EXPORT tracker_alert: torrent_alert + { + // internal + tracker_alert(torrent_handle const& h + , std::string const& u); + + const static int alert_type = 3; + const static int static_category = alert::tracker_notification; + virtual int category() const { return static_category; } + virtual std::string message() const; + + // The tracker URL + std::string url; + }; + +#define TORRENT_DEFINE_ALERT(name) \ + const static int alert_type = __LINE__; \ + virtual int type() const { return alert_type; } \ + virtual std::auto_ptr clone() const \ + { return std::auto_ptr(new name(*this)); } \ + virtual int category() const { return static_category; } \ + virtual char const* what() const { return #name; } + + // The ``torrent_added_alert`` is posted once every time a torrent is successfully + // added. It doesn't contain any members of its own, but inherits the torrent handle + // from its base class. + // It's posted when the ``status_notification`` bit is set in the alert_mask. + struct TORRENT_EXPORT torrent_added_alert: torrent_alert + { + // internal + torrent_added_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_added_alert); + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // The ``torrent_removed_alert`` is posted whenever a torrent is removed. Since + // the torrent handle in its baseclass will always be invalid (since the torrent + // is already removed) it has the info hash as a member, to identify it. + // It's posted when the ``status_notification`` bit is set in the alert_mask. + // + // Even though the ``handle`` member doesn't point to an existing torrent anymore, + // it is still useful for comparing to other handles, which may also no + // longer point to existing torrents, but to the same non-existing torrents. + // + // The ``torrent_handle`` acts as a ``weak_ptr``, even though its object no + // longer exists, it can still compare equal to another weak pointer which + // points to the same non-existent object. + struct TORRENT_EXPORT torrent_removed_alert: torrent_alert + { + // internal + torrent_removed_alert(torrent_handle const& h, sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_removed_alert); + const static int static_category = alert::status_notification; + virtual std::string message() const; + sha1_hash info_hash; + }; + + // This alert is posted when the asynchronous read operation initiated by + // a call to torrent_handle::read_piece() is completed. If the read failed, the torrent + // is paused and an error state is set and the buffer member of the alert + // is 0. If successful, ``buffer`` points to a buffer containing all the data + // of the piece. ``piece`` is the piece index that was read. ``size`` is the + // number of bytes that was read. + // + // If the operation fails, ec will indicat what went wrong. + struct TORRENT_EXPORT read_piece_alert: torrent_alert + { + // internal + read_piece_alert(torrent_handle const& h + , int p, boost::shared_array d, int s); + read_piece_alert(torrent_handle h, int p, error_code e); + + TORRENT_DEFINE_ALERT(read_piece_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code ec; + boost::shared_array buffer; + int piece; + int size; + }; + + // This is posted whenever an individual file completes its download. i.e. + // All pieces overlapping this file have passed their hash check. + struct TORRENT_EXPORT file_completed_alert: torrent_alert + { + // internal + file_completed_alert(torrent_handle const& h + , int idx); + + TORRENT_DEFINE_ALERT(file_completed_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + // refers to the index of the file that completed. + int index; + }; + + // This is posted as a response to a torrent_handle::rename_file() call, if the rename + // operation succeeds. + struct TORRENT_EXPORT file_renamed_alert: torrent_alert + { + // internal + file_renamed_alert(torrent_handle const& h + , std::string const& n + , int idx); + + TORRENT_DEFINE_ALERT(file_renamed_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + std::string name; + + // refers to the index of the file that was renamed, + // ``name`` is the new name of the file. + int index; + }; + + // This is posted as a response to a torrent_handle::rename_file() call, if the rename + // operation failed. + struct TORRENT_EXPORT file_rename_failed_alert: torrent_alert + { + // internal + file_rename_failed_alert(torrent_handle const& h + , int idx + , error_code ec); + + TORRENT_DEFINE_ALERT(file_rename_failed_alert); + + const static int static_category = alert::storage_notification; + + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // refers to the index of the file that was supposed to be renamed, + // ``error`` is the error code returned from the filesystem. + int index; + error_code error; + }; + + // This alert is generated when a limit is reached that might have a negative impact on + // upload or download rate performance. + struct TORRENT_EXPORT performance_alert: torrent_alert + { + enum performance_warning_t + { + + // This warning means that the number of bytes queued to be written to disk + // exceeds the max disk byte queue setting (``session_settings::max_queued_disk_bytes``). + // This might restrict the download rate, by not queuing up enough write jobs + // to the disk I/O thread. When this alert is posted, peer connections are + // temporarily stopped from downloading, until the queued disk bytes have fallen + // below the limit again. Unless your ``max_queued_disk_bytes`` setting is already + // high, you might want to increase it to get better performance. + outstanding_disk_buffer_limit_reached, + + // This is posted when libtorrent would like to send more requests to a peer, + // but it's limited by ``session_settings::max_out_request_queue``. The queue length + // libtorrent is trying to achieve is determined by the download rate and the + // assumed round-trip-time (``session_settings::request_queue_time``). The assumed + // rount-trip-time is not limited to just the network RTT, but also the remote disk + // access time and message handling time. It defaults to 3 seconds. The target number + // of outstanding requests is set to fill the bandwidth-delay product (assumed RTT + // times download rate divided by number of bytes per request). When this alert + // is posted, there is a risk that the number of outstanding requests is too low + // and limits the download rate. You might want to increase the ``max_out_request_queue`` + // setting. + outstanding_request_limit_reached, + + // This warning is posted when the amount of TCP/IP overhead is greater than the + // upload rate limit. When this happens, the TCP/IP overhead is caused by a much + // faster download rate, triggering TCP ACK packets. These packets eat into the + // rate limit specified to libtorrent. When the overhead traffic is greater than + // the rate limit, libtorrent will not be able to send any actual payload, such + // as piece requests. This means the download rate will suffer, and new requests + // can be sent again. There will be an equilibrium where the download rate, on + // average, is about 20 times the upload rate limit. If you want to maximize the + // download rate, increase the upload rate limit above 5% of your download capacity. + upload_limit_too_low, + + // This is the same warning as ``upload_limit_too_low`` but referring to the download + // limit instead of upload. This suggests that your download rate limit is mcuh lower + // than your upload capacity. Your upload rate will suffer. To maximize upload rate, + // make sure your download rate limit is above 5% of your upload capacity. + download_limit_too_low, + + // We're stalled on the disk. We want to write to the socket, and we can write + // but our send buffer is empty, waiting to be refilled from the disk. + // This either means the disk is slower than the network connection + // or that our send buffer watermark is too small, because we can + // send it all before the disk gets back to us. + // The number of bytes that we keep outstanding, requested from the disk, is calculated + // as follows:: + // + // min(512, max(upload_rate * send_buffer_watermark_factor / 100, send_buffer_watermark)) + // + // If you receive this alert, you migth want to either increase your ``send_buffer_watermark`` + // or ``send_buffer_watermark_factor``. + send_buffer_watermark_too_low, + + // If the half (or more) of all upload slots are set as optimistic unchoke slots, this + // warning is issued. You probably want more regular (rate based) unchoke slots. + too_many_optimistic_unchoke_slots, + + // If the disk write queue ever grows larger than half of the cache size, this warning + // is posted. The disk write queue eats into the total disk cache and leaves very little + // left for the actual cache. This causes the disk cache to oscillate in evicting large + // portions of the cache before allowing peers to download any more, onto the disk write + // queue. Either lower ``max_queued_disk_bytes`` or increase ``cache_size``. + too_high_disk_queue_limit, + + bittyrant_with_no_uplimit, + + // This is generated if outgoing peer connections are failing because of *address in use* + // errors, indicating that ``session_settings::outgoing_ports`` is set and is too small of + // a range. Consider not using the ``outgoing_ports`` setting at all, or widen the range to + // include more ports. + too_few_outgoing_ports, + + too_few_file_descriptors, + + num_warnings + }; + + // internal + performance_alert(torrent_handle const& h + , performance_warning_t w); + + TORRENT_DEFINE_ALERT(performance_alert); + + const static int static_category = alert::performance_warning; + + virtual std::string message() const; + + performance_warning_t warning_code; + }; + + // Generated whenever a torrent changes its state. + struct TORRENT_EXPORT state_changed_alert: torrent_alert + { + // internal + state_changed_alert(torrent_handle const& h + , torrent_status::state_t st + , torrent_status::state_t prev_st); + + TORRENT_DEFINE_ALERT(state_changed_alert); + + const static int static_category = alert::status_notification; + + virtual std::string message() const; + + // the new state of the torrent. + torrent_status::state_t state; + + // the previous state. + torrent_status::state_t prev_state; + }; + + // This alert is generated on tracker time outs, premature disconnects, + // invalid response or a HTTP response other than "200 OK". From the alert + // you can get the handle to the torrent the tracker belongs to. + // + // The ``times_in_row`` member says how many times in a row this tracker has + // failed. ``status_code`` is the code returned from the HTTP server. 401 + // means the tracker needs authentication, 404 means not found etc. If the + // tracker timed out, the code will be set to 0. + struct TORRENT_EXPORT tracker_error_alert: tracker_alert + { + // internal + tracker_error_alert(torrent_handle const& h + , int times + , int status + , std::string const& u + , error_code const& e + , std::string const& m); + + TORRENT_DEFINE_ALERT(tracker_error_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + int times_in_row; + int status_code; + error_code error; + std::string msg; + }; + + // This alert is triggered if the tracker reply contains a warning field. + // Usually this means that the tracker announce was successful, but the + // tracker has a message to the client. + struct TORRENT_EXPORT tracker_warning_alert: tracker_alert + { + // internal + tracker_warning_alert(torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(tracker_warning_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + // contains the warning message from the tracker. + std::string msg; + }; + + // This alert is generated when a scrape request succeeds. + struct TORRENT_EXPORT scrape_reply_alert: tracker_alert + { + // internal + scrape_reply_alert(torrent_handle const& h + , int incomp + , int comp + , std::string const& u); + + TORRENT_DEFINE_ALERT(scrape_reply_alert); + + virtual std::string message() const; + + // the data returned in the scrape response. These numbers + // may be -1 if the reponse was malformed. + int incomplete; + int complete; + }; + + // If a scrape request fails, this alert is generated. This might be due + // to the tracker timing out, refusing connection or returning an http response + // code indicating an error. + struct TORRENT_EXPORT scrape_failed_alert: tracker_alert + { + // internal + scrape_failed_alert(torrent_handle const& h + , std::string const& u + , error_code const& e); + + scrape_failed_alert(torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(scrape_failed_alert); + + const static int static_category = alert::tracker_notification | alert::error_notification; + virtual std::string message() const; + + // contains a message describing the error. + std::string msg; + }; + + // This alert is only for informational purpose. It is generated when a tracker announce + // succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or + // the DHT. + struct TORRENT_EXPORT tracker_reply_alert: tracker_alert + { + // internal + tracker_reply_alert(torrent_handle const& h + , int np + , std::string const& u); + + TORRENT_DEFINE_ALERT(tracker_reply_alert); + + virtual std::string message() const; + + // tells how many peers the tracker returned in this response. This is + // not expected to be more thant the ``num_want`` settings. These are not necessarily + // all new peers, some of them may already be connected. + int num_peers; + }; + + // This alert is generated each time the DHT receives peers from a node. ``num_peers`` + // is the number of peers we received in this packet. Typically these packets are + // received from multiple DHT nodes, and so the alerts are typically generated + // a few at a time. + struct TORRENT_EXPORT dht_reply_alert: tracker_alert + { + // internal + dht_reply_alert(torrent_handle const& h + , int np); + + TORRENT_DEFINE_ALERT(dht_reply_alert); + + virtual std::string message() const; + + int num_peers; + }; + + // This alert is generated each time a tracker announce is sent (or attempted to be sent). + // There are no extra data members in this alert. The url can be found in the base class + // however. + struct TORRENT_EXPORT tracker_announce_alert: tracker_alert + { + // internal + tracker_announce_alert(torrent_handle const& h + , std::string const& u, int e); + + TORRENT_DEFINE_ALERT(tracker_announce_alert); + + virtual std::string message() const; + + // specifies what event was sent to the tracker. It is defined as: + // + // 0. None + // 1. Completed + // 2. Started + // 3. Stopped + int event; + }; + + // This alert is generated when a finished piece fails its hash check. You can get the handle + // to the torrent which got the failed piece and the index of the piece itself from the alert. + struct TORRENT_EXPORT hash_failed_alert: torrent_alert + { + // internal + hash_failed_alert( + torrent_handle const& h + , int index); + + TORRENT_DEFINE_ALERT(hash_failed_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + int piece_index; + }; + + // This alert is generated when a peer is banned because it has sent too many corrupt pieces + // to us. ``ip`` is the endpoint to the peer that was banned. + struct TORRENT_EXPORT peer_ban_alert: peer_alert + { + // internal + peer_ban_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_ban_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer is unsnubbed. Essentially when it was snubbed for stalling + // sending data, and now it started sending data again. + struct TORRENT_EXPORT peer_unsnubbed_alert: peer_alert + { + // internal + peer_unsnubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_unsnubbed_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer is snubbed, when it stops sending data when we request + // it. + struct TORRENT_EXPORT peer_snubbed_alert: peer_alert + { + // internal + peer_snubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id); + + TORRENT_DEFINE_ALERT(peer_snubbed_alert); + + virtual std::string message() const; + }; + + // This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer + // will be disconnected, but you get its ip address from the alert, to identify it. + struct TORRENT_EXPORT peer_error_alert: peer_alert + { + // internal + peer_error_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e); + + TORRENT_DEFINE_ALERT(peer_error_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + + // tells you what error caused this alert. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is posted every time an outgoing peer connect attempts succeeds. + struct TORRENT_EXPORT peer_connect_alert: peer_alert + { + // internal + peer_connect_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id, int type); + + TORRENT_DEFINE_ALERT(peer_connect_alert); + + const static int static_category = alert::debug_notification; + virtual std::string message() const; + + int socket_type; + }; + + // This alert is generated when a peer is disconnected for any reason (other than the ones + // covered by peer_error_alert ). + struct TORRENT_EXPORT peer_disconnected_alert: peer_alert + { + // internal + peer_disconnected_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e); + + TORRENT_DEFINE_ALERT(peer_disconnected_alert); + + const static int static_category = alert::debug_notification; + virtual std::string message() const; + + // tells you what error caused peer to disconnect. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This is a debug alert that is generated by an incoming invalid piece request. + // ``ip`` is the address of the peer and the ``request`` is the actual incoming + // request from the peer. See peer_request for more info. + struct TORRENT_EXPORT invalid_request_alert: peer_alert + { + // internal + invalid_request_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, peer_request const& r); + + TORRENT_DEFINE_ALERT(invalid_request_alert); + + virtual std::string message() const; + + peer_request request; + }; + + // This alert is generated when a torrent switches from being a downloader to a seed. + // It will only be generated once per torrent. It contains a torrent_handle to the + // torrent in question. + struct TORRENT_EXPORT torrent_finished_alert: torrent_alert + { + // internal + torrent_finished_alert(const torrent_handle& h); + + TORRENT_DEFINE_ALERT(torrent_finished_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // this alert is posted every time a piece completes downloading + // and passes the hash check. This alert derives from torrent_alert + // which contains the torrent_handle to the torrent the piece belongs to. + struct TORRENT_EXPORT piece_finished_alert: torrent_alert + { + // internal + piece_finished_alert( + const torrent_handle& h + , int piece_num); + + TORRENT_DEFINE_ALERT(piece_finished_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + // the index of the piece that finished + int piece_index; + }; + + // This alert is generated when a peer rejects or ignores a piece request. + struct TORRENT_EXPORT request_dropped_alert: peer_alert + { + // internal + request_dropped_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(request_dropped_alert); + + const static int static_category = alert::progress_notification + | alert::peer_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request times out. + struct TORRENT_EXPORT block_timeout_alert: peer_alert + { + // internal + block_timeout_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_timeout_alert); + + const static int static_category = alert::progress_notification + | alert::peer_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request receives a response. + struct TORRENT_EXPORT block_finished_alert: peer_alert + { + // internal + block_finished_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_finished_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // This alert is generated when a block request is sent to a peer. + struct TORRENT_EXPORT block_downloading_alert: peer_alert + { + // internal + block_downloading_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, char const* speedmsg, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(block_downloading_alert); + + const static int static_category = alert::progress_notification; + virtual std::string message() const; + + char const* peer_speedmsg; + int block_index; + int piece_index; + }; + + // This alert is generated when a block is received that was not requested or + // whose request timed out. + struct TORRENT_EXPORT unwanted_block_alert: peer_alert + { + // internal + unwanted_block_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num); + + TORRENT_DEFINE_ALERT(unwanted_block_alert); + + virtual std::string message() const; + + int block_index; + int piece_index; + }; + + // The ``storage_moved_alert`` is generated when all the disk IO has completed and the + // files have been moved, as an effect of a call to ``torrent_handle::move_storage``. This + // is useful to synchronize with the actual disk. The ``path`` member is the new path of + // the storage. + struct TORRENT_EXPORT storage_moved_alert: torrent_alert + { + // internal + storage_moved_alert(torrent_handle const& h, std::string const& p); + + TORRENT_DEFINE_ALERT(storage_moved_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + + std::string path; + }; + + // The ``storage_moved_failed_alert`` is generated when an attempt to move the storage, + // via torrent_handle::move_storage(), fails. + struct TORRENT_EXPORT storage_moved_failed_alert: torrent_alert + { + // internal + storage_moved_failed_alert(torrent_handle const& h, error_code const& e); + + TORRENT_DEFINE_ALERT(storage_moved_failed_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + + error_code error; + }; + + // This alert is generated when a request to delete the files of a torrent complete. + // + // The ``info_hash`` is the info-hash of the torrent that was just deleted. Most of + // the time the torrent_handle in the ``torrent_alert`` will be invalid by the time + // this alert arrives, since the torrent is being deleted. The ``info_hash`` member + // is hence the main way of identifying which torrent just completed the delete. + // + // This alert is posted in the ``storage_notification`` category, and that bit + // needs to be set in the alert_mask. + struct TORRENT_EXPORT torrent_deleted_alert: torrent_alert + { + // internal + torrent_deleted_alert(torrent_handle const& h, sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_deleted_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + sha1_hash info_hash; + }; + + // This alert is generated when a request to delete the files of a torrent fails. + // Just removing a torrent from the session cannot fail + struct TORRENT_EXPORT torrent_delete_failed_alert: torrent_alert + { + // internal + torrent_delete_failed_alert(torrent_handle const& h, error_code const& e + , sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(torrent_delete_failed_alert); + + const static int static_category = alert::storage_notification + | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // tells you why it failed. + error_code error; + + // the info hash of the torrent whose files failed to be deleted + sha1_hash info_hash; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated as a response to a ``torrent_handle::save_resume_data`` request. + // It is generated once the disk IO thread is done writing the state for this torrent. + struct TORRENT_EXPORT save_resume_data_alert: torrent_alert + { + // internal + save_resume_data_alert(boost::shared_ptr const& rd + , torrent_handle const& h); + + TORRENT_DEFINE_ALERT(save_resume_data_alert); + + const static int static_category = alert::storage_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // points to the resume data. + boost::shared_ptr resume_data; + }; + + // This alert is generated instead of ``save_resume_data_alert`` if there was an error + // generating the resume data. ``error`` describes what went wrong. + struct TORRENT_EXPORT save_resume_data_failed_alert: torrent_alert + { + // internal + save_resume_data_failed_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(save_resume_data_failed_alert); + + const static int static_category = alert::storage_notification + | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated as a response to a ``torrent_handle::pause`` request. It is + // generated once all disk IO is complete and the files in the torrent have been closed. + // This is useful for synchronizing with the disk. + struct TORRENT_EXPORT torrent_paused_alert: torrent_alert + { + // internal + torrent_paused_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_paused_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is generated as a response to a torrent_handle::resume() request. It is + // generated when a torrent goes from a paused state to an active state. + struct TORRENT_EXPORT torrent_resumed_alert: torrent_alert + { + // internal + torrent_resumed_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_resumed_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is posted when a torrent completes checking. i.e. when it transitions + // out of the ``checking files`` state into a state where it is ready to start downloading + struct TORRENT_EXPORT torrent_checked_alert: torrent_alert + { + // internal + torrent_checked_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_checked_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is generated when a HTTP seed name lookup fails. + struct TORRENT_EXPORT url_seed_alert: torrent_alert + { + // internal + url_seed_alert( + torrent_handle const& h + , std::string const& u + , error_code const& e); + url_seed_alert( + torrent_handle const& h + , std::string const& u + , std::string const& m); + + TORRENT_DEFINE_ALERT(url_seed_alert); + + const static int static_category = alert::peer_notification | alert::error_notification; + virtual std::string message() const; + + // the HTTP seed that failed + std::string url; + + // the error message, potentially from the server + std::string msg; + }; + + // If the storage fails to read or write files that it needs access to, this alert is + // generated and the torrent is paused. + struct TORRENT_EXPORT file_error_alert: torrent_alert + { + // internal + file_error_alert( + std::string const& f + , torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(file_error_alert); + + const static int static_category = alert::status_notification + | alert::error_notification + | alert::storage_notification; + virtual std::string message() const; + + // the path to the file that was accessed when the error occurred. + std::string file; + + // the error code describing the error. + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated when the metadata has been completely received and the info-hash + // failed to match it. i.e. the metadata that was received was corrupt. libtorrent will + // automatically retry to fetch it in this case. This is only relevant when running a + // torrent-less download, with the metadata extension provided by libtorrent. + struct TORRENT_EXPORT metadata_failed_alert: torrent_alert + { + // internal + metadata_failed_alert(const torrent_handle& h, error_code e); + + TORRENT_DEFINE_ALERT(metadata_failed_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the error that occurred + error_code error; + }; + + // This alert is generated when the metadata has been completely received and the torrent + // can start downloading. It is not generated on torrents that are started with metadata, but + // only those that needs to download it from peers (when utilizing the libtorrent extension). + // + // There are no additional data members in this alert. + // + // Typically, when receiving this alert, you would want to save the torrent file in order + // to load it back up again when the session is restarted. Here's an example snippet of + // code to do that:: + // + // torrent_handle h = alert->handle(); + // if (h.is_valid()) { + // boost::intrusive_ptr ti = h.torrent_file(); + // create_torrent ct(*ti); + // entry te = ct.generate(); + // std::vector buffer; + // bencode(std::back_inserter(buffer), te); + // FILE* f = fopen((to_hex(ti->info_hash().to_string()) + ".torrent").c_str(), "wb+"); + // if (f) { + // fwrite(&buffer[0], 1, buffer.size(), f); + // fclose(f); + // } + // } + // + struct TORRENT_EXPORT metadata_received_alert: torrent_alert + { + // internal + metadata_received_alert( + const torrent_handle& h); + + TORRENT_DEFINE_ALERT(metadata_received_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + }; + + // This alert is posted when there is an error on the UDP socket. The + // UDP socket is used for all uTP, DHT and UDP tracker traffic. It's + // global to the session. + struct TORRENT_EXPORT udp_error_alert: alert + { + // internal + udp_error_alert( + udp::endpoint const& ep + , error_code const& ec); + + TORRENT_DEFINE_ALERT(udp_error_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the source address associated with the error (if any) + udp::endpoint endpoint; + + // the error code describing the error + error_code error; + }; + + // Whenever libtorrent learns about the machines external IP, this alert is + // generated. The external IP address can be acquired from the tracker (if it + // supports that) or from peers that supports the extension protocol. + // The address can be accessed through the ``external_address`` member. + struct TORRENT_EXPORT external_ip_alert: alert + { + // internal + external_ip_alert(address const& ip); + + TORRENT_DEFINE_ALERT(external_ip_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + // the IP address that is believed to be our external IP + address external_address; + }; + + // This alert is generated when none of the ports, given in the port range, to + // session can be opened for listening. The ``endpoint`` member is the + // interface and port that failed, ``error`` is the error code describing + // the failure. + // + // libtorrent may sometimes try to listen on port 0, if all other ports failed. + // Port 0 asks the operating system to pick a port that's free). If that fails + // you may see a listen_failed_alert with port 0 even if you didn't ask to + // listen on it. + struct TORRENT_EXPORT listen_failed_alert: alert + { + enum socket_type_t { tcp, tcp_ssl, udp, i2p, socks5 }; + + // internal + listen_failed_alert( + tcp::endpoint const& ep + , int op + , error_code const& ec + , socket_type_t t); + + TORRENT_DEFINE_ALERT(listen_failed_alert); + + const static int static_category = alert::status_notification | alert::error_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the endpoint libtorrent attempted to listen on + tcp::endpoint endpoint; + + // the error the system returned + error_code error; + + enum op_t + { + parse_addr, open, bind, listen, get_peer_name, accept + }; + + // the specific low level operation that failed. See op_t. + int operation; + + // the type of listen socket this alert refers to. + socket_type_t sock_type; + }; + + // This alert is posted when the listen port succeeds to be opened on a + // particular interface. ``endpoint`` is the endpoint that successfully + // was opened for listening. + struct TORRENT_EXPORT listen_succeeded_alert: alert + { + enum socket_type_t { tcp, tcp_ssl, udp }; + + // internal + listen_succeeded_alert(tcp::endpoint const& ep, socket_type_t t); + + TORRENT_DEFINE_ALERT(listen_succeeded_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the endpoint libtorrent ended up listening on. The address + // refers to the local interface and the port is the listen port. + tcp::endpoint endpoint; + + // the type of listen socket this alert refers to. + socket_type_t sock_type; + }; + + // This alert is generated when a NAT router was successfully found but some + // part of the port mapping request failed. It contains a text message that + // may help the user figure out what is wrong. This alert is not generated in + // case it appears the client is not running on a NAT:ed network or if it + // appears there is no NAT router that can be remote controlled to add port + // mappings. + struct TORRENT_EXPORT portmap_error_alert: alert + { + // internal + portmap_error_alert(int i, int t, error_code const& e); + + TORRENT_DEFINE_ALERT(portmap_error_alert); + + const static int static_category = alert::port_mapping_notification + | alert::error_notification; + virtual std::string message() const; + + // refers to the mapping index of the port map that failed, i.e. + // the index returned from add_mapping(). + int mapping; + + // is 0 for NAT-PMP and 1 for UPnP. + int map_type; + + // tells you what failed. + error_code error; +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is generated when a NAT router was successfully found and + // a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP + // capable router, this is typically generated once when mapping the TCP + // port and, if DHT is enabled, when the UDP port is mapped. + struct TORRENT_EXPORT portmap_alert: alert + { + // internal + portmap_alert(int i, int port, int t); + + TORRENT_DEFINE_ALERT(portmap_alert); + + const static int static_category = alert::port_mapping_notification; + virtual std::string message() const; + + // refers to the mapping index of the port map that failed, i.e. + // the index returned from add_mapping(). + int mapping; + + // the external port allocated for the mapping. + int external_port; + + // 0 for NAT-PMP and 1 for UPnP. + int map_type; + }; + + // This alert is generated to log informational events related to either + // UPnP or NAT-PMP. They contain a log line and the type (0 = NAT-PMP + // and 1 = UPnP). Displaying these messages to an end user is only useful + // for debugging the UPnP or NAT-PMP implementation. + struct TORRENT_EXPORT portmap_log_alert: alert + { + // internal + portmap_log_alert(int t, std::string const& m); + + TORRENT_DEFINE_ALERT(portmap_log_alert); + + const static int static_category = alert::port_mapping_notification; + virtual std::string message() const; + + int map_type; + std::string msg; + }; + + // This alert is generated when a fastresume file has been passed to add_torrent() but the + // files on disk did not match the fastresume file. The error_code explains the reason why the + // resume file was rejected. + struct TORRENT_EXPORT fastresume_rejected_alert: torrent_alert + { + // internal + fastresume_rejected_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(fastresume_rejected_alert); + + const static int static_category = alert::status_notification + | alert::error_notification; + virtual std::string message() const; + + error_code error; + +#ifndef TORRENT_NO_DEPRECATE + std::string msg; +#endif + }; + + // This alert is posted when an incoming peer connection, or a peer that's about to be added + // to our peer list, is blocked for some reason. This could be any of: + // + // * the IP filter + // * i2p mixed mode restrictions (a normal peer is not allowed on an i2p swarm) + // * the port filter + // * the peer has a low port and ``no_connect_privileged_ports`` is enabled + // * the protocol of the peer is blocked (uTP/TCP blocking) + struct TORRENT_EXPORT peer_blocked_alert: torrent_alert + { + // internal + peer_blocked_alert(torrent_handle const& h, address const& i + , int r); + + TORRENT_DEFINE_ALERT(peer_blocked_alert); + + const static int static_category = alert::ip_block_notification; + virtual std::string message() const; + + // the address that was blocked. + address ip; + + enum reason_t + { + ip_filter, + port_filter, + i2p_mixed, + privileged_ports, + utp_disabled, + tcp_disabled + }; + + int reason; + }; + + // This alert is generated when a DHT node announces to an info-hash on our + // DHT node. It belongs to the ``dht_notification`` category. + struct TORRENT_EXPORT dht_announce_alert: alert + { + // internal + dht_announce_alert(address const& i, int p + , sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(dht_announce_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + address ip; + int port; + sha1_hash info_hash; + }; + + // This alert is generated when a DHT node sends a ``get_peers`` message to + // our DHT node. It belongs to the ``dht_notification`` category. + struct TORRENT_EXPORT dht_get_peers_alert: alert + { + // internal + dht_get_peers_alert(sha1_hash const& ih); + + TORRENT_DEFINE_ALERT(dht_get_peers_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + sha1_hash info_hash; + }; + + // This alert is posted approximately once every second, and it contains + // byte counters of most statistics that's tracked for torrents. Each active + // torrent posts these alerts regularly. + struct TORRENT_EXPORT stats_alert: torrent_alert + { + // internal + stats_alert(torrent_handle const& h, int interval + , stat const& s); + + TORRENT_DEFINE_ALERT(stats_alert); + + const static int static_category = alert::stats_notification; + virtual std::string message() const; + + enum stats_channel + { + upload_payload, + upload_protocol, + download_payload, + download_protocol, +#ifndef TORRENT_DISABLE_FULL_STATS + upload_ip_protocol, + upload_dht_protocol, + upload_tracker_protocol, + download_ip_protocol, + download_dht_protocol, + download_tracker_protocol, +#endif + num_channels + }; + + // an array of samples. The enum describes what each sample is a + // measurement of. All of these are raw, and not smoothing is performed. + int transferred[num_channels]; + + // the number of milliseconds during which these stats were collected. + // This is typically just above 1000, but if CPU is limited, it may be + // higher than that. + int interval; + }; + + // This alert is posted when the disk cache has been flushed for a specific + // torrent as a result of a call to torrent_handle::flush_cache(). This + // alert belongs to the ``storage_notification`` category, which must be + // enabled to let this alert through. The alert is also posted when removing + // a torrent from the session, once the outstanding cache flush is complete + // and the torrent does no longer have any files open. + struct TORRENT_EXPORT cache_flushed_alert: torrent_alert + { + // internal + cache_flushed_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(cache_flushed_alert); + + const static int static_category = alert::storage_notification; + }; + + // This alert is posted when a bittorrent feature is blocked because of the + // anonymous mode. For instance, if the tracker proxy is not set up, no + // trackers will be used, because trackers can only be used through proxies + // when in anonymous mode. + struct TORRENT_EXPORT anonymous_mode_alert: torrent_alert + { + // internal + anonymous_mode_alert(torrent_handle const& h + , int k, std::string const& s); + + TORRENT_DEFINE_ALERT(anonymous_mode_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + enum kind_t + { + // means that there's no proxy set up for tracker + // communication and the tracker will not be contacted. + // The tracker which this failed for is specified in the ``str`` member. + tracker_not_anonymous = 0 + }; + + // specifies what error this is, see kind_t. + int kind; + std::string str; + }; + + // This alert is generated when we receive a local service discovery message + // from a peer for a torrent we're currently participating in. + struct TORRENT_EXPORT lsd_peer_alert: peer_alert + { + // internal + lsd_peer_alert(torrent_handle const& h + , tcp::endpoint const& i); + + TORRENT_DEFINE_ALERT(lsd_peer_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + }; + + // This alert is posted whenever a tracker responds with a ``trackerid``. + // The tracker ID is like a cookie. The libtorrent will store the tracker ID + // for this tracker and repeat it in subsequent announces. + struct TORRENT_EXPORT trackerid_alert: tracker_alert + { + // internal + trackerid_alert(torrent_handle const& h + , std::string const& u + , const std::string& id); + + TORRENT_DEFINE_ALERT(trackerid_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + + // The tracker ID returned by the tracker + std::string trackerid; + }; + + // This alert is posted when the initial DHT bootstrap is done. + struct TORRENT_EXPORT dht_bootstrap_alert: alert + { + // internal + dht_bootstrap_alert(); + + TORRENT_DEFINE_ALERT(dht_bootstrap_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + }; + + // This alert is posted on RSS feed events such as start of RSS feed updates, + // successful completed updates and errors during updates. + // + // This alert is only posted if the ``rss_notifications`` category is enabled + // in the alert_mask. + struct TORRENT_EXPORT rss_alert: alert + { + // internal + rss_alert(feed_handle h, std::string const& u, int s, error_code const& ec); + + TORRENT_DEFINE_ALERT(rss_alert); + + const static int static_category = alert::rss_notification; + virtual std::string message() const; + + enum state_t + { + // An update of this feed was just initiated, it will either succeed + // or fail soon. + state_updating, + + // The feed just completed a successful update, there may be new items + // in it. If you're adding torrents manually, you may want to request + // the feed status of the feed and look through the ``items`` vector. + state_updated, + + // An error just occurred. See the ``error`` field for information on + // what went wrong. + state_error + }; + + // the handle to the feed which generated this alert. + feed_handle handle; + + // a short cut to access the url of the feed, without + // having to call feed_handle::get_settings(). + std::string url; + + // one of the values from rss_alert::state_t. + int state; + + // an error code used for when an error occurs on the feed. + error_code error; + }; + + // This is posted whenever a torrent is transitioned into the error state. + struct TORRENT_EXPORT torrent_error_alert: torrent_alert + { + // internal + torrent_error_alert(torrent_handle const& h + , error_code const& e); + + TORRENT_DEFINE_ALERT(torrent_error_alert); + + const static int static_category = alert::error_notification | alert::status_notification; + virtual std::string message() const; + + // specifies which error the torrent encountered. + error_code error; + }; + + // This is always posted for SSL torrents. This is a reminder to the client that + // the torrent won't work unless torrent_handle::set_ssl_certificate() is called with + // a valid certificate. Valid certificates MUST be signed by the SSL certificate + // in the .torrent file. + struct TORRENT_EXPORT torrent_need_cert_alert: torrent_alert + { + // internal + torrent_need_cert_alert(torrent_handle const& h); + + TORRENT_DEFINE_ALERT(torrent_need_cert_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + error_code error; + }; + + // The incoming connection alert is posted every time we successfully accept + // an incoming connection, through any mean. The most straigh-forward ways + // of accepting incoming connections are through the TCP listen socket and + // the UDP listen socket for uTP sockets. However, connections may also be + // accepted ofer a Socks5 or i2p listen socket, or via a torrent specific + // listen socket for SSL torrents. + struct TORRENT_EXPORT incoming_connection_alert: alert + { + // internal + incoming_connection_alert(int t, tcp::endpoint const& i); + + TORRENT_DEFINE_ALERT(incoming_connection_alert); + + const static int static_category = alert::peer_notification; + virtual std::string message() const; + + // tells you what kind of socket the connection was accepted + // as: + // + // 0. none (no socket instantiated) + // 1. TCP + // 2. Socks5 + // 3. HTTP + // 4. uTP + // 5. i2p + // 6. SSL/TCP + // 7. SSL/Socks5 + // 8. HTTPS (SSL/HTTP) + // 9. SSL/uTP + // + int socket_type; + + // is the IP address and port the connection came from. + tcp::endpoint ip; + }; + + // This alert is always posted when a torrent was attempted to be added + // and contains the return status of the add operation. The torrent handle of the new + // torrent can be found in the base class' ``handle`` member. If adding + // the torrent failed, ``error`` contains the error code. + struct TORRENT_EXPORT add_torrent_alert : torrent_alert + { + // internal + add_torrent_alert(torrent_handle h, add_torrent_params const& p, error_code ec); + + TORRENT_DEFINE_ALERT(add_torrent_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // a copy of the parameters used when adding the torrent, it can be used + // to identify which invocation to ``async_add_torrent()`` caused this alert. + add_torrent_params params; + + // set to the error, if one occurred while adding the torrent. + error_code error; + }; + + // This alert is only posted when requested by the user, by calling session::post_torrent_updates() + // on the session. It contains the torrent status of all torrents that changed + // since last time this message was posted. Its category is ``status_notification``, but + // it's not subject to filtering, since it's only manually posted anyway. + struct TORRENT_EXPORT state_update_alert : alert + { + TORRENT_DEFINE_ALERT(state_update_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // contains the torrent status of all torrents that changed since last time + // this message was posted. Note that you can map a torrent status to a specific torrent + // via its ``handle`` member. The receiving end is suggested to have all torrents sorted + // by the torrent_handle or hashed by it, for efficient updates. + std::vector status; + }; + + // When a torrent changes its info-hash, this alert is posted. This only happens in very + // specific cases. For instance, when a torrent is downloaded from a URL, the true info + // hash is not known immediately. First the .torrent file must be downloaded and parsed. + // + // Once this download completes, the ``torrent_update_alert`` is posted to notify the client + // of the info-hash changing. + struct TORRENT_EXPORT torrent_update_alert : torrent_alert + { + // internal + torrent_update_alert(torrent_handle h, sha1_hash const& old_hash, sha1_hash const& new_hash); + + TORRENT_DEFINE_ALERT(torrent_update_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // ``old_ih`` and ``new_ih`` are the previous and new info-hash for the torrent, respectively. + sha1_hash old_ih; + sha1_hash new_ih; + }; + + // This alert is posted every time a new RSS item (i.e. torrent) is received + // from an RSS feed. + // + // It is only posted if the ``rss_notifications`` category is enabled in the + // alert_mask. + struct TORRENT_EXPORT rss_item_alert : alert + { + // internal + rss_item_alert(feed_handle h, feed_item const& item); + + TORRENT_DEFINE_ALERT(rss_item_alert); + + const static int static_category = alert::rss_notification; + virtual std::string message() const; + + feed_handle handle; + feed_item item; + }; + + // posted when something fails in the DHT. This is not necessarily a fatal + // error, but it could prevent proper operation + struct TORRENT_EXPORT dht_error_alert: alert + { + // internal + dht_error_alert(int op, error_code const& ec); + + TORRENT_DEFINE_ALERT(dht_error_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + + // the error code + error_code error; + + enum op_t + { + unknown, + hostname_lookup + }; + + // the operation that failed + op_t operation; + }; + + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up immutable items in the DHT. + struct TORRENT_EXPORT dht_immutable_item_alert: alert + { + dht_immutable_item_alert(sha1_hash const& t, entry const& i); + + TORRENT_DEFINE_ALERT(dht_immutable_item_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the target hash of the immutable item. This must + // match the sha-1 hash of the bencoded form of ``item``. + sha1_hash target; + + // the data for this item + entry item; + }; + + // this alert is posted as a response to a call to session::get_item(), + // specifically the overload for looking up mutable items in the DHT. + struct TORRENT_EXPORT dht_mutable_item_alert: alert + { + dht_mutable_item_alert(boost::array k + , boost::array sig + , boost::uint64_t sequence + , std::string const& s + , entry const& i); + + TORRENT_DEFINE_ALERT(dht_mutable_item_alert); + + const static int static_category = alert::error_notification + | alert::dht_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + // the public key that was looked up + boost::array key; + + // the signature of the data. This is not the signature of the + // plain encoded form of the item, but it includes the sequence number + // and possibly the hash as well. See the dht_store document for more + // information. This is primarily useful for echoing back in a store + // request. + boost::array signature; + + // the sequence number of this item + boost::uint64_t seq; + + // the salf, if any, used to lookup and store this item. If no + // salt was used, this is an empty string + std::string salt; + + // the data for this item + entry item; + }; + + // this is posted when a DHT put operation completes. This is useful if the + // client is waiting for a put to complete before shutting down for instance. + struct TORRENT_EXPORT dht_put_alert: alert + { + // internal + dht_put_alert(sha1_hash const& t); + dht_put_alert(boost::array key + , boost::array sig + , std::string s + , boost::uint64_t sequence_number); + + TORRENT_DEFINE_ALERT(dht_put_alert); + + const static int static_category = alert::dht_notification; + virtual std::string message() const; + + // the target hash the item was stored under if this was an *immutable* + // item. + sha1_hash target; + + // if a mutable item was stored, these are the public key, signature, + // salt and sequence number the item was stored under. + boost::array public_key; + boost::array signature; + std::string salt; + boost::uint64_t seq; + }; + + // this alert is used to report errors in the i2p SAM connection + struct TORRENT_EXPORT i2p_alert : alert + { + i2p_alert(error_code const& ec); + + TORRENT_DEFINE_ALERT(i2p_alert); + + const static int static_category = alert::error_notification; + virtual std::string message() const; + + // the error that occurred in the i2p SAM connection + error_code error; + }; + +#undef TORRENT_DEFINE_ALERT + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp new file mode 100644 index 0000000000..c5521ff469 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/alloca.hpp @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCA + +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_MINGW + +#include +#define TORRENT_ALLOCA(t, n) static_cast(_alloca(sizeof(t) * (n))) + +#elif defined TORRENT_BSD + +#include +#define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) + +#else + +#include +#define TORRENT_ALLOCA(t, n) static_cast(alloca(sizeof(t) * (n))) + +#endif + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp new file mode 100644 index 0000000000..b67816986a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/allocator.hpp @@ -0,0 +1,79 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCATOR_HPP_INCLUDED +#define TORRENT_ALLOCATOR_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT int page_size(); + + struct TORRENT_EXTRA_EXPORT page_aligned_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static char* malloc(const size_type bytes); + static void free(char* block); + }; + + struct TORRENT_EXTRA_EXPORT aligned_holder + { + aligned_holder(): m_buf(0) {} + aligned_holder(int size): m_buf(page_aligned_allocator::malloc(size)) {} + ~aligned_holder() { if (m_buf) page_aligned_allocator::free(m_buf); } + char* get() const { return m_buf; } + void reset(char* buf = 0) + { + if (m_buf) page_aligned_allocator::free(m_buf); + m_buf = buf; + } + void swap(aligned_holder& h) + { + char* tmp = m_buf; + m_buf = h.m_buf; + h.m_buf = tmp; + } + private: + aligned_holder(aligned_holder const&); + aligned_holder& operator=(aligned_holder const&); + char* m_buf; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp new file mode 100644 index 0000000000..847648297c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/assert.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ASSERT + +#include "libtorrent/config.hpp" + +#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING \ + || TORRENT_RELEASE_ASSERTS || defined TORRENT_DEBUG_BUFFERS +#include +std::string demangle(char const* name); +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth = 0); +#endif + +#if TORRENT_USE_ASSERTS + +#if TORRENT_PRODUCTION_ASSERTS +extern char const* libtorrent_assert_log; +#endif + +#if (defined __linux__ || defined __MACH__) && defined __GNUC__ && !TORRENT_USE_SYSTEM_ASSERT + +#if TORRENT_USE_IOSTREAM +#include +#endif + +TORRENT_EXPORT void assert_fail(const char* expr, int line, char const* file + , char const* function, char const* val, int kind = 0); + +#define TORRENT_ASSERT_PRECOND(x) \ + do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, "", 1); } while (false) + +#define TORRENT_ASSERT(x) \ + do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, "", 0); } while (false) + +#if TORRENT_USE_IOSTREAM +#define TORRENT_ASSERT_VAL(x, y) \ + do { if (x) {} else { std::stringstream __s__; __s__ << #y ": " << y; \ + assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, __s__.str().c_str(), 0); } } while (false) +#else +#define TORRENT_ASSERT_VAL(x, y) TORRENT_ASSERT(x) +#endif + +#else +#include +#define TORRENT_ASSERT_PRECOND(x) assert(x) +#define TORRENT_ASSERT(x) assert(x) +#define TORRENT_ASSERT_VAL(x, y) assert(x) +#endif + +#else // TORRENT_USE_ASSERTS + +#define TORRENT_ASSERT_PRECOND(a) do {} while(false) +#define TORRENT_ASSERT(a) do {} while(false) +#define TORRENT_ASSERT_VAL(a, b) do {} while(false) + +#endif // TORRENT_USE_ASSERTS + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp new file mode 100644 index 0000000000..18dab8b9bc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -0,0 +1,1286 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_IMPL_HPP_INCLUDED +#define TORRENT_SESSION_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include // for va_start, va_end + +#ifndef TORRENT_DISABLE_GEO_IP +#ifdef WITH_SHIPPED_GEOIP_H +#include "libtorrent/GeoIP.h" +#else +#include +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/ip_voter.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/debug.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/policy.hpp" // for policy::peer +#include "libtorrent/alert_manager.hpp" // for alert_manager +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/socket_io.hpp" // for print_address +#include "libtorrent/address.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/bloom_filter.hpp" +#include "libtorrent/rss.hpp" +#include "libtorrent/alert_dispatcher.hpp" +#include "libtorrent/kademlia/dht_observer.hpp" + +#if TORRENT_COMPLETE_TYPES_REQUIRED +#include "libtorrent/peer_connection.hpp" +#endif + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +#if defined TORRENT_STATS && defined __MACH__ +#include +#include +#include +#include +#endif + +namespace libtorrent +{ + + struct plugin; + class upnp; + class natpmp; + class lsd; + struct fingerprint; + class torrent; + class alert; + + namespace dht + { + struct dht_tracker; + class item; + } + + struct bencode_map_entry; + + struct listen_socket_t + { + listen_socket_t(): external_port(0), ssl(false) {} + + // this is typically empty but can be set + // to the WAN IP address of NAT-PMP or UPnP router + address external_address; + + // this is typically set to the same as the local + // listen port. In case a NAT port forward was + // successfully opened, this will be set to the + // port that is open on the external (NAT) interface + // on the NAT box itself. This is the port that has + // to be published to peers, since this is the port + // the client is reachable through. + int external_port; + + // set to true if this is an SSL listen socket + bool ssl; + + // the actual socket + boost::shared_ptr sock; + }; + + namespace aux + { + struct session_impl; + +#if defined TORRENT_STATS && !defined __MACH__ + struct vm_statistics_data_t + { + boost::uint64_t active_count; + boost::uint64_t inactive_count; + boost::uint64_t wire_count; + boost::uint64_t free_count; + boost::uint64_t pageins; + boost::uint64_t pageouts; + boost::uint64_t faults; + }; +#endif + + struct thread_cpu_usage + { + ptime user_time; + ptime system_time; + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct tracker_logger; +#endif + + // used to initialize the g_current_time before + // anything else + struct initialize_timer + { + initialize_timer(); + }; + + TORRENT_EXPORT std::pair settings_map(); + + // this is the link between the main thread and the + // thread started to run the main downloader loop + struct TORRENT_EXTRA_EXPORT session_impl + : alert_dispatcher + , dht::dht_observer + , boost::noncopyable + , initialize_timer + , udp_socket_observer + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + // this needs to be destructed last, since other components may log + // things as they are being destructed. That's why it's declared at + // the top of session_impl + boost::shared_ptr m_logger; +#endif + + // the size of each allocation that is chained in the send buffer + enum { send_buffer_size = 128 }; + +#ifdef TORRENT_DEBUG + friend class ::libtorrent::peer_connection; +#endif + friend struct checker_impl; + friend class invariant_access; + typedef std::set > connection_map; + typedef std::map > torrent_map; + + session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface + , boost::uint32_t alert_mask); + virtual ~session_impl(); + void update_dht_announce_interval(); + void init(); + void start_session(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + void set_log_path(std::string const& p) { m_logpath = p; } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::function( + torrent*, void*)> ext); + void add_ses_extension(boost::shared_ptr ext); +#endif +#if TORRENT_USE_ASSERTS + bool has_peer(peer_connection const* p) const + { + TORRENT_ASSERT(is_network_thread()); + return std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&boost::intrusive_ptr::get, _1) == p) + != m_connections.end(); + } + // this is set while the session is building the + // torrent status update message + bool m_posting_torrent_updates; +#endif + void main_thread(); + + void open_listen_port(int flags, error_code& ec); + + // prioritize this torrent to be allocated some connection + // attempts, because this torrent needs more peers. + // this is typically done when a torrent starts out and + // need the initial push to connect peers + void prioritize_connections(boost::weak_ptr t); + + // if we are listening on an IPv6 interface + // this will return one of the IPv6 addresses on this + // machine, otherwise just an empty endpoint + tcp::endpoint get_ipv6_interface() const; + tcp::endpoint get_ipv4_interface() const; + + void async_accept(boost::shared_ptr const& listener, bool ssl); + void on_accept_connection(boost::shared_ptr const& s + , boost::weak_ptr listener, error_code const& e, bool ssl); + void on_socks_accept(boost::shared_ptr const& s + , error_code const& e); + + void incoming_connection(boost::shared_ptr const& s); + +#if TORRENT_USE_ASSERTS + bool is_network_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_network_thread == 0) return true; + return m_network_thread == pthread_self(); +#endif + return true; + } + bool is_not_network_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_network_thread == 0) return true; + return m_network_thread != pthread_self(); +#endif + return true; + } +#endif + + feed_handle add_feed(feed_settings const& feed); + void remove_feed(feed_handle h); + void get_feeds(std::vector* f) const; + + boost::weak_ptr find_torrent(sha1_hash const& info_hash) const; + boost::weak_ptr find_torrent(std::string const& uuid) const; + boost::weak_ptr find_disconnect_candidate_torrent() const; + + peer_id const& get_peer_id() const { return m_peer_id; } + + void close_connection(peer_connection const* p, error_code const& ec); + + void set_settings(session_settings const& s); + session_settings const& settings() const { return m_settings; } + +#ifndef TORRENT_DISABLE_DHT + void add_dht_node_name(std::pair const& node); + void add_dht_node(udp::endpoint n); + void add_dht_router(std::pair const& node); + void set_dht_settings(dht_settings const& s); + dht_settings const& get_dht_settings() const { return m_dht_settings; } + void start_dht(); + void stop_dht(); + void start_dht(entry const& startup_state); + + // this is called for torrents when they are started + // it will prioritize them for announcing to + // the DHT, to get the initial peers quickly + void prioritize_dht(boost::weak_ptr t); + + void get_immutable_callback(sha1_hash target + , dht::item const& i); + void get_mutable_callback(dht::item const& i); + + void dht_get_immutable_item(sha1_hash const& target); + + void dht_get_mutable_item(boost::array key + , std::string salt = std::string()); + + void dht_put_item(entry data, sha1_hash target); + + void dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + +#ifndef TORRENT_NO_DEPRECATE + entry dht_state() const; +#endif + void on_dht_announce(error_code const& e); + void on_dht_router_name_lookup(error_code const& e + , tcp::resolver::iterator host); +#endif + + void maybe_update_udp_mapping(int nat, int local_port, int external_port); + +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const { return m_pe_settings; } +#endif + + void on_port_map_log(char const* msg, int map_transport); + + void on_lsd_announce(error_code const& e); + + // called when a port mapping is successful, or a router returns + // a failure to map a port + void on_port_mapping(int mapping, address const& ip, int port + , error_code const& ec, int nat_transport); + + bool is_aborted() const { return m_abort; } + bool is_paused() const { return m_paused; } + + void pause(); + void resume(); + + void set_ip_filter(ip_filter const& f); + ip_filter const& get_ip_filter() const; + + void set_port_filter(port_filter const& f); + + void listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface = 0 + , int flags = 0); + bool is_listening() const; + + torrent_handle add_torrent(add_torrent_params const&, error_code& ec); + torrent_handle add_torrent_impl(add_torrent_params const&, error_code& ec); + void async_add_torrent(add_torrent_params* params); + + void remove_torrent(torrent_handle const& h, int options); + void remove_torrent_impl(boost::shared_ptr tptr, int options); + + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const; + void post_torrent_updates(); + + std::vector get_torrents() const; + + void queue_check_torrent(boost::shared_ptr const& t); + void dequeue_check_torrent(boost::shared_ptr const& t); + + void set_alert_mask(boost::uint32_t m); + size_t set_alert_queue_size_limit(size_t queue_size_limit_); + std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); + void set_alert_dispatch(boost::function)> const&); + void post_alert(const alert& alert_); + + alert const* wait_for_alert(time_duration max_wait); + +#ifndef TORRENT_NO_DEPRECATE + int upload_rate_limit() const; + int download_rate_limit() const; + int local_upload_rate_limit() const; + int local_download_rate_limit() const; + + void set_local_download_rate_limit(int bytes_per_second); + void set_local_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); + void set_upload_rate_limit(int bytes_per_second); + void set_max_half_open_connections(int limit); + void set_max_connections(int limit); + void set_max_uploads(int limit); + + int max_connections() const; + int max_uploads() const; + int max_half_open_connections() const; + +#endif + + int num_uploads() const { return m_num_unchoked; } + int num_connections() const + { return m_connections.size(); } + + void unchoke_peer(peer_connection& c); + void choke_peer(peer_connection& c); + + session_status status() const; + void set_peer_id(peer_id const& id); + void set_key(int key); + address listen_address() const; + boost::uint16_t listen_port() const; + boost::uint16_t ssl_listen_port() const; + + void abort(); + + torrent_handle find_torrent_handle(sha1_hash const& info_hash); + + void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false); + + void save_state(entry* e, boost::uint32_t flags) const; + void load_state(lazy_entry const* e); + + void set_proxy(proxy_settings const& s); + proxy_settings const& proxy() const { return m_proxy; } + +#ifndef TORRENT_NO_DEPRECATE + void set_peer_proxy(proxy_settings const& s) { set_proxy(s); } + void set_web_seed_proxy(proxy_settings const& s) { set_proxy(s); } + void set_tracker_proxy(proxy_settings const& s) { set_proxy(s); } + proxy_settings const& peer_proxy() const { return proxy(); } + proxy_settings const& web_seed_proxy() const { return proxy(); } + proxy_settings const& tracker_proxy() const { return proxy(); } + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s) { set_proxy(s); } + proxy_settings const& dht_proxy() const { return proxy(); } +#endif +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_DISABLE_DHT + bool is_dht_running() const { return (m_dht.get() != NULL); } +#endif + +#if TORRENT_USE_I2P + void set_i2p_proxy(proxy_settings const& s); + void on_i2p_open(error_code const& ec); + proxy_settings const& i2p_proxy() const + { return m_i2p_conn.proxy(); } + void open_new_incoming_i2p_connection(); + void on_i2p_accept(boost::shared_ptr const& s + , error_code const& e); +#endif + +#ifndef TORRENT_DISABLE_GEO_IP + std::string as_name_for_ip(address const& a); + int as_for_ip(address const& a); + std::pair* lookup_as(int as); + void load_asnum_db(std::string file); + bool has_asnum_db() const { return m_asnum_db; } + + void load_country_db(std::string file); + bool has_country_db() const { return m_country_db; } + char const* country_for_ip(address const& a); + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void load_asnum_dbw(std::wstring file); + void load_country_dbw(std::wstring file); +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_DISABLE_GEO_IP + + void start_lsd(); + natpmp* start_natpmp(); + upnp* start_upnp(); + + void stop_lsd(); + void stop_natpmp(); + void stop_upnp(); + + int add_port_mapping(int t, int external_port + , int local_port); + void delete_port_mapping(int handle); + + int next_port(); + + void add_redundant_bytes(size_type b, int reason) + { + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + m_redundant_bytes[reason] += b; + } + + void add_failed_bytes(size_type b) + { + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + } + + char* allocate_buffer(); + void free_buffer(char* buf); + + char* allocate_disk_buffer(char const* category); + void free_disk_buffer(char* buf); + + enum + { + source_dht = 1, + source_peer = 2, + source_tracker = 4, + source_router = 8 + }; + + // implements dht_observer + virtual void set_external_address(address const& ip + , address const& source); + + void set_external_address(address const& ip + , int source_type, address const& source); + + external_ip const& external_address() const; + + bool can_write_to_disk() const + { return m_disk_thread.can_write(); } + + // used when posting synchronous function + // calls to session_impl and torrent objects + mutable libtorrent::mutex mut; + mutable libtorrent::condition_variable cond; + + void inc_disk_queue(int channel) + { + TORRENT_ASSERT(channel >= 0 && channel < 2); + ++m_disk_queues[channel]; + } + + void dec_disk_queue(int channel) + { + TORRENT_ASSERT(channel >= 0 && channel < 2); + TORRENT_ASSERT(m_disk_queues[channel] > 0); + --m_disk_queues[channel]; + } + + void inc_active_downloading() { ++m_num_active_downloading; } + void dec_active_downloading() + { + TORRENT_ASSERT(m_num_active_downloading > 0); + --m_num_active_downloading; + } + void inc_active_finished() { ++m_num_active_finished; } + void dec_active_finished() + { + TORRENT_ASSERT(m_num_active_finished > 0); + --m_num_active_finished; + } + +#if TORRENT_USE_ASSERTS + bool in_state_updates(boost::shared_ptr t) + { + return std::find_if(m_state_updates.begin(), m_state_updates.end() + , boost::bind(&boost::weak_ptr::lock, _1) == t) != m_state_updates.end(); + } +#endif + + void add_to_update_queue(boost::weak_ptr t) + { + TORRENT_ASSERT(std::find_if(m_state_updates.begin(), m_state_updates.end() + , boost::bind(&boost::weak_ptr::lock, _1) == t.lock()) == m_state_updates.end()); + m_state_updates.push_back(t); + } + +// private: + + // implements alert_dispatcher + virtual bool post_alert(alert* a); + + void update_connections_limit(); + void update_unchoke_limit(); + void trigger_auto_manage(); + void on_trigger_auto_manage(); + void update_rate_settings(); + + void update_disk_thread_settings(); + void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); + void setup_socket_buffers(socket_type& s); + + // the settings for the client + session_settings m_settings; + + // this is a shared pool where policy_peer objects + // are allocated. It's a pool since we're likely + // to have tens of thousands of peers, and a pool + // saves significant overhead +#ifdef TORRENT_STATS + struct logging_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static char* malloc(const size_type bytes) + { + allocated_bytes += bytes; + ++allocations; + return (char*)::malloc(bytes); + } + + static void free(char* const block) + { + --allocations; + return ::free(block); + } + + static int allocations; + static int allocated_bytes; + }; + boost::object_pool< + policy::ipv4_peer, logging_allocator> m_ipv4_peer_pool; +#if TORRENT_USE_IPV6 + boost::object_pool< + policy::ipv6_peer, logging_allocator> m_ipv6_peer_pool; +#endif +#if TORRENT_USE_I2P + boost::object_pool< + policy::i2p_peer, logging_allocator> m_i2p_peer_pool; +#endif +#else + boost::object_pool m_ipv4_peer_pool; +#if TORRENT_USE_IPV6 + boost::object_pool m_ipv6_peer_pool; +#endif +#if TORRENT_USE_I2P + boost::object_pool m_i2p_peer_pool; +#endif +#endif + + // this vector is used to store the block_info + // objects pointed to by partial_piece_info returned + // by torrent::get_download_queue. + std::vector m_block_info_storage; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // this pool is used to allocate and recycle send + // buffers from. + boost::pool<> m_send_buffers; +#endif + + // the file pool that all storages in this session's + // torrents uses. It sets a limit on the number of + // open files by this session. + // file pool must be destructed after the torrents + // since they will still have references to it + // when they are destructed. + file_pool m_files; + + // this is where all active sockets are stored. + // the selector can sleep while there's no activity on + // them + mutable io_service m_io_service; + +#ifdef TORRENT_USE_OPENSSL + // this is a generic SSL context used when talking to + // unauthenticated HTTPS servers + asio::ssl::context m_ssl_ctx; +#endif + + // handles delayed alerts + alert_manager m_alerts; + + // handles disk io requests asynchronously + // peers have pointers into the disk buffer + // pool, and must be destructed before this + // object. The disk thread relies on the file + // pool object, and must be destructed before + // m_files. The disk io thread posts completion + // events to the io service, and needs to be + // constructed after it. + disk_io_thread m_disk_thread; + + // this is a list of half-open tcp connections + // (only outgoing connections) + // this has to be one of the last + // members to be destructed + connection_queue m_half_open; + + // the bandwidth manager is responsible for + // handing out bandwidth to connections that + // asks for it, it can also throttle the + // rate. + bandwidth_manager m_download_rate; + bandwidth_manager m_upload_rate; + + // the global rate limiter bandwidth channels + bandwidth_channel m_download_channel; + bandwidth_channel m_upload_channel; + + // bandwidth channels for local peers when + // rate limits are ignored. They are only + // throttled by these global rate limiters + // and they don't have a rate limit set by + // default + bandwidth_channel m_local_download_channel; + bandwidth_channel m_local_upload_channel; + + // all tcp peer connections are subject to these + // bandwidth limits. Local peers are excempted + // from this limit. The purpose is to be able to + // throttle TCP that passes over the internet + // bottleneck (i.e. modem) to avoid starving out + // uTP connections. + bandwidth_channel m_tcp_download_channel; + bandwidth_channel m_tcp_upload_channel; + + bandwidth_channel* m_bandwidth_channel[2]; + + // the number of peer connections that are waiting + // for the disk. one for each channel. + // upload_channel means waiting to read from disk + // and download_channel is waiting to write to disk + int m_disk_queues[2]; + + tracker_manager m_tracker_manager; + torrent_map m_torrents; + std::map > m_uuids; + + // counters of how many of the active (non-paused) torrents + // are finished and downloading. This is used to weigh the + // priority of downloading and finished torrents when connecting + // more peers. + int m_num_active_downloading; + int m_num_active_finished; + + typedef std::list > check_queue_t; + + // this has all torrents that wants to be checked in it + check_queue_t m_queued_for_checking; + + // this maps sockets to their peer_connection + // object. It is the complete list of all connected + // peers. + connection_map m_connections; + + // this list holds incoming connections while they + // are performing SSL handshake. When we shut down + // the session, all of these are disconnected, otherwise + // they would linger and stall or hang session shutdown + std::set > m_incoming_sockets; + + // peer connections are put here when disconnected to avoid + // race conditions with the disk thread. It's important that + // peer connections are destructed from the network thread, + // once a peer is disconnected, it's put in this list and + // every second their refcount is checked, and if it's 1, + // they are deleted (from the network thread) + std::vector > m_undead_peers; + + // filters incoming connections + ip_filter m_ip_filter; + + // filters outgoing connections + port_filter m_port_filter; + + // the peer id that is generated at the start of the session + peer_id m_peer_id; + + // the key is an id that is used to identify the + // client with the tracker only. It is randomized + // at startup + int m_key; + + // the number of retries we make when binding the + // listen socket. For each retry the port number + // is incremented by one + int m_listen_port_retries; + + // the ip-address of the interface + // we are supposed to listen on. + // if the ip is set to zero, it means + // that we should let the os decide which + // interface to listen on + tcp::endpoint m_listen_interface; + + // if we're listening on an IPv6 interface + // this is one of the non local IPv6 interfaces + // on this machine + tcp::endpoint m_ipv6_interface; + tcp::endpoint m_ipv4_interface; + + // since we might be listening on multiple interfaces + // we might need more than one listen socket + std::list m_listen_sockets; + +#if TORRENT_USE_I2P + i2p_connection m_i2p_conn; + boost::shared_ptr m_i2p_listen_socket; +#endif + +#ifdef TORRENT_USE_OPENSSL + void ssl_handshake(error_code const& ec, boost::shared_ptr s); +#endif + + // when as a socks proxy is used for peers, also + // listen for incoming connections on a socks connection + boost::shared_ptr m_socks_listen_socket; + boost::uint16_t m_socks_listen_port; + + void open_new_incoming_socks_connection(); + + void setup_listener(listen_socket_t* s, tcp::endpoint ep, int& retries + , bool v6_only, int flags, error_code& ec); + + // the proxy used for bittorrent + proxy_settings m_proxy; + +#ifndef TORRENT_DISABLE_DHT + entry m_dht_state; +#endif + + // the number of unchoked peers as set by the auto-unchoker + // this should always be >= m_max_uploads + int m_allowed_upload_slots; + + // the number of unchoked peers + int m_num_unchoked; + + // this is initialized to the unchoke_interval + // session_setting and decreased every second. + // when it reaches zero, it is reset to the + // unchoke_interval and the unchoke set is + // recomputed. + int m_unchoke_time_scaler; + + // this is used to decide when to recalculate which + // torrents to keep queued and which to activate + int m_auto_manage_time_scaler; + + // works like unchoke_time_scaler but it + // is only decresed when the unchoke set + // is recomputed, and when it reaches zero, + // the optimistic unchoke is moved to another peer. + int m_optimistic_unchoke_time_scaler; + + // works like unchoke_time_scaler. Each time + // it reaches 0, and all the connections are + // used, the worst connection will be disconnected + // from the torrent with the most peers + int m_disconnect_time_scaler; + + // when this scaler reaches zero, it will + // scrape one of the auto managed, paused, + // torrents. + int m_auto_scrape_time_scaler; + + // the index of the torrent that we'll + // refresh the next time + int m_next_explicit_cache_torrent; + + // this is a counter of the number of seconds until + // the next time the read cache is rotated, if we're + // using an explicit read read cache. + int m_cache_rotation_timer; + + // statistics gathered from all torrents. + stat m_stat; + + int m_peak_up_rate; + int m_peak_down_rate; + + void on_disk_queue(); + void on_tick(error_code const& e); + + void try_connect_more_peers(int num_downloads, int num_downloads_peers); + void auto_manage_torrents(std::vector& list + , int& dht_limit, int& tracker_limit, int& lsd_limit + , int& hard_limit, int type_limit); + void recalculate_auto_managed_torrents(); + void recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents); + void recalculate_optimistic_unchoke_slots(); + + ptime m_created; + boost::int64_t session_time() const { return total_seconds(time_now() - m_created); } + + ptime m_last_tick; + ptime m_last_second_tick; + // used to limit how often disk warnings are generated + ptime m_last_disk_performance_warning; + ptime m_last_disk_queue_performance_warning; + + // the last time we went through the peers + // to decide which ones to choke/unchoke + ptime m_last_choke; + + // the time when the next rss feed needs updating + ptime m_next_rss_update; + + // update any rss feeds that need updating and + // recalculate m_next_rss_update + void update_rss_feeds(); + + // when outgoing_ports is configured, this is the + // port we'll bind the next outgoing socket to + int m_next_port; + +#ifndef TORRENT_DISABLE_DHT + boost::intrusive_ptr m_dht; + dht_settings m_dht_settings; + + // these are used when starting the DHT + // (and bootstrapping it), and then erased + std::list m_dht_router_nodes; + + // this announce timer is used + // by the DHT. + deadline_timer m_dht_announce_timer; + + // the number of torrents there were when the + // update_dht_announce_interval() was last called. + // if the number of torrents changes significantly + // compared to this number, the DHT announce interval + // is updated again. This especially matters for + // small numbers. + int m_dht_interval_update_torrents; +#endif + + bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size); + + // see m_external_listen_port. This is the same + // but for the udp port used by the DHT. + int m_external_udp_port; + + rate_limited_udp_socket m_udp_socket; + + utp_socket_manager m_utp_socket_manager; + + // the number of torrent connection boosts + // connections that have been made this second + // this is deducted from the connect speed + int m_boost_connections; + +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif + + boost::intrusive_ptr m_natpmp; + boost::intrusive_ptr m_upnp; + boost::intrusive_ptr m_lsd; + + // mask is a bitmask of which protocols to remap on: + // 1: NAT-PMP + // 2: UPnP + void remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port); + + // 0 is natpmp 1 is upnp + int m_tcp_mapping[2]; + int m_udp_mapping[2]; +#ifdef TORRENT_USE_OPENSSL + int m_ssl_mapping[2]; +#endif + + // the timer used to fire the tick + deadline_timer m_timer; + + // torrents are announced on the local network in a + // round-robin fashion. All torrents are cycled through + // within the LSD announce interval (which defaults to + // 5 minutes) + torrent_map::iterator m_next_lsd_torrent; + +#ifndef TORRENT_DISABLE_DHT + // torrents are announced on the DHT in a + // round-robin fashion. All torrents are cycled through + // within the DHT announce interval (which defaults to + // 15 minutes) + torrent_map::iterator m_next_dht_torrent; + + // torrents that don't have any peers + // when added should be announced to the DHT + // as soon as possible. Such torrents are put + // in this queue and get announced the next time + // the timer fires, instead of the next one in + // the round-robin sequence. + std::deque > m_dht_torrents; +#endif + + // torrents prioritized to get connection attempts + std::deque, int> > m_prio_torrents; + + // this announce timer is used + // by Local service discovery + deadline_timer m_lsd_announce_timer; + + tcp::resolver m_host_resolver; + + // the index of the torrent that will be offered to + // connect to a peer next time on_tick is called. + // This implements a round robin. + torrent_map::iterator m_next_connect_torrent; + + // this is the number of attempts of connecting to + // peers we have given to the torrent pointed to + // by m_next_connect_torrent. Once this reaches + // the number of connection attempts this particular + // torrent should have, the counter is reset and + // m_next_connect_torrent takes a step forward + // to give the next torrent its connection attempts. + int m_current_connect_attempts; + + // this is the round-robin cursor for peers that + // get to download again after the disk has been + // blocked + connection_map::iterator m_next_disk_peer; +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +#ifdef TORRENT_DISK_STATS + void log_buffer_usage(); + // used to log send buffer usage statistics + std::ofstream m_buffer_usage_logger; + // the number of send buffers that are allocated + int m_buffer_allocations; +#endif + +#ifdef TORRENT_REQUEST_LOGGING + // used to log all requests from peers + FILE* m_request_log; +#endif + +#ifdef TORRENT_STATS + void rotate_stats_log(); + void print_log_line(int tick_interval_ms, ptime now); + void reset_stat_counters(); + void enable_stats_logging(bool s); + + bool m_stats_logging_enabled; + + // the last time we rotated the log file + ptime m_last_log_rotation; + + // logger used to write bandwidth usage statistics + FILE* m_stats_logger; + // sequence number for log file. Log files are + // rotated every hour and the sequence number is + // incremented by one + int m_log_seq; + // the number of peers that were disconnected this + // tick due to protocol error + int m_error_peers; + int m_disconnected_peers; + int m_eof_peers; + int m_connreset_peers; + int m_connrefused_peers; + int m_connaborted_peers; + int m_perm_peers; + int m_buffer_peers; + int m_unreachable_peers; + int m_broken_pipe_peers; + int m_addrinuse_peers; + int m_no_access_peers; + int m_invalid_arg_peers; + int m_aborted_peers; + + int m_piece_requests; + int m_max_piece_requests; + int m_invalid_piece_requests; + int m_choked_piece_requests; + int m_cancelled_piece_requests; + int m_piece_rejects; + + int m_error_incoming_peers; + int m_error_outgoing_peers; + int m_error_rc4_peers; + int m_error_encrypted_peers; + int m_error_tcp_peers; + int m_error_utp_peers; + // the number of times the piece picker fell through + // to the end-game mode + int m_end_game_piece_picker_blocks; + int m_piece_picker_blocks; + int m_piece_picks; + int m_reject_piece_picks; + int m_unchoke_piece_picks; + int m_incoming_redundant_piece_picks; + int m_incoming_piece_picks; + int m_end_game_piece_picks; + int m_snubbed_piece_picks; + int m_connect_timeouts; + int m_uninteresting_peers; + int m_timeout_peers; + int m_no_memory_peers; + int m_too_many_peers; + int m_transport_timeout_peers; + cache_status m_last_cache_status; + size_type m_last_failed; + size_type m_last_redundant; + size_type m_last_uploaded; + size_type m_last_downloaded; + int m_connection_attempts; + int m_num_banned_peers; + int m_banned_for_hash_failure; + vm_statistics_data_t m_last_vm_stat; + thread_cpu_usage m_network_thread_cpu_usage; + sliding_average<20> m_read_ops; + sliding_average<20> m_write_ops;; + enum + { + on_read_counter, + on_write_counter, + on_tick_counter, + on_lsd_counter, + on_lsd_peer_counter, + on_udp_counter, + on_accept_counter, + on_disk_queue_counter, + on_disk_read_counter, + on_disk_write_counter, + max_messages + }; + int m_num_messages[max_messages]; + // 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, + // 16384, 32768, 65536, 131072, 262144, 524288, 1048576 + int m_send_buffer_sizes[18]; + int m_recv_buffer_sizes[18]; +#endif + + // each second tick the timer takes a little + // bit longer than one second to trigger. The + // extra time it took is accumulated into this + // counter. Every time it exceeds 1000, torrents + // will tick their timers 2 seconds instead of one. + // this keeps the timers more accurate over time + // as a kind of "leap second" to adjust for the + // accumulated error + boost::uint16_t m_tick_residual; + + // the number of torrents that have apply_ip_filter + // set to false. This is typically 0 + int m_non_filtered_torrents; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr create_log(std::string const& name + , int instance, bool append = true); + + void session_log(char const* fmt, ...) const; + + // this list of tracker loggers serves as tracker_callbacks when + // shutting down. This list is just here to keep them alive during + // whe shutting down process + std::list > m_tracker_loggers; + + std::string get_log_path() const + { return m_logpath; } + + std::string m_logpath; + + private: + +#endif +#ifdef TORRENT_UPNP_LOGGING + std::ofstream m_upnp_log; +#endif + + // state for keeping track of external IPs + external_ip m_external_ip; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > ses_extension_list_t; + ses_extension_list_t m_ses_extensions; +#endif + +#ifndef TORRENT_DISABLE_GEO_IP + GeoIP* m_asnum_db; + GeoIP* m_country_db; + + // maps AS number to the peak download rate + // we've seen from it. Entries are never removed + // from this map. Pointers to its elements + // are kept in the policy::peer structures. + std::map m_as_peak; +#endif + + // total redundant and failed bytes + size_type m_total_failed_bytes; + size_type m_total_redundant_bytes; + + // this is set to true when a torrent auto-manage + // event is triggered, and reset whenever the message + // is delivered and the auto-manage is executed. + // there should never be more than a single pending auto-manage + // message in-flight at any given time. + bool m_pending_auto_manage; + + // this is also set to true when triggering an auto-manage + // of the torrents. However, if the normal auto-manage + // timer comes along and executes the auto-management, + // this is set to false, which means the triggered event + // no longer needs to execute the auto-management. + bool m_need_auto_manage; + + // set to true when the session object + // is being destructed and the thread + // should exit + bool m_abort; + + // is true if the session is paused + bool m_paused; + // is false by default and set to true when + // the first incoming connection is established + // this is used to know if the client is behind + // NAT or not. + bool m_incoming_connection; + + // redundant bytes per category + size_type m_redundant_bytes[7]; + + std::vector > m_feeds; + + // this is the set of (subscribed) torrents that have changed + // their states since the last time the user requested updates. + std::vector > m_state_updates; + + // the main working thread + boost::scoped_ptr m_thread; + +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + pthread_t m_network_thread; +#endif + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct tracker_logger : request_callback + { + tracker_logger(session_impl& ses); + void tracker_warning(tracker_request const& req + , std::string const& str); + void tracker_response(tracker_request const& + , libtorrent::address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& tracker_id); + void tracker_request_timed_out( + tracker_request const&); + void tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& str + , int retry_interval); + void debug_log(const char* fmt, ...) const; + session_impl& m_ses; + }; +#endif + + } +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp new file mode 100644 index 0000000000..5316a9dea2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_limit.hpp @@ -0,0 +1,101 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED +#define TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED + +#include +#include + +#include "libtorrent/assert.hpp" + +namespace libtorrent { + +// member of peer_connection +struct TORRENT_EXTRA_EXPORT bandwidth_channel +{ + static const int inf = boost::integer_traits::const_max; + + bandwidth_channel(); + + // 0 means infinite + void throttle(int limit); + int throttle() const + { + TORRENT_ASSERT_VAL(m_limit < INT_MAX, m_limit); + return int(m_limit); + } + + int quota_left() const; + void update_quota(int dt_milliseconds); + + // this is used when connections disconnect with + // some quota left. It's returned to its bandwidth + // channels. + void return_quota(int amount); + void use_quota(int amount); + + // this is an optimization. If there is more than one second + // of quota built up in this channel, just apply it right away + // instead of introducing a delay to split it up evenly. This + // should especially help in situations where a single peer + // has a capacity under the rate limit, but would otherwise be + // held back by the latency of getting bandwidth from the limiter + bool need_queueing(int amount) + { + if (m_quota_left - amount < m_limit) return true; + m_quota_left -= amount; + return false; + } + + // used as temporary storage while distributing + // bandwidth + int tmp; + + // this is the number of bytes to distribute this round + int distribute_quota; + +private: + + // this is the amount of bandwidth we have + // been assigned without using yet. + boost::int64_t m_quota_left; + + // the limit is the number of bytes + // per second we are allowed to use. + boost::int64_t m_limit; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp new file mode 100644 index 0000000000..975ccca8fb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -0,0 +1,116 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED +#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED + +#include + +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT +#include +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/bandwidth_socket.hpp" +#include "libtorrent/ptime.hpp" + +using boost::intrusive_ptr; + + +namespace libtorrent { + +struct TORRENT_EXTRA_EXPORT bandwidth_manager +{ + bandwidth_manager(int channel +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , bool log = false +#endif + ); + + void close(); + +#if TORRENT_USE_ASSERTS + bool is_queued(bandwidth_socket const* peer) const; +#endif + + int queue_size() const; + boost::int64_t queued_bytes() const; + + // non prioritized means that, if there's a line for bandwidth, + // others will cut in front of the non-prioritized peers. + // this is used by web seeds + // returns the number of bytes to assign to the peer, or 0 + // if the peer's 'assign_bandwidth' callback will be called later + int request_bandwidth(intrusive_ptr const& peer + , int blk, int priority + , bandwidth_channel* chan1 = 0 + , bandwidth_channel* chan2 = 0 + , bandwidth_channel* chan3 = 0 + , bandwidth_channel* chan4 = 0 + , bandwidth_channel* chan5 = 0); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + void update_quotas(time_duration const& dt); + +private: + + // these are the consumers that want bandwidth + typedef std::vector queue_t; + queue_t m_queue; + // the number of bytes all the requests in queue are for + boost::int64_t m_queued_bytes; + + // this is the channel within the consumers + // that bandwidth is assigned to (upload or download) + int m_channel; + + bool m_abort; + +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + std::ofstream m_log; + ptime m_start; +#endif +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp new file mode 100644 index 0000000000..641e1ebc41 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED +#define TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED + +#include +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_socket.hpp" + +namespace libtorrent { + +struct TORRENT_EXTRA_EXPORT bw_request +{ + bw_request(boost::intrusive_ptr const& pe + , int blk, int prio); + + boost::intrusive_ptr peer; + // 1 is normal prio + int priority; + // the number of bytes assigned to this request so far + int assigned; + // once assigned reaches this, we dispatch the request function + int request_size; + + // the max number of rounds for this request to survive + // this ensures that requests gets responses at very low + // rate limits, when the requested size would take a long + // time to satisfy + int ttl; + + // loops over the bandwidth channels and assigns bandwidth + // from the most limiting one + int assign_bandwidth(); + + enum { max_bandwidth_channels = 5 }; + // we don't actually support more than 5 channels per peer + bandwidth_channel* channel[max_bandwidth_channels]; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp new file mode 100644 index 0000000000..486f8fac95 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bandwidth_socket.hpp @@ -0,0 +1,51 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED +#define TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + +#include "libtorrent/intrusive_ptr_base.hpp" + +namespace libtorrent +{ + struct bandwidth_socket + : public intrusive_ptr_base + { + virtual void assign_bandwidth(int channel, int amount) = 0; + virtual bool is_disconnecting() const = 0; + virtual ~bandwidth_socket() {} + }; +} + +#endif // TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp new file mode 100644 index 0000000000..af2a3c66ee --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bencode.hpp @@ -0,0 +1,465 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef TORRENT_BENCODE_HPP_INCLUDED +#define TORRENT_BENCODE_HPP_INCLUDED + + + +// OVERVIEW +// +// Bencoding is a common representation in bittorrent used for +// for dictionary, list, int and string hierarchies. It's used +// to encode .torrent files and some messages in the network +// protocol. libtorrent also uses it to store settings, resume +// data and other state between sessions. +// +// Strings in bencoded structures are not necessarily representing +// text. Strings are raw byte buffers of a certain length. If a +// string is meant to be interpreted as text, it is required to +// be UTF-8 encoded. See `BEP 3`_. +// +// There are two mechanims to *decode* bencoded buffers in libtorrent. +// +// The most flexible one is bdecode(), which returns a structure +// represented by entry. When a buffer is decoded with this function, +// it can be discarded. The entry does not contain any references back +// to it. This means that bdecode() actually copies all the data out +// of the buffer and into its own hierarchy. This makes this +// function potentially expensive, if you're parsing large amounts +// of data. +// +// Another consideration is that bdecode() is a recursive parser. +// For this reason, in order to avoid DoS attacks by triggering +// a stack overflow, there is a recursion limit. This limit is +// a sanity check to make sure it doesn't run the risk of +// busting the stack. +// +// The second mechanism is lazy_bdecode(), which returns a +// bencoded structure represented by lazy_entry. This function +// builds a tree that points back into the original buffer. +// The returned lazy_entry will not be valid once the buffer +// it was parsed out of is discarded. +// +// Not only is this function more efficient because of less +// memory allocation and data copy, the parser is also not +// recursive, which means it probably performs a little bit +// better and can have a higher recursion limit on the structures +// it's parsing. + +#include +#include +#include +#include // for distance + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" + +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/io.hpp" // for write_string + +namespace libtorrent +{ + +#ifndef TORRENT_NO_DEPRECATE + // thrown by bdecode() if the provided bencoded buffer does not contain + // valid encoding. + struct TORRENT_EXPORT invalid_encoding: std::exception + { + // hidden + virtual const char* what() const throw() { return "invalid bencoding"; } + }; +#endif + + namespace detail + { + // this is used in the template, so it must be available to the client + TORRENT_EXPORT char const* integer_to_str(char* buf, int size + , entry::integer_type val); + + template + int write_integer(OutIt& out, entry::integer_type val) + { + // the stack allocated buffer for keeping the + // decimal representation of the number can + // not hold number bigger than this: + BOOST_STATIC_ASSERT(sizeof(entry::integer_type) <= 8); + char buf[21]; + int ret = 0; + for (char const* str = integer_to_str(buf, 21, val); + *str != 0; ++str) + { + *out = *str; + ++out; + ++ret; + } + return ret; + } + + template + void write_char(OutIt& out, char c) + { + *out = c; + ++out; + } + + template + std::string read_until(InIt& in, InIt end, char end_token, bool& err) + { + std::string ret; + if (in == end) + { + err = true; + return ret; + } + while (*in != end_token) + { + ret += *in; + ++in; + if (in == end) + { + err = true; + return ret; + } + } + return ret; + } + + template + void read_string(InIt& in, InIt end, int len, std::string& str, bool& err) + { + TORRENT_ASSERT(len >= 0); + for (int i = 0; i < len; ++i) + { + if (in == end) + { + err = true; + return; + } + str += *in; + ++in; + } + } + + template + int bencode_recursive(OutIt& out, const entry& e) + { + int ret = 0; + switch(e.type()) + { + case entry::int_t: + write_char(out, 'i'); + ret += write_integer(out, e.integer()); + write_char(out, 'e'); + ret += 2; + break; + case entry::string_t: + ret += write_integer(out, e.string().length()); + write_char(out, ':'); + ret += write_string(e.string(), out); + ret += 1; + break; + case entry::list_t: + write_char(out, 'l'); + for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i) + ret += bencode_recursive(out, *i); + write_char(out, 'e'); + ret += 2; + break; + case entry::dictionary_t: + write_char(out, 'd'); + for (entry::dictionary_type::const_iterator i = e.dict().begin(); + i != e.dict().end(); ++i) + { + // write key + ret += write_integer(out, i->first.length()); + write_char(out, ':'); + ret += write_string(i->first, out); + // write value + ret += bencode_recursive(out, i->second); + ret += 1; + } + write_char(out, 'e'); + ret += 2; + break; + default: + // trying to encode a structure with uninitialized values! + TORRENT_ASSERT_VAL(false, e.type()); + // do nothing + break; + } + return ret; + } + + template + void bdecode_recursive(InIt& in, InIt end, entry& ret, bool& err, int depth) + { + if (depth >= 100) + { + err = true; + return; + } + + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + switch (*in) + { + + // ---------------------------------------------- + // integer + case 'i': + { + ++in; // 'i' + std::string val = read_until(in, end, 'e', err); + if (err) return; + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + ret = entry(entry::int_t); + char* end_pointer; + ret.integer() = strtoll(val.c_str(), &end_pointer, 10); +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + if (end_pointer == val.c_str()) + { + err = true; + return; + } + } break; + + // ---------------------------------------------- + // list + case 'l': + { + ret = entry(entry::list_t); + ++in; // 'l' + while (*in != 'e') + { + ret.list().push_back(entry()); + entry& e = ret.list().back(); + bdecode_recursive(in, end, e, err, depth + 1); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // dictionary + case 'd': + { + ret = entry(entry::dictionary_t); + ++in; // 'd' + while (*in != 'e') + { + entry key; + bdecode_recursive(in, end, key, err, depth + 1); + if (err || key.type() != entry::string_t) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + entry& e = ret[key.string()]; + bdecode_recursive(in, end, e, err, depth + 1); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + if (in == end) + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + TORRENT_ASSERT(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // string + default: + if (is_digit((unsigned char)*in)) + { + std::string len_s = read_until(in, end, ':', err); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + TORRENT_ASSERT(*in == ':'); + ++in; // ':' + int len = atoi(len_s.c_str()); + ret = entry(entry::string_t); + read_string(in, end, len, ret.string(), err); + if (err) + { +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } + } + else + { + err = true; +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + return; + } +#ifdef TORRENT_DEBUG + ret.m_type_queried = false; +#endif + } + } + } + + // These functions will encode data to bencoded_ or decode bencoded_ data. + // + // If possible, lazy_bdecode() should be preferred over ``bdecode()``. + // + // The entry_ class is the internal representation of the bencoded data + // and it can be used to retrieve information, an entry_ can also be build by + // the program and given to ``bencode()`` to encode it into the ``OutIt`` + // iterator. + // + // The ``OutIt`` and ``InIt`` are iterators + // (InputIterator_ and OutputIterator_ respectively). They + // are templates and are usually instantiated as ostream_iterator_, + // back_insert_iterator_ or istream_iterator_. These + // functions will assume that the iterator refers to a character + // (``char``). So, if you want to encode entry ``e`` into a buffer + // in memory, you can do it like this:: + // + // std::vector buffer; + // bencode(std::back_inserter(buf), e); + // + // .. _InputIterator: http://www.sgi.com/tech/stl/InputIterator.html + // .. _OutputIterator: http://www.sgi.com/tech/stl/OutputIterator.html + // .. _ostream_iterator: http://www.sgi.com/tech/stl/ostream_iterator.html + // .. _back_insert_iterator: http://www.sgi.com/tech/stl/back_insert_iterator.html + // .. _istream_iterator: http://www.sgi.com/tech/stl/istream_iterator.html + // + // If you want to decode a torrent file from a buffer in memory, you can do it like this:: + // + // std::vector buffer; + // // ... + // entry e = bdecode(buf.begin(), buf.end()); + // + // Or, if you have a raw char buffer:: + // + // const char* buf; + // // ... + // entry e = bdecode(buf, buf + data_size); + // + // Now we just need to know how to retrieve information from the entry. + // + // If ``bdecode()`` encounters invalid encoded data in the range given to it + // it will return a default constructed ``entry`` object. + template int bencode(OutIt out, const entry& e) + { + return detail::bencode_recursive(out, e); + } + template entry bdecode(InIt start, InIt end) + { + entry e; + bool err = false; + detail::bdecode_recursive(start, end, e, err, 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(e.m_type_queried == false); +#endif + if (err) return entry(); + return e; + } + template entry bdecode(InIt start, InIt end, int& len) + { + entry e; + bool err = false; + InIt s = start; + detail::bdecode_recursive(start, end, e, err, 0); + len = std::distance(s, start); + TORRENT_ASSERT(len >= 0); + if (err) return entry(); + return e; + } +} + +#endif // TORRENT_BENCODE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp new file mode 100644 index 0000000000..2fbab64a98 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bitfield.hpp @@ -0,0 +1,360 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BITFIELD_HPP_INCLUDED +#define TORRENT_BITFIELD_HPP_INCLUDED + +#include "libtorrent/assert.hpp" +#include "libtorrent/config.hpp" +#include // for memset and memcpy +#include // for malloc, free and realloc +#include // uint32_t +#include // for min() + +namespace libtorrent +{ + // The bitfiled type stores any number of bits as a bitfield + // in a heap allocated or borrowed array. + struct TORRENT_EXPORT bitfield + { + // constructs a new bitfield. The default constructor creates an empty + // bitfield. ``bits`` is the size of the bitfield (specified in bits). + // ``val`` is the value to initialize the bits to. If not specified + // all bits are initialized to 0. + // + // The constructor taking a pointer ``b`` and ``bits`` copies a bitfield + // from the specified buffer, and ``bits`` number of bits (rounded up to + // the nearest byte boundry). + bitfield(): m_bytes(0), m_size(0), m_own(false) {} + bitfield(int bits): m_bytes(0), m_size(0), m_own(false) + { resize(bits); } + bitfield(int bits, bool val): m_bytes(0), m_size(0), m_own(false) + { resize(bits, val); } + bitfield(char const* b, int bits): m_bytes(0), m_size(0), m_own(false) + { assign(b, bits); } + bitfield(bitfield const& rhs): m_bytes(0), m_size(0), m_own(false) + { assign(rhs.bytes(), rhs.size()); } +#if __cplusplus > 199711L + bitfield(bitfield&& rhs): m_bytes(rhs.m_bytes), m_size(rhs.m_size), m_own(rhs.m_own) + { rhs.m_bytes = NULL; } +#endif + + // assigns a bitfield pointed to ``b`` of ``bits`` number of bits, without + // taking ownership of the buffer. This is a way to avoid copying data and + // yet provide a raw buffer to functions that may operate on the bitfield + // type. It is the user's responsibility to make sure the passed-in buffer's + // life time exceeds all uses of the bitfield. + void borrow_bytes(char* b, int bits) + { + dealloc(); + m_bytes = (unsigned char*)b; + m_size = bits; + m_own = false; + } + + // hidden + ~bitfield() { dealloc(); } + + // copy bitfield from buffer ``b`` of ``bits`` number of bits, rounded up to + // the nearest byte boundary. + void assign(char const* b, int bits) + { resize(bits); std::memcpy(m_bytes, b, (bits + 7) / 8); clear_trailing_bits(); } + + // query bit at ``index``. Returns true if bit is 1, otherwise false. + bool operator[](int index) const + { return get_bit(index); } + bool get_bit(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + return (m_bytes[index / 8] & (0x80 >> (index & 7))) != 0; + } + + // set bit at ``index`` to 0 (clear_bit) or 1 (set_bit). + void clear_bit(int index) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + m_bytes[index / 8] &= ~(0x80 >> (index & 7)); + } + void set_bit(int index) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_size); + m_bytes[index / 8] |= (0x80 >> (index & 7)); + } + + // returns true if all bits in the bitfield are set + bool all_set() const + { + boost::uint8_t* bytes = m_bytes; + int num_bytes = m_size / 8; + int num_words = 0; + + // head + if (num_bytes >= 4) + { + switch (uintptr_t(bytes) & 0x3) + { + case 0: break; + case 1: + if (bytes[0] != 0xff) return false; + if (bytes[1] != 0xff) return false; + if (bytes[2] != 0xff) return false; + bytes += 3; + num_bytes -= 3; + break; + case 2: + if (bytes[0] != 0xff) return false; + if (bytes[1] != 0xff) return false; + bytes += 2; + num_bytes -= 2; + break; + case 3: + if (bytes[0] != 0xff) return false; + ++bytes; + --num_bytes; + break; + } + + num_words = num_bytes / 4; + + TORRENT_ASSERT((uintptr_t(bytes) & 0x3) == 0); + boost::uint32_t* words = (boost::uint32_t*)bytes; + for (int i = 0; i < num_words; ++i) + { + if (words[i] != 0xffffffff) return false; + } + } + + // tail + for (int i = num_words * 4; i < num_bytes; ++i) + { + if (bytes[i] != 0xff) return false; + } + int rest = m_size & 0x7; + if (rest > 0) + { + boost::uint8_t mask = (0xff << (8-rest)) & 0xff; + if ((bytes[num_bytes] & mask) != mask) return false; + } + return true; + } + + // returns the size of the bitfield in bits. + int size() const { return m_size; } + + // returns true if the bitfield has zero size. + bool empty() const { return m_size == 0; } + + // returns a pointer to the internal buffer of the bitfield. + char const* bytes() const { return (char*)m_bytes; } + + // copy operator + bitfield& operator=(bitfield const& rhs) + { + assign(rhs.bytes(), rhs.size()); + return *this; + } + + // count the number of bits in the bitfield that are set to 1. + int count() const + { + // 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, + // 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111 + const static char num_bits[] = + { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 + }; + + int ret = 0; + const int num_bytes = m_size / 8; + for (int i = 0; i < num_bytes; ++i) + { + ret += num_bits[m_bytes[i] & 0xf] + num_bits[m_bytes[i] >> 4]; + } + + int rest = m_size - num_bytes * 8; + for (int i = 0; i < rest; ++i) + { + ret += (m_bytes[num_bytes] >> (7-i)) & 1; + } + TORRENT_ASSERT(ret <= m_size); + TORRENT_ASSERT(ret >= 0); + return ret; + } + + struct const_iterator + { + friend struct bitfield; + + typedef bool value_type; + typedef ptrdiff_t difference_type; + typedef bool const* pointer; + typedef bool& reference; + typedef std::forward_iterator_tag iterator_category; + + bool operator*() { return (*byte & bit) != 0; } + const_iterator& operator++() { inc(); return *this; } + const_iterator operator++(int) + { const_iterator ret(*this); inc(); return ret; } + const_iterator& operator--() { dec(); return *this; } + const_iterator operator--(int) + { const_iterator ret(*this); dec(); return ret; } + + const_iterator(): byte(0), bit(0x80) {} + bool operator==(const_iterator const& rhs) const + { return byte == rhs.byte && bit == rhs.bit; } + + bool operator!=(const_iterator const& rhs) const + { return byte != rhs.byte || bit != rhs.bit; } + + private: + void inc() + { + TORRENT_ASSERT(byte); + if (bit == 0x01) + { + bit = 0x80; + ++byte; + } + else + { + bit >>= 1; + } + } + void dec() + { + TORRENT_ASSERT(byte); + if (bit == 0x80) + { + bit = 0x01; + --byte; + } + else + { + bit <<= 1; + } + } + const_iterator(unsigned char const* ptr, int offset) + : byte(ptr), bit(0x80 >> offset) {} + unsigned char const* byte; + int bit; + }; + + const_iterator begin() const { return const_iterator(m_bytes, 0); } + const_iterator end() const { return const_iterator(m_bytes + m_size / 8, m_size & 7); } + + // set the size of the bitfield to ``bits`` length. If the bitfield is extended, + // the new bits are initialized to ``val``. + void resize(int bits, bool val) + { + int s = m_size; + int b = m_size & 7; + resize(bits); + if (s >= m_size) return; + int old_size_bytes = (s + 7) / 8; + int new_size_bytes = (m_size + 7) / 8; + if (val) + { + if (old_size_bytes && b) m_bytes[old_size_bytes - 1] |= (0xff >> b); + if (old_size_bytes < new_size_bytes) + std::memset(m_bytes + old_size_bytes, 0xff, new_size_bytes - old_size_bytes); + clear_trailing_bits(); + } + else + { + if (old_size_bytes < new_size_bytes) + std::memset(m_bytes + old_size_bytes, 0x00, new_size_bytes - old_size_bytes); + } + } + void resize(int bits) + { + TORRENT_ASSERT(bits >= 0); + const int b = (bits + 7) / 8; + if (m_bytes) + { + if (m_own) + { + m_bytes = (unsigned char*)std::realloc(m_bytes, b); + m_own = true; + } + else if (bits > m_size) + { + unsigned char* tmp = (unsigned char*)std::malloc(b); + std::memcpy(tmp, m_bytes, (std::min)(int(m_size + 7)/ 8, b)); + m_bytes = tmp; + m_own = true; + } + } + else if (bits > 0) + { + m_bytes = (unsigned char*)std::malloc(b); + m_own = true; + } + m_size = bits; + clear_trailing_bits(); + } + + // set all bits in the bitfield to 1 (set_all) or 0 (clear_all). + void set_all() + { + std::memset(m_bytes, 0xff, (m_size + 7) / 8); + clear_trailing_bits(); + } + void clear_all() + { + std::memset(m_bytes, 0x00, (m_size + 7) / 8); + } + + // make the bitfield empty, of zero size. + void clear() { dealloc(); m_size = 0; } + + private: + + void clear_trailing_bits() + { + // clear the tail bits in the last byte + if (m_size & 7) m_bytes[(m_size + 7) / 8 - 1] &= 0xff << (8 - (m_size & 7)); + } + + void dealloc() { if (m_own) std::free(m_bytes); m_bytes = 0; } + unsigned char* m_bytes; + int m_size:31; // in bits + bool m_own:1; + }; + +} + +#endif // TORRENT_BITFIELD_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp new file mode 100644 index 0000000000..b93741ec15 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bloom_filter.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BLOOM_FILTER_HPP_INCLUDED +#define TORRENT_BLOOM_FILTER_HPP_INCLUDED + +#include +#include "libtorrent/peer_id.hpp" // for sha1_hash +#include "libtorrent/config.hpp" // for sha1_hash + +#include // for log() + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT void set_bits(boost::uint8_t const* b, boost::uint8_t* bits, int len); + TORRENT_EXTRA_EXPORT bool has_bits(boost::uint8_t const* b, boost::uint8_t const* bits, int len); + TORRENT_EXTRA_EXPORT int count_zero_bits(boost::uint8_t const* bits, int len); + + template + struct bloom_filter + { + bool find(sha1_hash const& k) const + { return has_bits(&k[0], bits, N); } + + void set(sha1_hash const& k) + { set_bits(&k[0], bits, N); } + + std::string to_string() const + { return std::string((char const*)&bits[0], N); } + + void from_string(char const* str) + { memcpy(bits, str, N); } + + void clear() { memset(bits, 0, N); } + + float size() const + { + const int c = (std::min)(count_zero_bits(bits, N), (N * 8) - 1); + const int m = N * 8; + return ::log(c / float(m)) / (2.f * ::log(1.f - 1.f/m)); + } + + bloom_filter() { clear(); } + + private: + boost::uint8_t bits[N]; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp new file mode 100644 index 0000000000..a2d4a609a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/broadcast_socket.hpp @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BROADCAST_SOCKET_HPP_INCLUDED +#define TORRENT_BROADCAST_SOCKET_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/error_code.hpp" +#include +#include +#include + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT bool is_local(address const& a); + TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr); + TORRENT_EXTRA_EXPORT bool is_multicast(address const& addr); + TORRENT_EXTRA_EXPORT bool is_any(address const& addr); + TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr); + TORRENT_EXTRA_EXPORT int cidr_distance(address const& a1, address const& a2); + + // determines if the operating system supports IPv6 + TORRENT_EXTRA_EXPORT bool supports_ipv6(); + + TORRENT_EXTRA_EXPORT int common_bits(unsigned char const* b1 + , unsigned char const* b2, int n); + + TORRENT_EXTRA_EXPORT address guess_local_address(io_service&); + + typedef boost::function receive_handler_t; + + class TORRENT_EXTRA_EXPORT broadcast_socket + { + public: + broadcast_socket(udp::endpoint const& multicast_endpoint + , receive_handler_t const& handler); + ~broadcast_socket() { close(); } + + void open(io_service& ios, error_code& ec, bool loopback = true); + + enum flags_t { broadcast = 1 }; + void send(char const* buffer, int size, error_code& ec, int flags = 0); + + void close(); + int num_send_sockets() const { return m_unicast_sockets.size(); } + void enable_ip_broadcast(bool e); + + private: + + struct socket_entry + { + socket_entry(boost::shared_ptr const& s) + : socket(s), broadcast(false) {} + socket_entry(boost::shared_ptr const& s + , address_v4 const& mask): socket(s), netmask(mask), broadcast(false) {} + boost::shared_ptr socket; + char buffer[1500]; + udp::endpoint remote; + address_v4 netmask; + bool broadcast; + void close() + { + if (!socket) return; + error_code ec; + socket->close(ec); + } + bool can_broadcast() const + { + error_code ec; + return broadcast + && netmask != address_v4() + && socket->local_endpoint(ec).address().is_v4(); + } + address_v4 broadcast_address() const + { + error_code ec; +#if BOOST_VERSION < 104700 + return address_v4(socket->local_endpoint(ec).address().to_v4().to_ulong() | ((~netmask.to_ulong()) & 0xffffffff)); +#else + return address_v4::broadcast(socket->local_endpoint(ec).address().to_v4(), netmask); +#endif + } + }; + + void on_receive(socket_entry* s, error_code const& ec + , std::size_t bytes_transferred); + void open_unicast_socket(io_service& ios, address const& addr + , address_v4 const& mask); + void open_multicast_socket(io_service& ios, address const& addr + , bool loopback, error_code& ec); + + // if we're aborting, destruct the handler and return true + bool maybe_abort(); + + // these sockets are used to + // join the multicast group (on each interface) + // and receive multicast messages + std::list m_sockets; + // these sockets are not bound to any + // specific port and are used to + // send messages to the multicast group + // and receive unicast responses + std::list m_unicast_sockets; + udp::endpoint m_multicast_endpoint; + receive_handler_t m_on_receive; + + // the number of outstanding async operations + // we have on these sockets. The m_on_receive + // handler may not be destructed until this reaches + // 0, since it may be holding references to + // the broadcast_socket itself. + int m_outstanding_operations; + // when set to true, we're trying to shut down + // don't initiate new operations and once the + // outstanding counter reaches 0, destruct + // the handler object + bool m_abort; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp new file mode 100644 index 0000000000..e8b7b618ef --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -0,0 +1,461 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +Copyright (c) 2007-2014, Arvid Norberg, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/pe_crypto.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT bt_peer_connection + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + bt_peer_connection( + aux::session_impl& ses + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , peer_id const& pid + , boost::weak_ptr t = boost::weak_ptr() + , bool outgoing = false); + + void start(); + + enum + { + // pex_msg = 1, + // metadata_msg = 2, + upload_only_msg = 3, + holepunch_msg = 4, + // recommend_msg = 5, + // comment_msg = 6, + dont_have_msg = 7, + share_mode_msg = 8 + }; + + ~bt_peer_connection(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool supports_encryption() const + { return m_encrypted; } + bool rc4_encrypted() const + { return m_rc4_encrypted; } +#endif + + virtual int type() const { return peer_connection::bittorrent_connection; } + + enum message_type + { + // standard messages + msg_choke = 0, + msg_unchoke, + msg_interested, + msg_not_interested, + msg_have, + msg_bitfield, + msg_request, + msg_piece, + msg_cancel, + // DHT extension + msg_dht_port, + // FAST extension + msg_suggest_piece = 0xd, + msg_have_all, + msg_have_none, + msg_reject_request, + msg_allowed_fast, + + // extension protocol message + msg_extended = 20, + + num_supported_messages + }; + + enum hp_message_t + { + // msg_types + hp_rendezvous = 0, + hp_connect = 1, + hp_failed = 2, + + // error codes + hp_no_such_peer = 1, + hp_not_connected = 2, + hp_no_support = 3, + hp_no_self = 4 + }; + + // called from the main loop when this connection has any + // work to do. + + void on_sent(error_code const& error + , std::size_t bytes_transferred); + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + virtual void get_specific_peer_info(peer_info& p) const; + virtual bool in_handshake() const; + +#ifndef TORRENT_DISABLE_EXTENSIONS + bool supports_holepunch() const { return m_holepunch_id != 0; } +#endif + + bool support_extensions() const { return m_supports_extensions; } + + // the message handlers are called + // each time a recv() returns some new + // data, the last time it will be called + // is when the entire packet has been + // received, then it will no longer + // be called. i.e. most handlers need + // to check how much of the packet they + // have received before any processing + void on_keepalive(); + void on_choke(int received); + void on_unchoke(int received); + void on_interested(int received); + void on_not_interested(int received); + void on_have(int received); + void on_bitfield(int received); + void on_request(int received); + void on_piece(int received); + void on_cancel(int received); + + // DHT extension + void on_dht_port(int received); + + // FAST extension + void on_suggest_piece(int received); + void on_have_all(int received); + void on_have_none(int received); + void on_reject_request(int received); + void on_allowed_fast(int received); +#ifndef TORRENT_DISABLE_EXTENSIONS + void on_holepunch(); + + void on_extended(int received); + + void on_extended_handshake(); +#endif + + typedef void (bt_peer_connection::*message_handler)(int received); + + // the following functions appends messages + // to the send buffer + void write_choke(); + void write_unchoke(); + void write_interested(); + void write_not_interested(); + void write_request(peer_request const& r); + void write_cancel(peer_request const& r); + void write_bitfield(); + void write_have(int index); + void write_piece(peer_request const& r, disk_buffer_holder& buffer); + void write_handshake(); +#ifndef TORRENT_DISABLE_EXTENSIONS + void write_extensions(); + void write_upload_only(); + void write_share_mode(); + void write_holepunch_msg(int type, tcp::endpoint const& ep, int error); +#endif + void write_metadata(std::pair req); + void write_metadata_request(std::pair req); + void write_keepalive(); + + // DHT extension + void write_dht_port(int listen_port); + + // FAST extension + void write_have_all(); + void write_have_none(); + void write_reject_request(peer_request const&); + void write_allow_fast(int piece); + void write_suggest(int piece); + + void on_connected(); + void on_metadata(); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; + ptime m_last_choke; +#endif + + private: + + bool dispatch_message(int received); + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + +#ifndef TORRENT_DISABLE_ENCRYPTION + + // if (is_local()), we are 'a' otherwise 'b' + // + // 1. a -> b dhkey, pad + // 2. b -> a dhkey, pad + // 3. a -> b sync, payload + // 4. b -> a sync, payload + // 5. a -> b payload + + void write_pe1_2_dhkey(); + void write_pe3_sync(); + void write_pe4_sync(int crypto_select); + + void write_pe_vc_cryptofield(char* write_buf, int len + , int crypto_field, int pad_size); + + // stream key (info hash of attached torrent) + // secret is the DH shared secret + // initializes m_enc_handler + void init_pe_rc4_handler(char const* secret, sha1_hash const& stream_key); + + // Returns offset at which bytestream (src, src + src_size) + // matches bytestream(target, target + target_size). + // If no sync found, return -1 + int get_syncoffset(char const* src, int src_size + , char const* target, int target_size) const; +#endif + +public: + + // these functions encrypt the send buffer if m_rc4_encrypted + // is true, otherwise it passes the call to the + // peer_connection functions of the same names + virtual void append_const_send_buffer(char const* buffer, int size); + virtual void send_buffer(char const* begin, int size, int flags = 0 + , void (*fun)(char*, int, void*) = 0, void* userdata = 0); + template + void bt_append_send_buffer(char* buffer, int size, Destructor const& destructor) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_rc4_encrypted) + m_enc_handler->encrypt(buffer, size); +#endif + peer_connection::append_send_buffer(buffer, size, destructor, true); + } + +private: + + enum state + { +#ifndef TORRENT_DISABLE_ENCRYPTION + read_pe_dhkey = 0, + read_pe_syncvc, + read_pe_synchash, + read_pe_skey_vc, + read_pe_cryptofield, + read_pe_pad, + read_pe_ia, + init_bt_handshake, + read_protocol_identifier, +#else + read_protocol_identifier = 0, +#endif + read_info_hash, + read_peer_id, + + // handshake complete + read_packet_size, + read_packet + }; + +#ifndef TORRENT_DISABLE_ENCRYPTION + enum + { + handshake_len = 68, + dh_key_len = 96 + }; +#endif + + // state of on_receive + boost::uint8_t m_state; + + // this is set to true if the handshake from + // the peer indicated that it supports the + // extension protocol + bool m_supports_extensions:1; + bool m_supports_dht_port:1; + bool m_supports_fast:1; + +#if TORRENT_USE_ASSERTS + // this is set to true when the client's + // bitfield is sent to this peer + bool m_sent_bitfield:1; + + bool m_in_constructor:1; + + bool m_sent_handshake:1; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is set to true after the encryption method has been + // succesfully negotiated (either plaintext or rc4), to signal + // automatic encryption/decryption. + bool m_encrypted:1; + + // true if rc4, false if plaintext + bool m_rc4_encrypted:1; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + // the message ID for upload only message + // 0 if not supported + boost::uint8_t m_upload_only_id; + + // the message ID for holepunch messages + boost::uint8_t m_holepunch_id; +#endif + + std::string m_client_version; + + static const message_handler m_message_handler[num_supported_messages]; + + // the peer ID we advertise for ourself + peer_id m_our_peer_id; + + // this is a queue of ranges that describes + // where in the send buffer actual payload + // data is located. This is currently + // only used to be able to gather statistics + // seperately on payload and protocol data. + struct range + { + range(int s, int l) + : start(s) + , length(l) + { + TORRENT_ASSERT(s >= 0); + TORRENT_ASSERT(l > 0); + } + int start; + int length; + }; + static bool range_below_zero(const range& r) + { return r.start < 0; } + std::vector m_payloads; + + // we have suggested these pieces to the peer + // don't suggest it again + bitfield m_sent_suggested_pieces; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // initialized during write_pe1_2_dhkey, and destroyed on + // creation of m_enc_handler. Cannot reinitialize once + // initialized. + boost::scoped_ptr m_dh_key_exchange; + + // if encryption is negotiated, this is used for + // encryption/decryption during the entire session. Destroyed + // if plaintext is selected + boost::scoped_ptr m_enc_handler; + + // (outgoing only) synchronize verification constant with + // remote peer, this will hold rc4_decrypt(vc). Destroyed + // after the sync step. + boost::scoped_array m_sync_vc; + + // (incoming only) synchronize hash with remote peer, holds + // the sync hash (hash("req1",secret)). Destroyed after the + // sync step. + boost::scoped_ptr m_sync_hash; + + // used to disconnect peer if sync points are not found within + // the maximum number of bytes + int m_sync_bytes_read; +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_DISABLE_EXTENSIONS + // the message ID for don't-have message + boost::uint8_t m_dont_have_id; + + // the message ID for share mode message + // 0 if not supported + boost::uint8_t m_share_mode_id; + + // the reserved bits received from the other peer + // in the bittorrent handshake + char m_reserved_bits[8]; +#endif + }; +} + +#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp new file mode 100644 index 0000000000..657ea355fd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/buffer.hpp @@ -0,0 +1,224 @@ +/* +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of Rasterbar Software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_BUFFER_HPP +#define LIBTORRENT_BUFFER_HPP + +#include +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/assert.hpp" +#include // malloc/free/realloc + +namespace libtorrent { + +class buffer +{ +public: + struct interval + { + interval() + : begin(0) + , end(0) + {} + + interval(char* b, char* e) + : begin(b) + , end(e) + {} + + char operator[](int index) const + { + TORRENT_ASSERT(begin + index < end); + return begin[index]; + } + + int left() const + { + TORRENT_ASSERT(end >= begin); + TORRENT_ASSERT(end - begin < INT_MAX); + return int(end - begin); + } + + char* begin; + char* end; + }; + + struct const_interval + { + const_interval(interval const& i) + : begin(i.begin) + , end(i.end) + {} + + const_interval(char const* b, char const* e) + : begin(b) + , end(e) + {} + + char operator[](int index) const + { + TORRENT_ASSERT(begin + index < end); + return begin[index]; + } + + bool operator==(const const_interval& p_interval) + { + return begin == p_interval.begin + && end == p_interval.end; + } + + int left() const + { + TORRENT_ASSERT(end >= begin); + TORRENT_ASSERT(end - begin < INT_MAX); + return int(end - begin); + } + + char const* begin; + char const* end; + }; + + buffer(std::size_t n = 0) + : m_begin(0) + , m_end(0) + , m_last(0) + { + if (n) resize(n); + } + + buffer(buffer const& b) + : m_begin(0) + , m_end(0) + , m_last(0) + { + if (b.size() == 0) return; + resize(b.size()); + std::memcpy(m_begin, b.begin(), b.size()); + } + +#if __cplusplus > 199711L + buffer(buffer&& b): m_begin(b.m_begin), m_end(b.m_end), m_last(b.m_last) + { b.m_begin = b.m_end = b.m_last = NULL; } +#endif + + buffer& operator=(buffer const& b) + { + if (&b == this) return *this; + resize(b.size()); + if (b.size() == 0) return *this; + std::memcpy(m_begin, b.begin(), b.size()); + return *this; + } + + ~buffer() + { + std::free(m_begin); + } + + buffer::interval data() { return interval(m_begin, m_end); } + buffer::const_interval data() const { return const_interval(m_begin, m_end); } + + void resize(std::size_t n) + { + reserve(n); + m_end = m_begin + n; + } + + void insert(char* point, char const* first, char const* last) + { + std::size_t p = point - m_begin; + if (point == m_end) + { + resize(size() + last - first); + std::memcpy(m_begin + p, first, last - first); + return; + } + + resize(size() + last - first); + std::memmove(m_begin + p + (last - first), m_begin + p, last - first); + std::memcpy(m_begin + p, first, last - first); + } + + void erase(char* b, char* e) + { + TORRENT_ASSERT(e <= m_end); + TORRENT_ASSERT(b >= m_begin); + TORRENT_ASSERT(b <= e); + if (e == m_end) + { + resize(b - m_begin); + return; + } + std::memmove(b, e, m_end - e); + m_end = b + (m_end - e); + } + + void clear() { m_end = m_begin; } + std::size_t size() const { return m_end - m_begin; } + std::size_t capacity() const { return m_last - m_begin; } + void reserve(std::size_t n) + { + if (n <= capacity()) return; + TORRENT_ASSERT(n > 0); + + std::size_t s = size(); + m_begin = (char*)std::realloc(m_begin, n); + m_end = m_begin + s; + m_last = m_begin + n; + } + + bool empty() const { return m_begin == m_end; } + char& operator[](std::size_t i) { TORRENT_ASSERT(i < size()); return m_begin[i]; } + char const& operator[](std::size_t i) const { TORRENT_ASSERT(i < size()); return m_begin[i]; } + + char* begin() { return m_begin; } + char const* begin() const { return m_begin; } + char* end() { return m_end; } + char const* end() const { return m_end; } + + void swap(buffer& b) + { + using std::swap; + swap(m_begin, b.m_begin); + swap(m_end, b.m_end); + swap(m_last, b.m_last); + } +private: + char* m_begin; // first + char* m_end; // one passed end of size + char* m_last; // one passed end of allocation +}; + + +} + +#endif // LIBTORRENT_BUFFER_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp new file mode 100644 index 0000000000..510a01a333 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/build_config.hpp @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BUILD_CONFIG_HPP_INCLUDED +#define TORRENT_BUILD_CONFIG_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include +#include + +#if TORRENT_USE_BOOST_DATE_TIME +#define TORRENT_CFG_TIME boosttime_ +#elif TORRENT_USE_ABSOLUTE_TIME +#define TORRENT_CFG_TIME absolutetime_ +#elif TORRENT_USE_QUERY_PERFORMANCE_TIMER +#define TORRENT_CFG_TIME performancetimer_ +#elif TORRENT_USE_CLOCK_GETTIME +#define TORRENT_CFG_TIME clocktime_ +#elif TORRENT_USE_SYSTEM_TIME +#define TORRENT_CFG_TIME systime_ +#else +#error what timer is used? +#endif + +#if TORRENT_USE_IPV6 +#define TORRENT_CFG_IPV6 ipv6_ +#else +#define TORRENT_CFG_IPV6 noipv_- +#endif + +#ifdef TORRENT_NO_DEPRECATE +#define TORRENT_CFG_DEPR nodeprecate_ +#else +#define TORRENT_CFG_DEPR deprecated_ +#endif + +#define TORRENT_CFG \ + BOOST_PP_CAT(TORRENT_CFG_TIME, \ + TORRENT_CFG_DEPR) + +#define TORRENT_CFG_STRING BOOST_PP_STRINGIZE(TORRENT_CFG) + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp new file mode 100644 index 0000000000..7fd5bf437b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/chained_buffer.hpp @@ -0,0 +1,124 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CHAINED_BUFFER_HPP_INCLUDED +#define TORRENT_CHAINED_BUFFER_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#include +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif +#include +#include // for memcpy + +namespace libtorrent +{ +#if BOOST_VERSION >= 103500 + namespace asio = boost::asio; +#endif + struct TORRENT_EXTRA_EXPORT chained_buffer + { + chained_buffer(): m_bytes(0), m_capacity(0) + { +#if TORRENT_USE_ASSERTS + m_destructed = false; +#endif + } + + struct buffer_t + { + boost::function free; // destructs the buffer + char* buf; // the first byte of the buffer + char* start; // the first byte to send/receive in the buffer + int size; // the total size of the buffer + int used_size; // this is the number of bytes to send/receive + }; + + bool empty() const { return m_bytes == 0; } + int size() const { return m_bytes; } + int capacity() const { return m_capacity; } + + void pop_front(int bytes_to_pop); + + void append_buffer(char* buffer, int s, int used_size + , boost::function const& destructor); + + // returns the number of bytes available at the + // end of the last chained buffer. + int space_in_last_buffer(); + + // tries to copy the given buffer to the end of the + // last chained buffer. If there's not enough room + // it returns false + char* append(char const* buf, int s); + + // tries to allocate memory from the end + // of the last buffer. If there isn't + // enough room, returns 0 + char* allocate_appendix(int s); + + std::list const& build_iovec(int to_send); + + ~chained_buffer(); + + private: + + // this is the list of all the buffers we want to + // send + std::list m_vec; + + // this is the vector of buffers used when + // invoking the async write call + std::list m_tmp_vec; + + // this is the number of bytes in the send buf. + // this will always be equal to the sum of the + // size of all buffers in vec + int m_bytes; + + // the total size of all buffers in the chain + // including unused space + int m_capacity; + +#if TORRENT_USE_ASSERTS + bool m_destructed; +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp new file mode 100644 index 0000000000..51a8a7b934 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/config.hpp @@ -0,0 +1,572 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONFIG_HPP_INCLUDED +#define TORRENT_CONFIG_HPP_INCLUDED + +#if !defined _MSC_VER || _MSC_VER >= 1600 +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS 1 +#endif +#endif + +#include +#include +#include +#include // for snprintf +#include // for IOV_MAX + +#include "libtorrent/export.hpp" + +#if defined TORRENT_DEBUG_BUFFERS && !defined TORRENT_DISABLE_POOL_ALLOCATOR +#error TORRENT_DEBUG_BUFFERS only works if you also disable pool allocators with TORRENT_DISABLE_POOL_ALLOCATOR +#endif + +#if !defined BOOST_ASIO_SEPARATE_COMPILATION && !defined BOOST_ASIO_DYN_LINK +#define BOOST_ASIO_SEPARATE_COMPILATION +#endif + +#ifndef _MSC_VER +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif +#include // for PRId64 et.al. +#endif + +#ifndef PRId64 +// MinGW uses microsofts runtime +#if defined _MSC_VER || defined __MINGW32__ +#define PRId64 "I64d" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIu32 "u" +#else +#define PRId64 "lld" +#define PRIu64 "llu" +#define PRIx64 "llx" +#define PRIu32 "u" +#endif +#endif + +// ======= GCC ========= + +#if defined __GNUC__ + +# if __GNUC__ >= 3 +# define TORRENT_DEPRECATED __attribute__ ((deprecated)) +# endif + +// ======= SUNPRO ========= + +#elif defined __SUNPRO_CC + +// SunPRO seems to have an overly-strict +// definition of POD types and doesn't +// seem to allow boost::array in unions +#define TORRENT_BROKEN_UNIONS 1 + +// ======= MSVC ========= + +#elif defined BOOST_MSVC + +#pragma warning(disable: 4258) +#pragma warning(disable: 4251) + +// class X needs to have dll-interface to be used by clients of class Y +#pragma warning(disable:4251) +// '_vsnprintf': This function or variable may be unsafe +#pragma warning(disable:4996) + +#define TORRENT_DEPRECATED_PREFIX __declspec(deprecated) + +#endif + + +// ======= PLATFORMS ========= + + +// set up defines for target environments +// ==== AMIGA === +#if defined __AMIGA__ || defined __amigaos__ || defined __AROS__ +#define TORRENT_AMIGA +#define TORRENT_USE_MLOCK 0 +#define TORRENT_USE_WRITEV 0 +#define TORRENT_USE_READV 0 +#define TORRENT_USE_IPV6 0 +#define TORRENT_USE_BOOST_THREAD 0 +#define TORRENT_USE_IOSTREAM 0 +// set this to 1 to disable all floating point operations +// (disables some float-dependent APIs) +#define TORRENT_NO_FPU 1 +#define TORRENT_USE_I2P 0 +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#endif + +// ==== Darwin/BSD === +#elif (defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \ + || defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__ \ + || defined __FreeBSD_kernel__ +#define TORRENT_BSD +// we don't need iconv on mac, because +// the locale is always utf-8 +#if defined __APPLE__ +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_LOCALE 0 +#define TORRENT_CLOSE_MAY_BLOCK 1 + +#include + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#ifdef TORRENT_USE_OPENSSL +#define TORRENT_USE_COMMONCRYPTO 1 +#endif // TORRENT_USE_OPENSSL +#endif // MAC_OS_X_VERSION_MIN_REQUIRED + +// execinfo.h is available in the MacOS X 10.5 SDK. +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +#define TORRENT_USE_EXECINFO 1 +#endif + +#endif // __APPLE__ + +#else +// FreeBSD has a reasonable iconv signature +// unless we're on glibc +#ifndef __GLIBC__ +# define TORRENT_ICONV_ARG (const char**) +#endif +#endif +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_SYSCTL 1 +#define TORRENT_USE_IFCONF 1 + + +// ==== LINUX === +#elif defined __linux__ +#define TORRENT_LINUX +#define TORRENT_USE_NETLINK 1 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_HAS_SALEN 0 + +// ===== ANDROID ===== (almost linux, sort of) +#if defined __ANDROID__ +#define TORRENT_ANDROID +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_IFADDRS 0 +#define TORRENT_USE_MEMALIGN 1 +#else +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_POSIX_MEMALIGN 1 +#endif + +#if __amd64__ || __i386__ +#define TORRENT_USE_EXECINFO 1 +#endif + +// ==== MINGW === +#elif defined __MINGW32__ +#define TORRENT_MINGW +#define TORRENT_WINDOWS +#ifndef TORRENT_USE_ICONV +# define TORRENT_USE_ICONV 0 +# define TORRENT_USE_LOCALE 1 +#endif +#define TORRENT_USE_RLIMIT 0 +#define TORRENT_USE_NETLINK 0 +#define TORRENT_USE_GETADAPTERSADDRESSES 1 +#define TORRENT_HAS_SALEN 0 +#define TORRENT_USE_GETIPFORWARDTABLE 1 +#ifndef TORRENT_USE_UNC_PATHS +# define TORRENT_USE_UNC_PATHS 1 +#endif + +// ==== WINDOWS === +#elif defined WIN32 +#define TORRENT_WINDOWS +#ifndef TORRENT_USE_GETIPFORWARDTABLE +#define TORRENT_USE_GETIPFORWARDTABLE 1 +#endif +#define TORRENT_USE_GETADAPTERSADDRESSES 1 +#define TORRENT_HAS_SALEN 0 +// windows has its own functions to convert +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#define TORRENT_USE_LOCALE 1 +#endif +#define TORRENT_USE_RLIMIT 0 +#define TORRENT_HAS_FALLOCATE 0 +#ifndef TORRENT_USE_UNC_PATHS +#define TORRENT_USE_UNC_PATHS 1 +#endif + +// ==== SOLARIS === +#elif defined sun || defined __sun +#define TORRENT_SOLARIS +#define TORRENT_COMPLETE_TYPES_REQUIRED 1 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_HAS_SALEN 0 + +// ==== BEOS === +#elif defined __BEOS__ || defined __HAIKU__ +#define TORRENT_BEOS +#include // B_PATH_NAME_LENGTH +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_MLOCK 0 +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 0 +#endif + +// ==== GNU/Hurd === +#elif defined __GNU__ +#define TORRENT_HURD +#define TORRENT_USE_IFADDRS 1 +#define TORRENT_USE_IFCONF 1 + +// ==== eCS(OS/2) === +#elif defined __OS2__ +#define TORRENT_OS2 +#define TORRENT_HAS_FALLOCATE 0 +#define TORRENT_USE_IFCONF 1 +#define TORRENT_USE_SYSCTL 1 +#define TORRENT_USE_MLOCK 0 +#define TORRENT_USE_IPV6 0 +#define TORRENT_ICONV_ARG (const char**) +#define TORRENT_USE_WRITEV 0 +#define TORRENT_USE_READV 0 + +#else + +#ifdef _MSC_VER +#pragma message ( "unknown OS, assuming BSD" ) +#else +#warning "unknown OS, assuming BSD" +#endif + +#define TORRENT_BSD +#endif + +// on windows, NAME_MAX refers to Unicode characters +// on linux it refers to bytes (utf-8 encoded) +// TODO: Make this count Unicode characters instead of bytes on windows + +// windows +#if defined FILENAME_MAX +#define TORRENT_MAX_PATH FILENAME_MAX + +// beos +#elif defined B_PATH_NAME_LENGTH +#define TORRENT_MAX_PATH B_PATH_NAME_LENGTH + +// solaris +#elif defined MAXPATH +#define TORRENT_MAX_PATH MAXPATH + +// posix +#elif defined NAME_MAX +#define TORRENT_MAX_PATH NAME_MAX + +// none of the above +#else +// this is the maximum number of characters in a +// path element / filename on windows +#define TORRENT_MAX_PATH 255 + +#ifdef _MSC_VER +#pragma message ( "unknown platform, assuming the longest path is 255" ) +#else +#warning "unknown platform, assuming the longest path is 255" +#endif + +#endif + +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW + +#include + +// internal +#ifdef __cplusplus +inline +#else +static +#endif +int snprintf(char* buf, int len, char const* fmt, ...) +{ + va_list lp; + int ret; + va_start(lp, fmt); + ret = _vsnprintf(buf, len, fmt, lp); + va_end(lp); + if (ret < 0) { buf[len-1] = 0; ret = len-1; } + return ret; +} + +#define strtoll _strtoi64 +#else +#include +#endif + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) \ + && !defined (TORRENT_UPNP_LOGGING) && TORRENT_USE_IOSTREAM +#define TORRENT_UPNP_LOGGING +#endif + +#ifndef TORRENT_ICONV_ARG +#define TORRENT_ICONV_ARG (char**) +#endif + +// libiconv presence, not implemented yet +#ifndef TORRENT_USE_ICONV +#define TORRENT_USE_ICONV 1 +#endif + +#ifndef TORRENT_HAS_SALEN +#define TORRENT_HAS_SALEN 1 +#endif + +#ifndef TORRENT_USE_GETADAPTERSADDRESSES +#define TORRENT_USE_GETADAPTERSADDRESSES 0 +#endif + +#ifndef TORRENT_USE_NETLINK +#define TORRENT_USE_NETLINK 0 +#endif + +#ifndef TORRENT_USE_EXECINFO +#define TORRENT_USE_EXECINFO 0 +#endif + +#ifndef TORRENT_USE_SYSCTL +#define TORRENT_USE_SYSCTL 0 +#endif + +#ifndef TORRENT_USE_GETIPFORWARDTABLE +#define TORRENT_USE_GETIPFORWARDTABLE 0 +#endif + +#ifndef TORRENT_USE_LOCALE +#define TORRENT_USE_LOCALE 0 +#endif + +// set this to true if close() may block on your system +// Mac OS X does this if the file being closed is not fully +// allocated on disk yet for instance. When defined, the disk +// I/O subsytem will use a separate thread for closing files +#ifndef TORRENT_CLOSE_MAY_BLOCK +#define TORRENT_CLOSE_MAY_BLOCK 0 +#endif + +#ifndef TORRENT_BROKEN_UNIONS +#define TORRENT_BROKEN_UNIONS 0 +#endif + +#ifndef TORRENT_USE_WSTRING +#if !defined BOOST_NO_STD_WSTRING +#define TORRENT_USE_WSTRING 1 +#else +#define TORRENT_USE_WSTRING 0 +#endif // BOOST_NO_STD_WSTRING +#endif // TORRENT_USE_WSTRING + +#ifndef TORRENT_HAS_FALLOCATE +#define TORRENT_HAS_FALLOCATE 1 +#endif + +#ifndef TORRENT_DEPRECATED_PREFIX +#define TORRENT_DEPRECATED_PREFIX +#endif + +#ifndef TORRENT_USE_COMMONCRYPTO +#define TORRENT_USE_COMMONCRYPTO 0 +#endif + +#ifndef TORRENT_DEPRECATED +#define TORRENT_DEPRECATED +#endif + +#ifndef TORRENT_COMPLETE_TYPES_REQUIRED +#define TORRENT_COMPLETE_TYPES_REQUIRED 0 +#endif + +#ifndef TORRENT_USE_UNC_PATHS +#define TORRENT_USE_UNC_PATHS 0 +#endif + +#ifndef TORRENT_USE_RLIMIT +#define TORRENT_USE_RLIMIT 1 +#endif + +#ifndef TORRENT_USE_IFADDRS +#define TORRENT_USE_IFADDRS 0 +#endif + +#ifndef TORRENT_USE_IPV6 +#define TORRENT_USE_IPV6 1 +#endif + +#ifndef TORRENT_USE_MLOCK +#define TORRENT_USE_MLOCK 1 +#endif + +#ifndef TORRENT_USE_WRITEV +#define TORRENT_USE_WRITEV 1 +#endif + +#ifndef TORRENT_USE_READV +#define TORRENT_USE_READV 1 +#endif + +#ifndef TORRENT_NO_FPU +#define TORRENT_NO_FPU 0 +#endif + +#ifndef TORRENT_USE_IOSTREAM +#ifndef BOOST_NO_IOSTREAM +#define TORRENT_USE_IOSTREAM 1 +#else +#define TORRENT_USE_IOSTREAM 0 +#endif +#endif + +// if set to true, piece picker will use less RAM +// but only support up to ~260000 pieces in a torrent +#ifndef TORRENT_COMPACT_PICKER +#define TORRENT_COMPACT_PICKER 0 +#endif + +#ifndef TORRENT_USE_I2P +#define TORRENT_USE_I2P 1 +#endif + +#if !defined TORRENT_IOV_MAX +#ifdef IOV_MAX +#define TORRENT_IOV_MAX IOV_MAX +#else +#define TORRENT_IOV_MAX INT_MAX +#endif +#endif + +#if !defined(TORRENT_READ_HANDLER_MAX_SIZE) +# ifdef _GLIBCXX_DEBUG +# define TORRENT_READ_HANDLER_MAX_SIZE 400 +# else +// if this is not divisible by 8, we're wasting space +# define TORRENT_READ_HANDLER_MAX_SIZE 336 +# endif +#endif + +#if !defined(TORRENT_WRITE_HANDLER_MAX_SIZE) +# ifdef _GLIBCXX_DEBUG +# define TORRENT_WRITE_HANDLER_MAX_SIZE 400 +# else +// if this is not divisible by 8, we're wasting space +# define TORRENT_WRITE_HANDLER_MAX_SIZE 336 +# endif +#endif + +#if defined _MSC_VER && _MSC_VER <= 1200 +#define for if (false) {} else for +#endif + +#if TORRENT_BROKEN_UNIONS +#define TORRENT_UNION struct +#else +#define TORRENT_UNION union +#endif + +// determine what timer implementation we can use +// if one is already defined, don't pick one +// autmatically. This lets the user control this +// from the Jamfile +#if !defined TORRENT_USE_ABSOLUTE_TIME \ + && !defined TORRENT_USE_QUERY_PERFORMANCE_TIMER \ + && !defined TORRENT_USE_CLOCK_GETTIME \ + && !defined TORRENT_USE_BOOST_DATE_TIME \ + && !defined TORRENT_USE_ECLOCK \ + && !defined TORRENT_USE_SYSTEM_TIME + +#if defined __APPLE__ && defined __MACH__ +#define TORRENT_USE_ABSOLUTE_TIME 1 +#elif defined(_WIN32) || defined TORRENT_MINGW +#define TORRENT_USE_QUERY_PERFORMANCE_TIMER 1 +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 +#define TORRENT_USE_CLOCK_GETTIME 1 +#elif defined(TORRENT_AMIGA) +#define TORRENT_USE_ECLOCK 1 +#elif defined(TORRENT_BEOS) +#define TORRENT_USE_SYSTEM_TIME 1 +#else +#define TORRENT_USE_BOOST_DATE_TIME 1 +#endif + +#endif + +// debug builds have asserts enabled by default, release +// builds have asserts if they are explicitly enabled by +// the release_asserts macro. +#ifndef TORRENT_USE_ASSERTS +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS +#define TORRENT_USE_ASSERTS 1 +#else +#define TORRENT_USE_ASSERTS 0 +#endif +#endif // TORRENT_USE_ASSERTS + +#if defined TORRENT_DEBUG && TORRENT_USE_ASSERTS \ + && !defined TORRENT_DISABLE_INVARIANT_CHECKS +#define TORRENT_USE_INVARIANT_CHECKS 1 +#else +#define TORRENT_USE_INVARIANT_CHECKS 0 +#endif + +// for non-exception builds +#ifdef BOOST_NO_EXCEPTIONS +#define TORRENT_TRY if (true) +#define TORRENT_CATCH(x) else if (false) +#define TORRENT_CATCH_ALL else if (false) +#define TORRENT_DECLARE_DUMMY(x, y) x y +#else +#define TORRENT_TRY try +#define TORRENT_CATCH(x) catch(x) +#define TORRENT_CATCH_ALL catch(...) +#define TORRENT_DECLARE_DUMMY(x, y) +#endif // BOOST_NO_EXCEPTIONS + + +#endif // TORRENT_CONFIG_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp new file mode 100644 index 0000000000..73be10d7ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/connection_queue.hpp @@ -0,0 +1,150 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONNECTION_QUEUE +#define TORRENT_CONNECTION_QUEUE + +#include +#include +#include +#include +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/deadline_timer.hpp" + +#ifdef TORRENT_CONNECTION_LOGGING +#include +#endif + +#include "libtorrent/thread.hpp" + +namespace libtorrent +{ + +class TORRENT_EXTRA_EXPORT connection_queue : public boost::noncopyable +{ +public: + connection_queue(io_service& ios); + + // if there are no free slots, returns the negative + // number of queued up connections + int free_slots() const; + + void enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout, int priority = 0); + bool done(int ticket); + void limit(int limit); + int limit() const; + void close(); + int size() const { return m_queue.size(); } + int num_connecting() const { return m_num_connecting; } +#if defined TORRENT_ASIO_DEBUGGING + float next_timeout() const { return total_milliseconds(m_timer.expires_at() - time_now_hires()) / 1000.f; } + float max_timeout() const + { + ptime max_timeout = min_time(); + for (std::list::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (!i->connecting) continue; + if (i->expires > max_timeout) max_timeout = i->expires; + } + if (max_timeout == min_time()) return 0.f; + return total_milliseconds(max_timeout - time_now_hires()) / 1000.f; + } +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +private: + + typedef mutex mutex_t; + + void try_connect(mutex_t::scoped_lock& l); + void on_timeout(error_code const& e); + void on_try_connect(); + + struct entry + { + entry() + : expires(max_time()) + , ticket(0) + , connecting(false) + , priority(0) + {} + // called when the connection is initiated + // this is when the timeout countdown starts + boost::function on_connect; + // called if done hasn't been called within the timeout + // or if the connection queue aborts. This means there + // are 3 different interleaves of these function calls: + // 1. on_connect + // 2. on_connect, on_timeout + // 3. on_timeout + boost::function on_timeout; + ptime expires; + time_duration timeout; + boost::int32_t ticket; + bool connecting; + boost::uint8_t priority; + }; + + std::list m_queue; + + // the next ticket id a connection will be given + int m_next_ticket; + int m_num_connecting; + int m_half_open_limit; + bool m_abort; + + // the number of outstanding timers + int m_num_timers; + + deadline_timer m_timer; + + mutable mutex_t m_mutex; + +#ifdef TORRENT_DEBUG + bool m_in_timeout_function; +#endif +#ifdef TORRENT_CONNECTION_LOGGING + std::ofstream m_log; +#endif +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp new file mode 100644 index 0000000000..2010b3f61b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/copy_ptr.hpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_COPY_PTR +#define TORRENT_COPY_PTR + +namespace libtorrent +{ + template + struct copy_ptr + { + copy_ptr(): m_ptr(0) {} + copy_ptr(T* t): m_ptr(t) {} + copy_ptr(copy_ptr const& p): m_ptr(p.m_ptr ? new T(*p.m_ptr) : 0) {} + void reset(T* t = 0) { delete m_ptr; m_ptr = t; } + copy_ptr& operator=(copy_ptr const& p) + { + delete m_ptr; + m_ptr = p.m_ptr ? new T(*p.m_ptr) : 0; + return *this; + } + T* operator->() { return m_ptr; } + T const* operator->() const { return m_ptr; } + T& operator*() { return *m_ptr; } + T const& operator*() const { return *m_ptr; } + void swap(copy_ptr& p) + { + T* tmp = m_ptr; + m_ptr = p.m_ptr; + p.m_ptr = tmp; + } + operator bool() const { return m_ptr != 0; } + ~copy_ptr() { delete m_ptr; } + private: + T* m_ptr; + }; +} + +#endif // TORRENT_COPY_PTR + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp new file mode 100644 index 0000000000..3ae4429ccf --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/create_torrent.hpp @@ -0,0 +1,505 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CREATE_TORRENT_HPP_INCLUDED +#define TORRENT_CREATE_TORRENT_HPP_INCLUDED + +#include "libtorrent/bencode.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/file_storage.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/file.hpp" // for combine_path etc. + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// OVERVIEW +// +// This section describes the functions and classes that are used +// to create torrent files. It is a layered API with low level classes +// and higher level convenience functions. A torrent is created in 4 +// steps: +// +// 1. first the files that will be part of the torrent are determined. +// 2. the torrent properties are set, such as tracker url, web seeds, +// DHT nodes etc. +// 3. Read through all the files in the torrent, SHA-1 all the data +// and set the piece hashes. +// 4. The torrent is bencoded into a file or buffer. +// +// If there are a lot of files and or deep directoy hierarchies to +// traverse, step one can be time consuming. +// +// Typically step 3 is by far the most time consuming step, since it +// requires to read all the bytes from all the files in the torrent. +// +// All of these classes and functions are declared by including +// ``libtorrent/create_torrent.hpp``. +// +// example: +// +// .. code:: c++ +// +// file_storage fs; +// +// // recursively adds files in directories +// add_files(fs, "./my_torrent"); +// +// create_torrent t(fs); +// t.add_tracker("http://my.tracker.com/announce"); +// t.set_creator("libtorrent example"); +// +// // reads the files and calculates the hashes +// set_piece_hashes(t, "."); +// +// ofstream out("my_torrent.torrent", std::ios_base::binary); +// bencode(std::ostream_iterator(out), t.generate()); +// +namespace libtorrent +{ + class torrent_info; + + // This class holds state for creating a torrent. After having added + // all information to it, call create_torrent::generate() to generate + // the torrent. The entry that's returned can then be bencoded into a + // .torrent file using bencode(). + struct TORRENT_EXPORT create_torrent + { + // flags for create_torrent::create_torrent(). + enum flags_t + { + // This will insert pad files to align the files to piece boundaries, for + // optimized disk-I/O. + optimize = 1 + + // This will create a merkle hash tree torrent. A merkle torrent cannot + // be opened in clients that don't specifically support merkle torrents. + // The benefit is that the resulting torrent file will be much smaller and + // not grow with more pieces. When this option is specified, it is + // recommended to have a fairly small piece size, say 64 kiB. + // When creating merkle torrents, the full hash tree is also generated + // and should be saved off separately. It is accessed through the + // create_torrent::merkle_tree() function. + , merkle = 2 + + // This will include the file modification time as part of the torrent. + // This is not enabled by default, as it might cause problems when you + // create a torrent from separate files with the same content, hoping to + // yield the same info-hash. If the files have different modification times, + // with this option enabled, you would get different info-hashes for the + // files. + , modification_time = 4 + + // If this flag is set, files that are symlinks get a symlink attribute + // set on them and their data will not be included in the torrent. This + // is useful if you need to reconstruct a file hierarchy which contains + // symlinks. + , symlinks = 8 + + // If this is set, the set_piece_hashes() function will, as it calculates + // the piece hashes, also calculate the file hashes and add those associated + // with each file. Note that unless you use the set_piece_hashes() function, + // this flag will have no effect. + , calculate_file_hashes = 16 + }; + + // The ``piece_size`` is the size of each piece in bytes. It must + // be a multiple of 16 kiB. If a piece size of 0 is specified, a + // piece_size will be calculated such that the torrent file is roughly 40 kB. + // + // If a ``pad_size_limit`` is specified (other than -1), any file larger than + // the specified number of bytes will be preceeded by a pad file to align it + // with the start of a piece. The pad_file_limit is ignored unless the + // ``optimize`` flag is passed. Typically it doesn't make sense to set this + // any lower than 4kiB. + // + // The overload that takes a ``torrent_info`` object will make a verbatim + // copy of its info dictionary (to preserve the info-hash). The copy of + // the info dictionary will be used by create_torrent::generate(). This means + // that none of the member functions of create_torrent that affects + // the content of the info dictionary (such as ``set_hash()``), will + // have any affect. + // + // The ``flags`` arguments specifies options for the torrent creation. It can + // be any combination of the flags defined by create_torrent::flags_t. + // + // ``alignment`` is used when pad files are enabled. This is the size + // eligible files are aligned to. The default is -1, which means the + // piece size of the torrent. + create_torrent(file_storage& fs, int piece_size = 0 + , int pad_file_limit = -1, int flags = optimize, int alignment = -1); + create_torrent(torrent_info const& ti); + + // internal + ~create_torrent(); + + // This function will generate the .torrent file as a bencode tree. In order to + // generate the flat file, use the bencode() function. + // + // It may be useful to add custom entries to the torrent file before bencoding it + // and saving it to disk. + // + // If anything goes wrong during torrent generation, this function will return + // an empty ``entry`` structure. You can test for this condition by querying the + // type of the entry: + // + // .. code:: c++ + // + // file_storage fs; + // // add file ... + // create_torrent t(fs); + // // add trackers and piece hashes ... + // e = t.generate(); + // + // if (e.type() == entry::undefined_t) + // { + // // something went wrong + // } + // + // For instance, you cannot generate a torrent with 0 files in it. If you don't add + // any files to the ``file_storage``, torrent generation will fail. + entry generate() const; + + // returns an immutable reference to the file_storage used to create + // the torrent from. + file_storage const& files() const { return m_files; } + + // Sets the comment for the torrent. The string ``str`` should be utf-8 encoded. + // The comment in a torrent file is optional. + void set_comment(char const* str); + + // Sets the creator of the torrent. The string ``str`` should be utf-8 encoded. + // This is optional. + void set_creator(char const* str); + + // This sets the SHA-1 hash for the specified piece (``index``). You are required + // to set the hash for every piece in the torrent before generating it. If you have + // the files on disk, you can use the high level convenience function to do this. + // See set_piece_hashes(). + void set_hash(int index, sha1_hash const& h); + + // This sets the sha1 hash for this file. This hash will end up under the key ``sha1`` + // associated with this file (for multi-file torrents) or in the root info dictionary + // for single-file torrents. + void set_file_hash(int index, sha1_hash const& h); + + // This adds a url seed to the torrent. You can have any number of url seeds. For a + // single file torrent, this should be an HTTP url, pointing to a file with identical + // content as the file of the torrent. For a multi-file torrent, it should point to + // a directory containing a directory with the same name as this torrent, and all the + // files of the torrent in it. + // + // The second function, ``add_http_seed()`` adds an HTTP seed instead. + void add_url_seed(std::string const& url); + void add_http_seed(std::string const& url); + + // This adds a DHT node to the torrent. This especially useful if you're creating a + // tracker less torrent. It can be used by clients to bootstrap their DHT node from. + // The node is a hostname and a port number where there is a DHT node running. + // You can have any number of DHT nodes in a torrent. + void add_node(std::pair const& node); + + // Adds a tracker to the torrent. This is not strictly required, but most torrents + // use a tracker as their main source of peers. The url should be an http:// or udp:// + // url to a machine running a bittorrent tracker that accepts announces for this torrent's + // info-hash. The tier is the fallback priority of the tracker. All trackers with tier 0 are + // tried first (in any order). If all fail, trackers with tier 1 are tried. If all of those + // fail, trackers with tier 2 are tried, and so on. + void add_tracker(std::string const& url, int tier = 0); + + // This function sets an X.509 certificate in PEM format to the torrent. This makes the + // torrent an *SSL torrent*. An SSL torrent requires that each peer has a valid certificate + // signed by this root certificate. For SSL torrents, all peers are connecting over SSL + // connections. For more information, see the section on ssl-torrents_. + // + // The string is not the path to the cert, it's the actual content of the certificate, + // loaded into a std::string. + void set_root_cert(std::string const& pem); + + // Sets and queries the private flag of the torrent. + // Torrents with the private flag set ask clients to not use any other + // sources than the tracker for peers, and to not advertize itself publicly, + // apart from the tracker. + void set_priv(bool p) { m_private = p; } + bool priv() const { return m_private; } + + // returns the number of pieces in the associated file_storage object. + int num_pieces() const { return m_files.num_pieces(); } + + // ``piece_length()`` returns the piece size of all pieces but the + // last one. ``piece_size()`` returns the size of the specified piece. + // these functions are just forwarding to the associated file_storage. + int piece_length() const { return m_files.piece_length(); } + int piece_size(int i) const { return m_files.piece_size(i); } + + // internal + bool should_add_file_hashes() const { return m_calculate_file_hashes; } + + // This function returns the merkle hash tree, if the torrent was created as a merkle + // torrent. The tree is created by ``generate()`` and won't be valid until that function + // has been called. When creating a merkle tree torrent, the actual tree itself has to + // be saved off separately and fed into libtorrent the first time you start seeding it, + // through the ``torrent_info::set_merkle_tree()`` function. From that point onwards, the + // tree will be saved in the resume data. + std::vector const& merkle_tree() const { return m_merkle_tree; } + + private: + + file_storage& m_files; + // if m_info_dict is initialized, it is + // used instead of m_files to generate + // the info dictionary + entry m_info_dict; + + // the urls to the trackers + typedef std::pair announce_entry; + std::vector m_urls; + + std::vector m_url_seeds; + std::vector m_http_seeds; + + std::vector m_piece_hash; + + std::vector m_filehashes; + + // if we're generating a merkle torrent, this is the + // merkle tree we got. This should be saved in fast-resume + // in order to start seeding the torrent + mutable std::vector m_merkle_tree; + + // dht nodes to add to the routing table/bootstrap from + typedef std::vector > nodes_t; + nodes_t m_nodes; + + // the hash that identifies this torrent + // is mutable because it's calculated + // lazily + mutable sha1_hash m_info_hash; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + time_t m_creation_date; + + // if a comment is found in the torrent file + // this will be set to that comment + std::string m_comment; + + // an optional string naming the software used + // to create the torrent file + std::string m_created_by; + + // this is the root cert for SSL torrents + std::string m_root_cert; + + // this is used when creating a torrent. If there's + // only one file there are cases where it's impossible + // to know if it should be written as a multifile torrent + // or not. e.g. test/test there's one file and one directory + // and they have the same name. + bool m_multifile:1; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private:1; + + // if set to one, a merkle torrent will be generated + bool m_merkle_torrent:1; + + // if set, include the 'mtime' modification time in the + // torrent file + bool m_include_mtime:1; + + // if set, symbolic links are declared as such in + // the torrent file. The full data of the pointed-to + // file is still included + bool m_include_symlinks:1; + + // this is only used by set_piece_hashes(). It will + // calculate sha1 hashes for each file and add it + // to the file list + bool m_calculate_file_hashes:1; + }; + + namespace detail + { + inline bool default_pred(std::string const&) { return true; } + + inline bool ignore_subdir(std::string const& leaf) + { return leaf == ".." || leaf == "."; } + + inline void nop(int) {} + + int get_file_attributes(std::string const& p); + std::string get_symlink_path(std::string const& p); + + // internal + TORRENT_EXPORT void add_files_impl(file_storage& fs, std::string const& p + , std::string const& l, boost::function pred + , boost::uint32_t flags); + } + + // Adds the file specified by ``path`` to the file_storage object. In case ``path`` + // refers to a diretory, files will be added recursively from the directory. + // + // If specified, the predicate ``p`` is called once for every file and directory that + // is encountered. files for which ``p`` returns true are added, and directories for + // which ``p`` returns true are traversed. ``p`` must have the following signature:: + // + // bool Pred(std::string const& p); + // + // The path that is passed in to the predicate is the full path of the file or + // directory. If no predicate is specified, all files are added, and all directories + // are traveresed. + // + // The ".." directory is never traversed. + // + // The ``flags`` argument should be the same as the flags passed to the `create_torrent`_ + // constructor. + template void add_files(file_storage& fs, std::string const& file, Pred p, boost::uint32_t flags = 0) + { + detail::add_files_impl(fs, parent_path(complete(file)), filename(file), p, flags); + } + inline void add_files(file_storage& fs, std::string const& file, boost::uint32_t flags = 0) + { + detail::add_files_impl(fs, parent_path(complete(file)), filename(file) + , detail::default_pred, flags); + } + + // This function will assume that the files added to the torrent file exists at path + // ``p``, read those files and hash the content and set the hashes in the ``create_torrent`` + // object. The optional function ``f`` is called in between every hash that is set. ``f`` + // must have the following signature:: + // + // void Fun(int); + // + // The overloads that don't take an ``error_code&`` may throw an exception in case of a + // file error, the other overloads sets the error code to reflect the error, if any. + TORRENT_EXPORT void set_piece_hashes(create_torrent& t, std::string const& p + , boost::function f, error_code& ec); + inline void set_piece_hashes(create_torrent& t, std::string const& p, error_code& ec) + { + set_piece_hashes(t, p, detail::nop, ec); + } +#ifndef BOOST_NO_EXCEPTIONS + inline void set_piece_hashes(create_torrent& t, std::string const& p) + { + error_code ec; + set_piece_hashes(t, p, detail::nop, ec); + if (ec) throw libtorrent_exception(ec); + } + template + void set_piece_hashes(create_torrent& t, std::string const& p, Fun f) + { + error_code ec; + set_piece_hashes(t, p, f, ec); + if (ec) throw libtorrent_exception(ec); + } +#endif + +#if TORRENT_USE_WSTRING + // wstring versions + + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings +#ifndef TORRENT_NO_DEPRECATE + + template + TORRENT_DEPRECATED_PREFIX + void TORRENT_DEPRECATED add_files(file_storage& fs, std::wstring const& wfile, Pred p, boost::uint32_t flags = 0) + { + std::string utf8; + wchar_utf8(wfile, utf8); + detail::add_files_impl(fs, parent_path(complete(utf8)) + , filename(utf8), p, flags); + } + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED add_files(file_storage& fs, std::wstring const& wfile, boost::uint32_t flags = 0) + { + std::string utf8; + wchar_utf8(wfile, utf8); + detail::add_files_impl(fs, parent_path(complete(utf8)) + , filename(utf8), detail::default_pred, flags); + } + + void TORRENT_EXPORT set_piece_hashes(create_torrent& t, std::wstring const& p + , boost::function const& f, error_code& ec); + +#ifndef BOOST_NO_EXCEPTIONS + template + TORRENT_DEPRECATED_PREFIX + void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p, Fun f) + { + error_code ec; + set_piece_hashes(t, p, f, ec); + if (ec) throw libtorrent_exception(ec); + } + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p) + { + error_code ec; + set_piece_hashes(t, p, detail::nop, ec); + if (ec) throw libtorrent_exception(ec); + } +#endif + + TORRENT_DEPRECATED_PREFIX + inline void TORRENT_DEPRECATED set_piece_hashes(create_torrent& t, std::wstring const& p, error_code& ec) + { + set_piece_hashes(t, p, detail::nop, ec); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp new file mode 100644 index 0000000000..71c52c796a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/deadline_timer.hpp @@ -0,0 +1,103 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DEADLINE_TIMER_HPP_INCLUDED +#define TORRENT_DEADLINE_TIMER_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if __GNUC__ < 3 +// in GCC 2.95 templates seems to have all symbols +// resolved as they are parsed, so the time_traits +// template actually needs the definitions it uses, +// even though it's never instantiated +#include +#else +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#include "libtorrent/time.hpp" + +// asio time_traits +#if !TORRENT_USE_BOOST_DATE_TIME +#if BOOST_VERSION >= 103500 +namespace boost { +#endif +namespace asio +{ + template<> + struct time_traits + { + typedef libtorrent::ptime time_type; + typedef libtorrent::time_duration duration_type; + static time_type now() + { return time_type(libtorrent::time_now_hires()); } + static time_type add(time_type t, duration_type d) + { return time_type(t.time + d.diff);} + static duration_type subtract(time_type t1, time_type t2) + { return duration_type(t1 - t2); } + static bool less_than(time_type t1, time_type t2) + { return t1 < t2; } + static boost::posix_time::time_duration to_posix_duration( + duration_type d) + { return boost::posix_time::microseconds(libtorrent::total_microseconds(d)); } + }; +} +#if BOOST_VERSION >= 103500 +} +#endif +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::basic_deadline_timer deadline_timer; +#else + typedef boost::asio::basic_deadline_timer deadline_timer; +#endif +} + +#endif // TORRENT_DEADLINE_TIMER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp new file mode 100644 index 0000000000..02a2af49ce --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/debug.hpp @@ -0,0 +1,219 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DEBUG_HPP_INCLUDED +#define TORRENT_DEBUG_HPP_INCLUDED + +#if defined TORRENT_ASIO_DEBUGGING + +#include "libtorrent/assert.hpp" +#include "libtorrent/thread.hpp" + +#include +#include + +std::string demangle(char const* name); + +namespace libtorrent +{ + struct async_t + { + async_t() : refs(0) {} + std::string stack; + int refs; + }; + + extern std::map _async_ops; + extern int _async_ops_nthreads; + extern mutex _async_ops_mutex; + + inline void add_outstanding_async(char const* name) + { + mutex::scoped_lock l(_async_ops_mutex); + async_t& a = _async_ops[name]; + if (a.stack.empty()) + { + char stack_text[10000]; + print_backtrace(stack_text, sizeof(stack_text), 9); + a.stack = stack_text; + } + ++a.refs; + } + + inline void complete_async(char const* name) + { + mutex::scoped_lock l(_async_ops_mutex); + async_t& a = _async_ops[name]; + TORRENT_ASSERT(a.refs > 0); + --a.refs; + } + + inline void async_inc_threads() + { + mutex::scoped_lock l(_async_ops_mutex); + ++_async_ops_nthreads; + } + + inline void async_dec_threads() + { + mutex::scoped_lock l(_async_ops_mutex); + --_async_ops_nthreads; + } + + inline int log_async() + { + mutex::scoped_lock l(_async_ops_mutex); + int ret = 0; + for (std::map::iterator i = _async_ops.begin() + , end(_async_ops.end()); i != end; ++i) + { + if (i->second.refs <= _async_ops_nthreads - 1) continue; + ret += i->second.refs; + printf("%s: (%d)\n%s\n", i->first.c_str(), i->second.refs, i->second.stack.c_str()); + } + return ret; + } +} + +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/thread.hpp" + +#if TORRENT_USE_IOSTREAM +#include +#include +#include +#endif + +namespace libtorrent +{ + // DEBUG API + + struct logger + { +#if TORRENT_USE_IOSTREAM + // all log streams share a single file descriptor + // and re-opens the file for each log line + // these members are defined in session_impl.cpp + static std::ofstream log_file; + static std::string open_filename; + static mutex file_mutex; +#endif + + ~logger() + { + mutex::scoped_lock l(file_mutex); + log_file.close(); + open_filename.clear(); + } + + logger(std::string const& logpath, std::string const& filename + , int instance, bool append) + { + char log_name[512]; + snprintf(log_name, sizeof(log_name), "libtorrent_logs%d", instance); + std::string dir(complete(combine_path(combine_path(logpath, log_name), filename)) + ".log"); + error_code ec; + if (!exists(parent_path(dir))) + create_directories(parent_path(dir), ec); + m_filename = dir; + + mutex::scoped_lock l(file_mutex); + open(!append); + log_file << "\n\n\n*** starting log ***\n"; + } + + void move_log_file(std::string const& logpath, std::string const& new_name, int instance) + { + mutex::scoped_lock l(file_mutex); + if (open_filename == m_filename) + { + log_file.close(); + open_filename.clear(); + } + + char log_name[512]; + snprintf(log_name, sizeof(log_name), "libtorrent_logs%d", instance); + std::string dir(combine_path(combine_path(complete(logpath), log_name), new_name) + ".log"); + + error_code ec; + create_directories(parent_path(dir), ec); + + if (ec) + fprintf(stderr, "Failed to create logfile directory %s: %s\n" + , parent_path(dir).c_str(), ec.message().c_str()); + ec.clear(); + rename(m_filename, dir, ec); + if (ec) + fprintf(stderr, "Failed to move logfile %s: %s\n" + , parent_path(dir).c_str(), ec.message().c_str()); + + m_filename = dir; + } + +#if TORRENT_USE_IOSTREAM + void open(bool truncate) + { + if (open_filename == m_filename) return; + log_file.close(); + log_file.clear(); + log_file.open(m_filename.c_str(), truncate ? std::ios_base::trunc : std::ios_base::app); + open_filename = m_filename; + if (!log_file.good()) + fprintf(stderr, "Failed to open logfile %s: %s\n", m_filename.c_str(), strerror(errno)); + } +#endif + + template + logger& operator<<(T const& v) + { +#if TORRENT_USE_IOSTREAM + mutex::scoped_lock l(file_mutex); + open(false); + log_file << v; +#endif + return *this; + } + + std::string m_filename; + }; + +} + +#endif // TORRENT_VERBOSE_LOGGING || TORRENT_LOGGING || TORRENT_ERROR_LOGGING +#endif // TORRENT_DEBUG_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp new file mode 100644 index 0000000000..80905cf7b5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_holder.hpp @@ -0,0 +1,101 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED +#define TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include + +namespace libtorrent +{ + + namespace aux { struct session_impl; } + struct disk_buffer_pool; + + // The disk buffer holder acts like a ``scoped_ptr`` that frees a disk buffer + // when it's destructed, unless it's released. ``release`` returns the disk + // buffer and transferres ownership and responsibility to free it to the caller. + // + // A disk buffer is freed by passing it to ``session_impl::free_disk_buffer()``. + // + // ``buffer()`` returns the pointer without transferring responsibility. If + // this buffer has been released, ``buffer()`` will return 0. + struct TORRENT_EXPORT disk_buffer_holder + { + // internal + disk_buffer_holder(aux::session_impl& ses, char* buf); + + // construct a buffer holder that will free the held buffer + // using a disk buffer pool directly (there's only one + // disk_buffer_pool per session) + disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf); + + // frees any unreleased disk buffer held by this object + ~disk_buffer_holder(); + + // return the held disk buffer and clear it from the + // holder. The responsibility to free it is passed on + // to the caller + char* release(); + + // return a pointer to the held buffer + char* get() const { return m_buf; } + + // set the holder object to hold the specified buffer + // (or NULL by default). If it's already holding a + // disk buffer, it will first be freed. + void reset(char* buf = 0); + + // swap pointers of two disk buffer holders. + void swap(disk_buffer_holder& h) + { + TORRENT_ASSERT(&h.m_disk_pool == &m_disk_pool); + std::swap(h.m_buf, m_buf); + } + + typedef char* (disk_buffer_holder::*unspecified_bool_type)(); + + // internal + operator unspecified_bool_type() const + { return m_buf == 0? 0: &disk_buffer_holder::release; } + + private: + disk_buffer_pool& m_disk_pool; + char* m_buf; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp new file mode 100644 index 0000000000..9df167e20b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_buffer_pool.hpp @@ -0,0 +1,145 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_BUFFER_POOL +#define TORRENT_DISK_BUFFER_POOL + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/allocator.hpp" + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR +#include +#endif + +#ifdef TORRENT_DISK_STATS +#include +#endif + +#if TORRENT_USE_ASSERTS || TORRENT_DISK_STATS +#include +#endif + +namespace libtorrent +{ + struct TORRENT_EXTRA_EXPORT disk_buffer_pool : boost::noncopyable + { + disk_buffer_pool(int block_size); +#if TORRENT_USE_ASSERTS + ~disk_buffer_pool(); +#endif + +#if TORRENT_USE_ASSERTS || TORRENT_DISK_STATS + bool is_disk_buffer(char* buffer + , mutex::scoped_lock& l) const; + bool is_disk_buffer(char* buffer) const; +#endif + + char* allocate_buffer(char const* category); + void free_buffer(char* buf); + void free_multiple_buffers(char** bufvec, int numbufs); + + int block_size() const { return m_block_size; } + +#ifdef TORRENT_STATS + int disk_allocations() const + { return m_allocations; } +#endif + +#ifdef TORRENT_DISK_STATS + std::ofstream m_disk_access_log; +#endif + + void release_memory(); + + int in_use() const { return m_in_use; } + + protected: + + void free_buffer_impl(char* buf, mutex::scoped_lock& l); + + // number of bytes per block. The BitTorrent + // protocol defines the block size to 16 KiB. + const int m_block_size; + + // number of disk buffers currently allocated + int m_in_use; + + session_settings m_settings; + + private: + + mutable mutex m_pool_mutex; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // if this is true, all buffers are allocated + // from m_pool. If this is false, all buffers + // are allocated using page_aligned_allocator. + // if the settings change to prefer the other + // allocator, this bool will not switch over + // to match the settings until all buffers have + // been freed. That way, we never have a mixture + // of buffers allocated from different sources. + // in essence, this make the setting only take + // effect after a restart (which seems fine). + // or once the client goes idle for a while. + bool m_using_pool_allocator; + + // memory pool for read and write operations + // and disk cache + boost::pool m_pool; +#endif + +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + int m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + public: + void rename_buffer(char* buf, char const* category); + protected: + boost::unordered_map m_categories; + boost::unordered_map m_buf_to_category; + std::ofstream m_log; + private: +#endif +#if TORRENT_USE_ASSERTS + int m_magic; +#endif + }; + +} + +#endif // TORRENT_DISK_BUFFER_POOL + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp new file mode 100644 index 0000000000..975f855c6c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/disk_io_thread.hpp @@ -0,0 +1,534 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISK_IO_THREAD +#define TORRENT_DISK_IO_THREAD + +#include "libtorrent/storage.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/sliding_average.hpp" + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/disk_buffer_pool.hpp" + +#include +#include +#include +#include + +namespace libtorrent +{ + using boost::multi_index::multi_index_container; + using boost::multi_index::ordered_non_unique; + using boost::multi_index::ordered_unique; + using boost::multi_index::indexed_by; + using boost::multi_index::member; + using boost::multi_index::const_mem_fun; + + struct cached_piece_info + { + // the piece index for this cache entry. + int piece; + + // holds one entry for each block in this piece. ``true`` represents + // the data for that block being in the disk cache and ``false`` means it's not. + std::vector blocks; + + // the time when a block was last written to this piece. The older + // a piece is, the more likely it is to be flushed to disk. + ptime last_use; + + // The index of the next block that needs to be hashed. + // Blocks are hashed as they are downloaded in order to not + // have to re-read them from disk once the piece is complete, to + // compare its hash against the hashes in the .torrent file. + int next_to_hash; + + enum kind_t { read_cache = 0, write_cache = 1 }; + + // specifies if this piece is part of the read cache or the write cache. + kind_t kind; + }; + + struct disk_io_job + { + disk_io_job() + : buffer(0) + , buffer_size(0) + , piece(0) + , offset(0) + , max_cache_line(0) + , cache_min_time(0) + , action(read) + {} + + enum action_t + { + read + , write + , hash + , move_storage + , release_files + , delete_files + , check_fastresume + , check_files + , save_resume_data + , rename_file + , abort_thread + , clear_read_cache + , abort_torrent + , update_settings + , read_and_hash + , cache_piece + , file_priority +#ifndef TORRENT_NO_DEPRECATE + , finalize_file +#endif + }; + + + char* buffer; + + // this is called when operation completes + boost::function callback; + + boost::intrusive_ptr storage; + + boost::shared_ptr resume_data; + + // the error code from the file operation + error_code error; + + // the time when this job was issued. This is used to + // keep track of disk I/O congestion + ptime start_time; + + // used for move_storage and rename_file. On errors, this is set + // to the error message + std::string str; + + // on error, this is set to the path of the + // file the disk operation failed on + std::string error_file; + + int buffer_size; + + // arguments used for read and write + // piece is used as flags for move_storage + int piece, offset; + + // if this is > 0, it specifies the max number of blocks to read + // ahead in the read cache for this access. This is only valid + // for 'read' actions + int max_cache_line; + + // if this is > 0, it may increase the minimum time the cache + // line caused by this operation stays in the cache + int cache_min_time; + + boost::uint8_t action; + }; + + // returns true if the fundamental operation + // of the given disk job is a read operation + bool is_read_operation(disk_io_job const& j); + + // this is true if the buffer field in the disk_io_job + // points to a disk buffer + bool operation_has_buffer(disk_io_job const& j); + + // this struct holds a number of statistics counters + // relevant for the disk io thread and disk cache. + struct TORRENT_EXPORT cache_status + { + // initializes all counters to 0 + cache_status() + : blocks_written(0) + , writes(0) + , blocks_read(0) + , blocks_read_hit(0) + , reads(0) + , queued_bytes(0) + , cache_size(0) + , read_cache_size(0) + , total_used_buffers(0) + , average_queue_time(0) + , average_read_time(0) + , average_write_time(0) + , average_hash_time(0) + , average_job_time(0) + , average_sort_time(0) + , job_queue_length(0) + , cumulative_job_time(0) + , cumulative_read_time(0) + , cumulative_write_time(0) + , cumulative_hash_time(0) + , cumulative_sort_time(0) + , total_read_back(0) + , read_queue_size(0) + {} + + // the total number of 16 KiB blocks written to disk + // since this session was started. + size_type blocks_written; + + // the total number of write operations performed since this + // session was started. + // + // The ratio (``blocks_written`` - ``writes``) / ``blocks_written`` represents + // the number of saved write operations per total write operations. i.e. a kind + // of cache hit ratio for the write cahe. + size_type writes; + + // the number of blocks that were requested from the + // bittorrent engine (from peers), that were served from disk or cache. + size_type blocks_read; + + // the number of blocks that was just copied from the read cache + // + // The ratio ``blocks_read_hit`` / ``blocks_read`` is the cache hit ratio + // for the read cache. + size_type blocks_read_hit; + + // the number of read operations used + size_type reads; + + // the number of bytes waiting, in the disk job queue, to be written + // or inserted into the disk cache + mutable size_type queued_bytes; + + // the number of 16 KiB blocks currently in the disk cache (both read and write). + // This includes both read and write cache. + int cache_size; + + // the number of 16KiB blocks in the read cache. + int read_cache_size; + + // the total number of buffers currently in use. + // This includes the read/write disk cache as well as send and receive buffers + // used in peer connections. + mutable int total_used_buffers; + + // the number of microseconds an average disk I/O job + // has to wait in the job queue before it get processed. + int average_queue_time; + + // the time read jobs takes on average to complete + // (not including the time in the queue), in microseconds. This only measures + // read cache misses. + int average_read_time; + + // the time write jobs takes to complete, on average, + // in microseconds. This does not include the time the job sits in the disk job + // queue or in the write cache, only blocks that are flushed to disk. + int average_write_time; + + // the time hash jobs takes to complete on average, in + // microseconds. Hash jobs include running SHA-1 on the data (which for the most + // part is done incrementally) and sometimes reading back parts of the piece. It + // also includes checking files without valid resume data. + int average_hash_time; + int average_job_time; + int average_sort_time; + + // the number of jobs in the job queue. + int job_queue_length; + + // the number of milliseconds spent in all disk jobs, and specific ones + // since the start of the session. Times are specified in milliseconds + boost::uint32_t cumulative_job_time; + boost::uint32_t cumulative_read_time; + boost::uint32_t cumulative_write_time; + boost::uint32_t cumulative_hash_time; + boost::uint32_t cumulative_sort_time; + + // the number of blocks that had to be read back from disk because + // they were flushed before the SHA-1 hash got to hash them. If this + // is large, a larger cache could significantly improve performance + int total_read_back; + + // number of read jobs in the disk job queue + int read_queue_size; + }; + + // this is a singleton consisting of the thread and a queue + // of disk io jobs + struct TORRENT_EXTRA_EXPORT disk_io_thread : disk_buffer_pool + { + disk_io_thread(io_service& ios + , boost::function const& queue_callback + , file_pool& fp + , int block_size = 16 * 1024); + ~disk_io_thread(); + + void abort(); + void join(); + + // aborts read operations + void stop(boost::intrusive_ptr s); + + // returns the disk write queue size + int add_job(disk_io_job const& j + , boost::function const& f + = boost::function()); + + // keep track of the number of bytes in the job queue + // at any given time. i.e. the sum of all buffer_size. + // this is used to slow down the download global download + // speed when the queue buffer size is too big. + size_type queue_buffer_size() const; + bool can_write() const; + + void get_cache_info(sha1_hash const& ih + , std::vector& ret) const; + + cache_status status() const; + + void thread_fun(); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + struct cached_block_entry + { + cached_block_entry(): buf(0) {} + // the buffer pointer (this is a disk_pool buffer) + // or 0 + char* buf; + + // callback for when this block is flushed to disk + boost::function callback; + }; + + struct cached_piece_entry + { + int piece; + // storage this piece belongs to + boost::intrusive_ptr storage; + // the pointers to the block data + boost::shared_array blocks; + // the last time a block was writting to this piece + // plus the minimum amount of time the block is guaranteed + // to stay in the cache + ptime expire; + // the number of blocks in the cache for this piece + int num_blocks; + // used to determine if this piece should be flushed + int num_contiguous_blocks; + // this is the first block that has not yet been hashed + // by the partial hasher. When minimizing read-back, this + // is used to determine if flushing a range would force us + // to read it back later when hashing + int next_block_to_hash; + + std::pair storage_piece_pair() const + { return std::pair(storage.get(), piece); } + }; + + typedef multi_index_container< + cached_piece_entry, indexed_by< + ordered_unique + , &cached_piece_entry::storage_piece_pair> > + , ordered_non_unique > + > + > cache_t; + + typedef cache_t::nth_index<0>::type cache_piece_index_t; + typedef cache_t::nth_index<1>::type cache_lru_index_t; + + private: + + int add_job(disk_io_job const& j + , mutex::scoped_lock& l + , boost::function const& f + = boost::function()); + + bool test_error(disk_io_job& j); + void post_callback(disk_io_job const& j, int ret); + + // cache operations + cache_piece_index_t::iterator find_cached_piece( + cache_t& cache, disk_io_job const& j + , mutex::scoped_lock& l); + bool is_cache_hit(cached_piece_entry& p + , disk_io_job const& j, mutex::scoped_lock& l); + int copy_from_piece(cached_piece_entry& p, bool& hit + , disk_io_job const& j, mutex::scoped_lock& l); + + struct ignore_t + { + ignore_t(): piece(-1), storage(0) {} + ignore_t(int idx, piece_manager* st): piece(idx), storage(st) {} + int piece; + piece_manager* storage; + }; + + // write cache operations + enum options_t { dont_flush_write_blocks = 1, ignore_cache_size = 2 }; + int flush_cache_blocks(mutex::scoped_lock& l + , int blocks, ignore_t ignore = ignore_t(), int options = 0); + void flush_expired_pieces(); + int flush_contiguous_blocks(cached_piece_entry& p + , mutex::scoped_lock& l, int lower_limit = 0, bool avoid_readback = false); + int flush_range(cached_piece_entry& p, int start, int end, mutex::scoped_lock& l); + int cache_block(disk_io_job& j + , boost::function& handler + , int cache_expire + , mutex::scoped_lock& l); + + // read cache operations + int clear_oldest_read_piece(int num_blocks, ignore_t ignore + , mutex::scoped_lock& l); + int read_into_piece(cached_piece_entry& p, int start_block + , int options, int num_blocks, mutex::scoped_lock& l); + int cache_read_block(disk_io_job const& j, mutex::scoped_lock& l); + int free_piece(cached_piece_entry& p, mutex::scoped_lock& l); + int drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l); + + enum cache_flags_t { + cache_only = 1 + }; + int try_read_from_cache(disk_io_job const& j, bool& hit, int flags = 0); + int read_piece_from_cache_and_hash(disk_io_job const& j, sha1_hash& h); + int cache_piece(disk_io_job const& j, cache_piece_index_t::iterator& p + , bool& hit, int options, mutex::scoped_lock& l); + + // this mutex only protects m_jobs, m_queue_buffer_size, + // m_exceeded_write_queue and m_abort + mutable mutex m_queue_mutex; + event m_signal; + bool m_abort; + bool m_waiting_to_shutdown; + std::deque m_jobs; + size_type m_queue_buffer_size; + + ptime m_last_file_check; + + // this protects the piece cache and related members + mutable mutex m_piece_mutex; + // write cache + cache_t m_pieces; + + // read cache + cache_t m_read_pieces; + + void flip_stats(ptime now); + + // total number of blocks in use by both the read + // and the write cache. This is not supposed to + // exceed m_cache_size + cache_status m_cache_stats; + + // keeps average queue time for disk jobs (in microseconds) + average_accumulator m_queue_time; + + // average read time for cache misses (in microseconds) + average_accumulator m_read_time; + + // average write time (in microseconds) + average_accumulator m_write_time; + + // average hash time (in microseconds) + average_accumulator m_hash_time; + + // average time to serve a job (any job) in microseconds + average_accumulator m_job_time; + + // average time to ask for physical offset on disk + // and insert into queue + average_accumulator m_sort_time; + + // the last time we reset the average time and store the + // latest value in m_cache_stats + ptime m_last_stats_flip; + + typedef std::multimap read_jobs_t; + read_jobs_t m_sorted_read_jobs; + +#ifdef TORRENT_DISK_STATS + std::ofstream m_log; +#endif + + // the amount of physical ram in the machine + boost::uint64_t m_physical_ram; + + // if we exceeded the max queue disk write size + // this is set to true. It remains true until the + // queue is smaller than the low watermark + bool m_exceeded_write_queue; + + io_service& m_ios; + + boost::function m_queue_callback; + + // this keeps the io_service::run() call blocked from + // returning. When shutting down, it's possible that + // the event queue is drained before the disk_io_thread + // has posted its last callback. When this happens, the + // io_service will have a pending callback from the + // disk_io_thread, but the event loop is not running. + // this means that the event is destructed after the + // disk_io_thread. If the event refers to a disk buffer + // it will try to free it, but the buffer pool won't + // exist anymore, and crash. This prevents that. + boost::optional m_work; + + // reference to the file_pool which is a member of + // the session_impl object + file_pool& m_file_pool; + + // when completion notifications are queued, they're stuck + // in this list + std::list > m_queued_completions; + +#if TORRENT_USE_ASSERTS + int m_magic; +#endif + + // thread for performing blocking disk io operations + thread m_disk_io_thread; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp new file mode 100644 index 0000000000..9701680774 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ed25519.hpp @@ -0,0 +1,32 @@ +#ifndef ED25519_HPP +#define ED25519_HPP + +#include "libtorrent/export.hpp" // for TORRENT_EXPORT +#include // for size_t + +enum +{ + ed25519_seed_size = 32, + ed25519_private_key_size = 64, + ed25519_public_key_size = 32, + ed25519_signature_size = 64, + ed25519_scalar_size = 32, + ed25519_shared_secret_size = 32 +}; + +extern "C" { + +#ifndef ED25519_NO_SEED +int TORRENT_EXPORT ed25519_create_seed(unsigned char *seed); +#endif + +void TORRENT_EXPORT ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +void TORRENT_EXPORT ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); +int TORRENT_EXPORT ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key); +void TORRENT_EXPORT ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); +void TORRENT_EXPORT ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); + +} + +#endif // ED25519_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp new file mode 100644 index 0000000000..1b3a17d230 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/entry.hpp @@ -0,0 +1,310 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ENTRY_HPP_INCLUDED +#define TORRENT_ENTRY_HPP_INCLUDED + +/* + * + * This file declares the entry class. It is a + * variant-type that can be an integer, list, + * dictionary (map) or a string. This type is + * used to hold bdecoded data (which is the + * encoding BitTorrent messages uses). + * + * it has 4 accessors to access the actual + * type of the object. They are: + * integer() + * string() + * list() + * dict() + * The actual type has to match the type you + * are asking for, otherwise you will get an + * assertion failure. + * When you default construct an entry, it is + * uninitialized. You can initialize it through the + * assignment operator, copy-constructor or + * the constructor that takes a data_type enum. + * + * + */ + + +#include +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/max.hpp" + +#if TORRENT_USE_IOSTREAM +#include +#endif + +namespace libtorrent +{ + struct lazy_entry; + + // thrown by any accessor function of entry if the accessor + // function requires a type different than the actual type + // of the entry object. + struct TORRENT_EXPORT type_error: std::runtime_error + { + // internal + type_error(const char* error): std::runtime_error(error) {} + }; + + // The ``entry`` class represents one node in a bencoded hierarchy. It works as a + // variant type, it can be either a list, a dictionary (``std::map``), an integer + // or a string. + class TORRENT_EXPORT entry + { + public: + + // the key is always a string. If a generic entry would be allowed + // as a key, sorting would become a problem (e.g. to compare a string + // to a list). The definition doesn't mention such a limit though. + typedef std::map dictionary_type; + typedef std::string string_type; + typedef std::list list_type; + typedef size_type integer_type; + + // the types an entry can have + enum data_type + { + int_t, + string_t, + list_t, + dictionary_t, + undefined_t + }; + + // returns the concrete type of the entry + data_type type() const; + + // constructors directly from a specific type. + // The content of the argument is copied into the + // newly constructed entry + entry(dictionary_type const&); + entry(string_type const&); + entry(list_type const&); + entry(integer_type const&); + + // construct an empty entry of the specified type. + // see data_type enum. + entry(data_type t); + + // hidden + entry(entry const& e); + + // hidden + entry(); + + // hidden + ~entry(); + + // hidden + bool operator==(entry const& e) const; + bool operator!=(entry const& e) const { return !(*this == e); } + + // copies the structure of the right hand side into this + // entry. + void operator=(lazy_entry const&); + void operator=(entry const&); + void operator=(dictionary_type const&); + void operator=(string_type const&); + void operator=(list_type const&); + void operator=(integer_type const&); + + // The ``integer()``, ``string()``, ``list()`` and ``dict()`` functions + // are accessors that return the respective type. If the ``entry`` object + // isn't of the type you request, the accessor will throw + // libtorrent_exception (which derives from ``std::runtime_error``). You + // can ask an ``entry`` for its type through the ``type()`` function. + // + // If you want to create an ``entry`` you give it the type you want it to + // have in its constructor, and then use one of the non-const accessors + // to get a reference which you then can assign the value you want it to + // have. + // + // The typical code to get info from a torrent file will then look like + // this: + // + // .. code:: c++ + // + // entry torrent_file; + // // ... + // + // // throws if this is not a dictionary + // entry::dictionary_type const& dict = torrent_file.dict(); + // entry::dictionary_type::const_iterator i; + // i = dict.find("announce"); + // if (i != dict.end()) + // { + // std::string tracker_url = i->second.string(); + // std::cout << tracker_url << "\n"; + // } + // + // + // The following code is equivalent, but a little bit shorter: + // + // .. code:: c++ + // + // entry torrent_file; + // // ... + // + // // throws if this is not a dictionary + // if (entry* i = torrent_file.find_key("announce")) + // { + // std::string tracker_url = i->string(); + // std::cout << tracker_url << "\n"; + // } + // + // + // To make it easier to extract information from a torrent file, the + // class torrent_info exists. + integer_type& integer(); + const integer_type& integer() const; + string_type& string(); + const string_type& string() const; + list_type& list(); + const list_type& list() const; + dictionary_type& dict(); + const dictionary_type& dict() const; + + // swaps the content of *this* with ``e``. + void swap(entry& e); + + // All of these functions requires the entry to be a dictionary, if it + // isn't they will throw ``libtorrent::type_error``. + // + // The non-const versions of the ``operator[]`` will return a reference + // to either the existing element at the given key or, if there is no + // element with the given key, a reference to a newly inserted element at + // that key. + // + // The const version of ``operator[]`` will only return a reference to an + // existing element at the given key. If the key is not found, it will + // throw ``libtorrent::type_error``. + entry& operator[](char const* key); + entry& operator[](std::string const& key); +#ifndef BOOST_NO_EXCEPTIONS + const entry& operator[](char const* key) const; + const entry& operator[](std::string const& key) const; +#endif + + // These functions requires the entry to be a dictionary, if it isn't + // they will throw ``libtorrent::type_error``. + // + // They will look for an element at the given key in the dictionary, if + // the element cannot be found, they will return 0. If an element with + // the given key is found, the return a pointer to it. + entry* find_key(char const* key); + entry const* find_key(char const* key) const; + entry* find_key(std::string const& key); + entry const* find_key(std::string const& key) const; + + // returns a pretty-printed string representation + // of the bencoded structure, with JSON-style syntax + std::string to_string() const; + + protected: + + void construct(data_type t); + void copy(const entry& e); + void destruct(); + + private: + + void to_string_impl(std::string& out, int indent) const; + +#if (defined(_MSC_VER) && _MSC_VER < 1310) || TORRENT_COMPLETE_TYPES_REQUIRED + // workaround for msvc-bug. + // assumes sizeof(map) == sizeof(map) + // and sizeof(list) == sizeof(list) + enum { union_size + = max4) + , sizeof(std::map) + , sizeof(string_type) + , sizeof(integer_type)>::value + }; +#else + enum { union_size + = max4::value + }; +#endif + integer_type data[(union_size + sizeof(integer_type) - 1) + / sizeof(integer_type)]; + + // the bitfield is used so that the m_type_queried field still fits, so + // that the ABI is the same for debug builds and release builds. It + // appears to be very hard to match debug builds with debug versions of + // libtorrent + boost::uint8_t m_type:7; + + public: + // in debug mode this is set to false by bdecode to indicate that the + // program has not yet queried the type of this entry, and sould not + // assume that it has a certain type. This is asserted in the accessor + // functions. This does not apply if exceptions are used. + mutable boost::uint8_t m_type_queried:1; + }; + +#if TORRENT_USE_IOSTREAM + // prints the bencoded structure to the ostream as a JSON-style structure. + inline std::ostream& operator<<(std::ostream& os, const entry& e) + { + os << e.to_string(); + return os; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + // internal + inline void throw_type_error() + { + throw libtorrent_exception(error_code(errors::invalid_entry_type + , get_libtorrent_category())); + } +#endif + +} + +#endif // TORRENT_ENTRY_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp new file mode 100644 index 0000000000..7866211656 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/enum_net.hpp @@ -0,0 +1,83 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ENUM_NET_HPP_INCLUDED +#define TORRENT_ENUM_NET_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + + // the interface should not have a netmask + struct ip_interface + { + address interface_address; + address netmask; + char name[64]; + int mtu; + }; + + struct ip_route + { + address destination; + address netmask; + address gateway; + char name[64]; + int mtu; + }; + + // returns a list of the configured IP interfaces + // on the machine + TORRENT_EXTRA_EXPORT std::vector enum_net_interfaces(io_service& ios + , error_code& ec); + + TORRENT_EXTRA_EXPORT std::vector enum_routes(io_service& ios, error_code& ec); + + // return (a1 & mask) == (a2 & mask) + TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1, address const& a2, address const& mask); + + // returns true if the specified address is on the same + // local network as us + TORRENT_EXTRA_EXPORT bool in_local_network(io_service& ios, address const& addr + , error_code& ec); + + TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios + , error_code& ec); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp new file mode 100644 index 0000000000..8812e57a2e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/error.hpp @@ -0,0 +1,62 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ERROR_HPP_INCLUDED +#define TORRENT_ERROR_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + namespace error = asio::error; +#else + namespace error = boost::asio::error; +#endif + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp new file mode 100644 index 0000000000..3a2fa64e31 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/error_code.hpp @@ -0,0 +1,562 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ERROR_CODE_HPP_INCLUDED +#define TORRENT_ERROR_CODE_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include // free + +#ifndef BOOST_SYSTEM_NOEXCEPT +#define BOOST_SYSTEM_NOEXCEPT throw() +#endif + +namespace libtorrent +{ + + namespace errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_libtorrent_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + // Two torrents has files which end up overwriting each other + file_collision, + // A piece did not match its piece hash + failed_hash_check, + // The .torrent file does not contain a bencoded dictionary at + // its top level + torrent_is_no_dict, + // The .torrent file does not have an ``info`` dictionary + torrent_missing_info, + // The .torrent file's ``info`` entry is not a dictionary + torrent_info_no_dict, + // The .torrent file does not have a ``piece length`` entry + torrent_missing_piece_length, + // The .torrent file does not have a ``name`` entry + torrent_missing_name, + // The .torrent file's name entry is invalid + torrent_invalid_name, + // The length of a file, or of the whole .torrent file is invalid. + // Either negative or not an integer + torrent_invalid_length, + // Failed to parse a file entry in the .torrent + torrent_file_parse_failed, + // The ``pieces`` field is missing or invalid in the .torrent file + torrent_missing_pieces, + // The ``pieces`` string has incorrect length + torrent_invalid_hashes, + // The .torrent file has more pieces than is supported by libtorrent + too_many_pieces_in_torrent, + // The metadata (.torrent file) that was received from the swarm + // matched the info-hash, but failed to be parsed + invalid_swarm_metadata, + // The file or buffer is not correctly bencoded + invalid_bencoding, + // The .torrent file does not contain any files + no_files_in_torrent, + // The string was not properly url-encoded as expected + invalid_escaped_string, + // Operation is not permitted since the session is shutting down + session_is_closing, + // There's already a torrent with that info-hash added to the + // session + duplicate_torrent, + // The supplied torrent_handle is not referring to a valid torrent + invalid_torrent_handle, + // The type requested from the entry did not match its type + invalid_entry_type, + // The specified URI does not contain a valid info-hash + missing_info_hash_in_uri, + // One of the files in the torrent was unexpectadly small. This + // might be caused by files being changed by an external process + file_too_short, + // The URL used an unknown protocol. Currently ``http`` and + // ``https`` (if built with openssl support) are recognized. For + // trackers ``udp`` is recognized as well. + unsupported_url_protocol, + // The URL did not conform to URL syntax and failed to be parsed + url_parse_error, + // The peer sent a 'piece' message of length 0 + peer_sent_empty_piece, + // A bencoded structure was currupt and failed to be parsed + parse_failed, + // The fast resume file was missing or had an invalid file version + // tag + invalid_file_tag, + // The fast resume file was missing or had an invalid info-hash + missing_info_hash, + // The info-hash did not match the torrent + mismatching_info_hash, + // The URL contained an invalid hostname + invalid_hostname, + // The URL had an invalid port + invalid_port, + // The port is blocked by the port-filter, and prevented the + // connection + port_blocked, + // The IPv6 address was expected to end with ']' + expected_close_bracket_in_address, + // The torrent is being destructed, preventing the operation to + // succeed + destructing_torrent, + // The connection timed out + timed_out, + // The peer is upload only, and we are upload only. There's no point + // in keeping the connection + upload_upload_connection, + // The peer is upload only, and we're not interested in it. There's + // no point in keeping the connection + uninteresting_upload_peer, + // The peer sent an unknown info-hash + invalid_info_hash, + // The torrent is paused, preventing the operation from succeeding + torrent_paused, + // The peer sent an invalid have message, either wrong size or + // referring to a piece that doesn't exist in the torrent + invalid_have, + // The bitfield message had the incorrect size + invalid_bitfield_size, + // The peer kept requesting pieces after it was choked, possible + // abuse attempt. + too_many_requests_when_choked, + // The peer sent a piece message that does not correspond to a + // piece request sent by the client + invalid_piece, + // memory allocation failed + no_memory, + // The torrent is aborted, preventing the operation to succeed + torrent_aborted, + // The peer is a connection to ourself, no point in keeping it + self_connection, + // The peer sent a piece message with invalid size, either negative + // or greater than one block + invalid_piece_size, + // The peer has not been interesting or interested in us for too + // long, no point in keeping it around + timed_out_no_interest, + // The peer has not said anything in a long time, possibly dead + timed_out_inactivity, + // The peer did not send a handshake within a reasonable amount of + // time, it might not be a bittorrent peer + timed_out_no_handshake, + // The peer has been unchoked for too long without requesting any + // data. It might be lying about its interest in us + timed_out_no_request, + // The peer sent an invalid choke message + invalid_choke, + // The peer send an invalid unchoke message + invalid_unchoke, + // The peer sent an invalid interested message + invalid_interested, + // The peer sent an invalid not-interested message + invalid_not_interested, + // The peer sent an invalid piece request message + invalid_request, + // The peer sent an invalid hash-list message (this is part of the + // merkle-torrent extension) + invalid_hash_list, + // The peer sent an invalid hash-piece message (this is part of the + // merkle-torrent extension) + invalid_hash_piece, + // The peer sent an invalid cancel message + invalid_cancel, + // The peer sent an invalid DHT port-message + invalid_dht_port, + // The peer sent an invalid suggest piece-message + invalid_suggest, + // The peer sent an invalid have all-message + invalid_have_all, + // The peer sent an invalid have none-message + invalid_have_none, + // The peer sent an invalid reject message + invalid_reject, + // The peer sent an invalid allow fast-message + invalid_allow_fast, + // The peer sent an invalid extesion message ID + invalid_extended, + // The peer sent an invalid message ID + invalid_message, + // The synchronization hash was not found in the encrypted handshake + sync_hash_not_found, + // The encryption constant in the handshake is invalid + invalid_encryption_constant, + // The peer does not support plaintext, which is the selected mode + no_plaintext_mode, + // The peer does not support rc4, which is the selected mode + no_rc4_mode, + // The peer does not support any of the encryption modes that the + // client supports + unsupported_encryption_mode, + // The peer selected an encryption mode that the client did not + // advertise and does not support + unsupported_encryption_mode_selected, + // The pad size used in the encryption handshake is of invalid size + invalid_pad_size, + // The encryption handshake is invalid + invalid_encrypt_handshake, + // The client is set to not support incoming encrypted connections + // and this is an encrypted connection + no_incoming_encrypted, + // The client is set to not support incoming regular bittorrent + // connections, and this is a regular connection + no_incoming_regular, + // The client is already connected to this peer-ID + duplicate_peer_id, + // Torrent was removed + torrent_removed, + // The packet size exceeded the upper sanity check-limit + packet_too_large, + + reserved, + + // The web server responded with an error + http_error, + // The web server response is missing a location header + missing_location, + // The web seed redirected to a path that no longer matches the + // .torrent directory structure + invalid_redirection, + // The connection was closed becaused it redirected to a different + // URL + redirecting, + // The HTTP range header is invalid + invalid_range, + // The HTTP response did not have a content length + no_content_length, + // The IP is blocked by the IP filter + banned_by_ip_filter, + // At the connection limit + too_many_connections, + // The peer is marked as banned + peer_banned, + // The torrent is stopping, causing the operation to fail + stopping_torrent, + // The peer has sent too many corrupt pieces and is banned + too_many_corrupt_pieces, + // The torrent is not ready to receive peers + torrent_not_ready, + // The peer is not completely constructed yet + peer_not_constructed, + // The session is closing, causing the operation to fail + session_closing, + // The peer was disconnected in order to leave room for a + // potentially better peer + optimistic_disconnect, + // The torrent is finished + torrent_finished, + // No UPnP router found + no_router, + // The metadata message says the metadata exceeds the limit + metadata_too_large, + // The peer sent an invalid metadata request message + invalid_metadata_request, + // The peer advertised an invalid metadata size + invalid_metadata_size, + // The peer sent a message with an invalid metadata offset + invalid_metadata_offset, + // The peer sent an invalid metadata message + invalid_metadata_message, + // The peer sent a peer exchange message that was too large + pex_message_too_large, + // The peer sent an invalid peer exchange message + invalid_pex_message, + // The peer sent an invalid tracker exchange message + invalid_lt_tracker_message, + // The peer sent an pex messages too often. This is a possible + // attempt of and attack + too_frequent_pex, + // The operation failed because it requires the torrent to have + // the metadata (.torrent file) and it doesn't have it yet. + // This happens for magnet links before they have downloaded the + // metadata, and also torrents added by URL. + no_metadata, + // The peer sent an invalid ``dont_have`` message. The dont have + // message is an extension to allow peers to advertise that the + // no longer has a piece they previously had. + invalid_dont_have, + // The peer tried to connect to an SSL torrent without connecting + // over SSL. + requires_ssl_connection, + // The peer tried to connect to a torrent with a certificate + // for a different torrent. + invalid_ssl_cert, + // the torrent is not an SSL torrent, and the operation requires + // an SSL torrent + not_an_ssl_torrent, + + + // The NAT-PMP router responded with an unsupported protocol version + unsupported_protocol_version = 120, + // You are not authorized to map ports on this NAT-PMP router + natpmp_not_authorized, + // The NAT-PMP router failed because of a network failure + network_failure, + // The NAT-PMP router failed because of lack of resources + no_resources, + // The NAT-PMP router failed because an unsupported opcode was sent + unsupported_opcode, + + + + // The resume data file is missing the 'file sizes' entry + missing_file_sizes = 130, + // The resume data file 'file sizes' entry is empty + no_files_in_resume_data, + // The resume data file is missing the 'pieces' and 'slots' entry + missing_pieces, + // The number of files in the resume data does not match the number + // of files in the torrent + mismatching_number_of_files, + // One of the files on disk has a different size than in the fast + // resume file + mismatching_file_size, + // One of the files on disk has a different timestamp than in the + // fast resume file + mismatching_file_timestamp, + // The resume data file is not a dictionary + not_a_dictionary, + // The 'blocks per piece' entry is invalid in the resume data file + invalid_blocks_per_piece, + // The resume file is missing the 'slots' entry, which is required + // for torrents with compact allocation + missing_slots, + // The resume file contains more slots than the torrent + too_many_slots, + // The 'slot' entry is invalid in the resume data + invalid_slot_list, + // One index in the 'slot' list is invalid + invalid_piece_index, + // The pieces on disk needs to be re-ordered for the specified + // allocation mode. This happens if you specify sparse allocation + // and the files on disk are using compact storage. The pieces needs + // to be moved to their right position + pieces_need_reorder, + + + + // The HTTP header was not correctly formatted + http_parse_error = 150, + // The HTTP response was in the 300-399 range but lacked a location + // header + http_missing_location, + // The HTTP response was encoded with gzip or deflate but + // decompressing it failed + http_failed_decompress, + + + + // The URL specified an i2p address, but no i2p router is configured + no_i2p_router = 160, + + + + // The tracker URL doesn't support transforming it into a scrape + // URL. i.e. it doesn't contain "announce. + scrape_not_available = 170, + // invalid tracker response + invalid_tracker_response, + // invalid peer dictionary entry. Not a dictionary + invalid_peer_dict, + // tracker sent a failure message + tracker_failure, + // missing or invalid 'files' entry + invalid_files_entry, + // missing or invalid 'hash' entry + invalid_hash_entry, + // missing or invalid 'peers' and 'peers6' entry + invalid_peers_entry, + // udp tracker response packet has invalid size + invalid_tracker_response_length, + // invalid transaction id in udp tracker response + invalid_tracker_transaction_id, + // invalid action field in udp tracker response + invalid_tracker_action, + +#ifndef TORRENT_NO_DEPRECATE + // expected string in bencoded string + expected_string = 190, + // expected colon in bencoded string + expected_colon, + // unexpected end of file in bencoded string + unexpected_eof, + // expected value (list, dict, int or string) in bencoded string + expected_value, + // bencoded recursion depth limit exceeded + depth_exceeded, + // bencoded item count limit exceeded + limit_exceeded, + // integer overflow + overflow, +#endif + + // the number of error codes + error_code_max + }; + + // HTTP errors are reported in the libtorrent::http_category, with error code enums in + // the ``libtorrent::errors`` namespace. + enum http_errors + { + cont = 100, + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + + } // namespace errors + +#if BOOST_VERSION < 103500 + typedef asio::error_code error_code; + // hidden + inline asio::error::error_category get_posix_category() + { return asio::error::system_category; } + // hidden + inline asio::error::error_category get_system_category() + { return asio::error::system_category; } + + // hidden + boost::system::error_category const& get_libtorrent_category() + { + static ::asio::error::error_category libtorrent_category(20); + return libtorrent_category; + } + + // hidden + boost::system::error_category const& get_http_category() + { + static ::asio::error::error_category http_category(21); + return http_category; + } + +#else + + // return the instance of the libtorrent_error_category which + // maps libtorrent error codes to human readable error messages. + TORRENT_EXPORT boost::system::error_category& get_libtorrent_category(); + + // returns the error_category for HTTP errors + TORRENT_EXPORT boost::system::error_category& get_http_category(); + + using boost::system::error_code; + + // hidden + inline boost::system::error_category const& get_system_category() +#if BOOST_VERSION < 104400 + { return boost::system::get_system_category(); } +#else + { return boost::system::system_category(); } +#endif + + // hidden + inline boost::system::error_category const& get_posix_category() +#if BOOST_VERSION < 103600 + { return boost::system::get_posix_category(); } +#elif BOOST_VERSION < 104400 + { return boost::system::get_generic_category(); } +#else + { return boost::system::generic_category(); } +#endif // BOOST_VERSION < 103600 +#endif // BOOST_VERSION < 103500 + + // internal + inline boost::system::error_category const& generic_category() + { return get_posix_category(); } + +#ifndef BOOST_NO_EXCEPTIONS + struct TORRENT_EXPORT libtorrent_exception: std::exception + { + libtorrent_exception(error_code const& s): m_error(s), m_msg(0) {} + virtual const char* what() const throw(); + virtual ~libtorrent_exception() throw(); + error_code error() const { return m_error; } + private: + error_code m_error; + mutable char* m_msg; + }; +#endif +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp new file mode 100644 index 0000000000..ffe59cd423 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/escape_string.hpp @@ -0,0 +1,111 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ESCAPE_STRING_HPP_INCLUDED +#define TORRENT_ESCAPE_STRING_HPP_INCLUDED + +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT boost::array::digits10> to_string(size_type n); + + TORRENT_EXTRA_EXPORT std::string unescape_string(std::string const& s, error_code& ec); + // replaces all disallowed URL characters by their %-encoding + TORRENT_EXTRA_EXPORT std::string escape_string(const char* str, int len); + // same as escape_string but does not encode '/' + TORRENT_EXTRA_EXPORT std::string escape_path(const char* str, int len); + // if the url does not appear to be encoded, and it contains illegal url characters + // it will be encoded + TORRENT_EXTRA_EXPORT std::string maybe_url_encode(std::string const& url); + + // returns true if the given string (not null terminated) contains + // characters that would need to be escaped if used in a URL + TORRENT_EXTRA_EXPORT bool need_encoding(char const* str, int len); + + // encodes a string using the base64 scheme + TORRENT_EXTRA_EXPORT std::string base64encode(std::string const& s); + // encodes a string using the base32 scheme + TORRENT_EXTRA_EXPORT std::string base32encode(std::string const& s); + TORRENT_EXTRA_EXPORT std::string base32decode(std::string const& s); + + TORRENT_EXTRA_EXPORT std::string url_has_argument( + std::string const& url, std::string argument, std::string::size_type* out_pos = 0); + + // replaces \ with / + TORRENT_EXTRA_EXPORT void convert_path_to_posix(std::string& path); + + TORRENT_EXTRA_EXPORT std::string read_until(char const*& str, char delim, char const* end); + TORRENT_EXTRA_EXPORT int hex_to_int(char in); + + TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len); + + // converts (binary) the string ``s`` to hexadecimal representation and + // returns it. + TORRENT_EXPORT std::string to_hex(std::string const& s); + + // converts the binary buffer [``in``, ``in`` + len) to hexadecimal + // and prints it to the buffer ``out``. The caller is responsible for + // making sure the buffer pointed to by ``out`` is large enough, + // i.e. has at least len * 2 bytes of space. + TORRENT_EXPORT void to_hex(char const *in, int len, char* out); + + // converts the buffer [``in``, ``in`` + len) from hexadecimal to + // binary. The binary output is written to the buffer pointed to + // by ``out``. The caller is responsible for making sure the buffer + // at ``out`` has enough space for the result to be written to, i.e. + // (len + 1) / 2 bytes. + TORRENT_EXPORT bool from_hex(char const *in, int len, char* out); + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + TORRENT_EXTRA_EXPORT std::wstring convert_to_wstring(std::string const& s); + TORRENT_EXTRA_EXPORT std::string convert_from_wstring(std::wstring const& s); +#endif + +#if TORRENT_USE_ICONV || TORRENT_USE_LOCALE || defined TORRENT_WINDOWS + TORRENT_EXTRA_EXPORT std::string convert_to_native(std::string const& s); + TORRENT_EXTRA_EXPORT std::string convert_from_native(std::string const& s); +#else + // internal + inline std::string const& convert_to_native(std::string const& s) { return s; } + // internal + inline std::string const& convert_from_native(std::string const& s) { return s; } +#endif +} + +#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp new file mode 100644 index 0000000000..cef3bbef39 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/export.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_EXPORT_HPP_INCLUDED +#define TORRENT_EXPORT_HPP_INCLUDED + +#if !defined(BOOST_COMPILER_CONFIG) && !defined(BOOST_NO_COMPILER_CONFIG) +# include +#endif +#ifdef BOOST_COMPILER_CONFIG +# include BOOST_COMPILER_CONFIG +#endif + +#if !defined(BOOST_PLATFORM_CONFIG) && !defined(BOOST_NO_PLATFORM_CONFIG) +# include +#endif +#ifdef BOOST_PLATFORM_CONFIG +# include BOOST_PLATFORM_CONFIG +#endif + +// backwards compatibility with older versions of boost +#if !defined BOOST_SYMBOL_EXPORT && !defined BOOST_SYMBOL_IMPORT +# if defined _MSC_VER || defined __MINGW32__ +# define BOOST_SYMBOL_EXPORT __declspec(dllexport) +# define BOOST_SYMBOL_IMPORT __declspec(dllimport) +# elif __GNU__ >= 4 +# define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) +# define BOOST_SYMBOL_IMPORT __attribute__((visibility("default"))) +# else +# define BOOST_SYMBOL_EXPORT +# define BOOST_SYMBOL_IMPORT +# endif +#endif + +#if defined TORRENT_BUILDING_SHARED +# define TORRENT_EXPORT BOOST_SYMBOL_EXPORT +#elif defined TORRENT_LINKING_SHARED +# define TORRENT_EXPORT BOOST_SYMBOL_IMPORT +#endif + +// when this is specified, export a bunch of extra +// symbols, mostly for the unit tests to reach +#if TORRENT_EXPORT_EXTRA +# if defined TORRENT_BUILDING_SHARED +# define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_EXPORT +# elif defined TORRENT_LINKING_SHARED +# define TORRENT_EXTRA_EXPORT BOOST_SYMBOL_IMPORT +# endif +#endif + +#ifndef TORRENT_EXPORT +# define TORRENT_EXPORT +#endif + +#ifndef TORRENT_EXTRA_EXPORT +# define TORRENT_EXTRA_EXPORT +#endif + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp new file mode 100644 index 0000000000..789cfb5882 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions.hpp @@ -0,0 +1,440 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_EXTENSIONS_HPP_INCLUDED +#define TORRENT_EXTENSIONS_HPP_INCLUDED + +// OVERVIEW +// +// libtorrent has a plugin interface for implementing extensions to the protocol. +// These can be general extensions for transferring metadata or peer exchange +// extensions, or it could be used to provide a way to customize the protocol +// to fit a particular (closed) network. +// +// In short, the plugin interface makes it possible to: +// +// * register extension messages (sent in the extension handshake), see +// extensions_. +// * add data and parse data from the extension handshake. +// * send extension messages and standard bittorrent messages. +// * override or block the handling of standard bittorrent messages. +// * save and restore state via the session state +// * see all alerts that are posted +// +// .. _extensions: extension_protocol.html +// +// a word of caution +// ----------------- +// +// Writing your own plugin is a very easy way to introduce serious bugs such as +// dead locks and race conditions. Since a plugin has access to internal +// structures it is also quite easy to sabotage libtorrent's operation. +// +// All the callbacks in this interface are called with the main libtorrent thread +// mutex locked. And they are always called from the libtorrent network thread. In +// case portions of your plugin are called from other threads, typically the main +// thread, you cannot use any of the member functions on the internal structures +// in libtorrent, since those require the mutex to be locked. Futhermore, you would +// also need to have a mutex on your own shared data within the plugin, to make +// sure it is not accessed at the same time from the libtorrent thread (through a +// callback). See `boost thread's mutex`_. If you need to send out a message from +// another thread, it is advised to use an internal queue, and do the actual +// sending in ``tick()``. +// +// Since the plugin interface gives you easy access to internal structures, it +// is not supported as a stable API. Plugins should be considered spcific to a +// specific version of libtorrent. Although, in practice the internals mostly +// don't change that dramatically. +// +// .. _`boost thread's mutex`: http://www.boost.org/doc/html/mutex.html +// +// +// plugin-interface +// ================ +// +// The plugin interface consists of three base classes that the plugin may +// implement. These are called plugin, torrent_plugin and peer_plugin. +// They are found in the ```` header. +// +// These plugins are instantiated for each session, torrent and possibly each peer, +// respectively. +// +// For plugins that only need per torrent state, it is enough to only implement +// ``torrent_plugin`` and pass a constructor function or function object to +// ``session::add_extension()`` or ``torrent_handle::add_extension()`` (if the +// torrent has already been started and you want to hook in the extension at +// run-time). +// +// The signature of the function is:: +// +// boost::shared_ptr (*)(torrent*, void*); +// +// The first argument is the internal torrent object, the second argument +// is the userdata passed to ``session::add_torrent()`` or +// ``torrent_handle::add_extension()``. +// +// The function should return a ``boost::shared_ptr`` which +// may or may not be 0. If it is a null pointer, the extension is simply ignored +// for this torrent. If it is a valid pointer (to a class inheriting +// ``torrent_plugin``), it will be associated with this torrent and callbacks +// will be made on torrent events. +// +// For more elaborate plugins which require session wide state, you would +// implement ``plugin``, construct an object (in a ``boost::shared_ptr``) and pass +// it in to ``session::add_extension()``. +// +// custom alerts +// ============= +// +// Since plugins are running within internal libtorrent threads, one convenient +// way to communicate with the client is to post custom alerts. +// +// The expected interface of any alert, apart from deriving from the alert +// base class, looks like this: +// +// .. parsed-literal:: +// +// const static int alert_type = **; +// virtual int type() const { return alert_type; } +// +// virtual std::string message() const; +// +// virtual std::auto_ptr clone() const +// { return std::auto_ptr(new name(\*this)); } +// +// const static int static_category = **; +// virtual int category() const { return static_category; } +// +// virtual char const* what() const { return **; } +// +// The ``alert_type`` is used for the type-checking in ``alert_cast``. It must +// not collide with any other alert. The built-in alerts in libtorrent will +// not use alert type IDs greater than ``user_alert_id``. When defining your +// own alert, make sure it's greater than this constant. +// +// ``type()`` is the run-time equivalence of the ``alert_type``. +// +// The ``message()`` virtual function is expected to construct a useful +// string representation of the alert and the event or data it represents. +// Something convenient to put in a log file for instance. +// +// ``clone()`` is used internally to copy alerts. The suggested implementation +// of simply allocating a new instance as a copy of ``*this`` is all that's +// expected. +// +// The static category is required for checking wether or not the category +// for a specific alert is enabled or not, without instantiating the alert. +// The ``category`` virtual function is the run-time equivalence. +// +// The ``what()`` virtual function may simply be a string literal of the class +// name of your alert. +// +// For more information, see the `alert section`_. +// +// .. _`alert section`: reference-Alerts.html + + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/policy.hpp" // for policy::peer + +namespace libtorrent +{ + namespace aux { struct session_impl; } + + struct peer_plugin; + class bt_peer_connection; + struct peer_request; + class peer_connection; + class entry; + struct lazy_entry; + struct disk_buffer_holder; + struct bitfield; + class alert; + struct torrent_plugin; + class torrent; + struct torrent_peer; + + // this is the base class for a session plugin. One primary feature + // is that it is notified of all torrents that are added to the session, + // and can add its own torrent_plugins. + struct TORRENT_EXPORT plugin + { + // hidden + virtual ~plugin() {} + + // this is called by the session every time a new torrent is added. + // The ``torrent*`` points to the internal torrent object created + // for the new torrent. The ``void*`` is the userdata pointer as + // passed in via add_torrent_params. + // + // If the plugin returns a torrent_plugin instance, it will be added + // to the new torrent. Otherwise, return an empty shared_ptr to a + // torrent_plugin (the default). + virtual boost::shared_ptr new_torrent(torrent*, void*) + { return boost::shared_ptr(); } + + // called when plugin is added to a session + virtual void added(aux::session_impl*) {} + + // called when an alert is posted + // alerts that are filtered are not + // posted + virtual void on_alert(alert const*) {} + + // called once per second + virtual void on_tick() {} + + // called when choosing peers to optimisticly unchoke + // peer's will be unchoked in the order they appear in the given + // vector which is initiallity sorted by when they were last + // optimistically unchoked. + // if the plugin returns true then the ordering provided will be + // used and no other plugin will be allowed to change it. + virtual bool on_optimistic_unchoke(std::vector& /* peers */) + { return false; } + + // called when saving settings state + virtual void save_state(entry&) const {} + + // called when loading settings state + virtual void load_state(lazy_entry const&) {} + }; + + // Torrent plugins are associated with a single torrent and have a number + // of functions called at certain events. Many of its functions have the + // ability to change or override the default libtorrent behavior. + struct TORRENT_EXPORT torrent_plugin + { + // hidden + virtual ~torrent_plugin() {} + + // This function is called each time a new peer is connected to the torrent. You + // may choose to ignore this by just returning a default constructed + // ``shared_ptr`` (in which case you don't need to override this member + // function). + // + // If you need an extension to the peer connection (which most plugins do) you + // are supposed to return an instance of your peer_plugin class. Which in + // turn will have its hook functions called on event specific to that peer. + // + // The ``peer_connection`` will be valid as long as the ``shared_ptr`` is being + // held by the torrent object. So, it is generally a good idea to not keep a + // ``shared_ptr`` to your own peer_plugin. If you want to keep references to it, + // use ``weak_ptr``. + // + // If this function throws an exception, the connection will be closed. + virtual boost::shared_ptr new_connection(peer_connection*) + { return boost::shared_ptr(); } + + // These hooks are called when a piece passes the hash check or fails the hash + // check, respectively. The ``index`` is the piece index that was downloaded. + // It is possible to access the list of peers that participated in sending the + // piece through the ``torrent`` and the ``piece_picker``. + virtual void on_piece_pass(int /*index*/) {} + virtual void on_piece_failed(int /*index*/) {} + + // This hook is called approximately once per second. It is a way of making it + // easy for plugins to do timed events, for sending messages or whatever. + virtual void tick() {} + + // These hooks are called when the torrent is paused and unpaused respectively. + // The return value indicates if the event was handled. A return value of + // ``true`` indicates that it was handled, and no other plugin after this one + // will have this hook function called, and the standard handler will also not be + // invoked. So, returning true effectively overrides the standard behavior of + // pause or unpause. + // + // Note that if you call ``pause()`` or ``resume()`` on the torrent from your + // handler it will recurse back into your handler, so in order to invoke the + // standard handler, you have to keep your own state on whether you want standard + // behavior or overridden behavior. + virtual bool on_pause() { return false; } + virtual bool on_resume() { return false; } + + // This function is called when the initial files of the torrent have been + // checked. If there are no files to check, this function is called immediately. + // + // i.e. This function is always called when the torrent is in a state where it + // can start downloading. + virtual void on_files_checked() {} + + // called when the torrent changes state + // the state is one of torrent_status::state_t + // enum members + virtual void on_state(int /*s*/) {} + + enum flags_t { + // this is the first time we see this peer + first_time = 1, + // this peer was not added because it was + // filtered by the IP filter + filtered = 2 + }; + + // called every time a new peer is added to the peer list. + // This is before the peer is connected to. For ``flags``, see + // torrent_plugin::flags_t. The ``source`` argument refers to + // the source where we learned about this peer from. It's a + // bitmask, because many sources may have told us about the same + // peer. For peer source flags, see peer_info::peer_source_flags. + virtual void on_add_peer(tcp::endpoint const&, + int /*src*/, int /*flags*/) {} + }; + + // peer plugins are associated with a specific peer. A peer could be + // both a regular bittorrent peer (``bt_peer_connection``) or one of the + // web seed connections (``web_peer_connection`` or ``http_seed_connection``). + // In order to only attach to certain peers, make your + // torrent_plugin::new_connection only return a plugin for certain peer + // connection types + struct TORRENT_EXPORT peer_plugin + { + // hidden + virtual ~peer_plugin() {} + + // This function is expected to return the name of + // the plugin. + virtual char const* type() const { return ""; } + + // can add entries to the extension handshake + // this is not called for web seeds + virtual void add_handshake(entry&) {} + + // called when the peer is being disconnected. + virtual void on_disconnect(error_code const& /*ec*/) {} + + // called when the peer is successfully connected. Note that + // incoming connections will have been connected by the time + // the peer plugin is attached to it, and won't have this hook + // called. + virtual void on_connected() {} + + // throwing an exception from any of the handlers (except add_handshake) + // closes the connection + + // this is called when the initial BT handshake is received. Returning false + // means that the other end doesn't support this extension and will remove + // it from the list of plugins. + // this is not called for web seeds + virtual bool on_handshake(char const* /*reserved_bits*/) { return true; } + + // called when the extension handshake from the other end is received + // if this returns false, it means that this extension isn't + // supported by this peer. It will result in this peer_plugin + // being removed from the peer_connection and destructed. + // this is not called for web seeds + virtual bool on_extension_handshake(lazy_entry const&) { return true; } + + // returning true from any of the message handlers + // indicates that the plugin has handeled the message. + // it will break the plugin chain traversing and not let + // anyone else handle the message, including the default + // handler. + virtual bool on_choke() { return false; } + virtual bool on_unchoke() { return false; } + virtual bool on_interested() { return false; } + virtual bool on_not_interested() { return false; } + virtual bool on_have(int /*index*/) { return false; } + virtual bool on_dont_have(int /*index*/) { return false; } + virtual bool on_bitfield(bitfield const& /*bitfield*/) { return false; } + virtual bool on_have_all() { return false; } + virtual bool on_have_none() { return false; } + virtual bool on_allowed_fast(int /*index*/) { return false; } + virtual bool on_request(peer_request const&) { return false; } + virtual bool on_piece(peer_request const& /*piece*/ + , disk_buffer_holder& /*data*/) { return false; } + virtual bool on_cancel(peer_request const&) { return false; } + virtual bool on_reject(peer_request const&) { return false; } + virtual bool on_suggest(int /*index*/) { return false; } + + // called after a choke message has been sent to the peer + virtual void sent_unchoke() {} + + // called when libtorrent think this peer should be disconnected. + // if the plugin returns false, the peer will not be disconnected. + virtual bool can_disconnect(error_code const& /*ec*/) { return true; } + + // called when an extended message is received. If returning true, + // the message is not processed by any other plugin and if false + // is returned the next plugin in the chain will receive it to + // be able to handle it. This is not called for web seeds. + // thus function may be called more than once per incoming message, but + // only the last of the calls will the ``body`` size equal the ``length``. + // i.e. Every time another fragment of the message is received, this + // function will be called, until finally the whole message has been + // received. The purpose of this is to allow early disconnects for invalid + // messages and for reporting progress of receiving large messages. + virtual bool on_extended(int /*length*/, int /*msg*/, + buffer::const_interval /*body*/) + { return false; } + + // this is not called for web seeds + virtual bool on_unknown_message(int /*length*/, int /*msg*/, + buffer::const_interval /*body*/) + { return false; } + + // called when a piece that this peer participated in either + // fails or passes the hash_check + virtual void on_piece_pass(int /*index*/) {} + virtual void on_piece_failed(int /*index*/) {} + + // called aproximately once every second + virtual void tick() {} + + // called each time a request message is to be sent. If true + // is returned, the original request message won't be sent and + // no other plugin will have this function called. + virtual bool write_request(peer_request const&) { return false; } + }; + +} + +#endif + +#endif // TORRENT_EXTENSIONS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp new file mode 100644 index 0000000000..5e5e6cb3a2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/logger.hpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LOGGER_HPP_INCLUDED +#define TORRENT_LOGGER_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_IOSTREAM + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + boost::shared_ptr create_logger_plugin(torrent*); +} + +#endif + +#endif // TORRENT_LOGGER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp new file mode 100644 index 0000000000..8587ee0576 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/lt_trackers.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LT_TRACKERS_HPP_INCLUDED +#define TORRENT_LT_TRACKERS_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the trackers exchange extension. This can + // either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_LT_TRACKERS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp new file mode 100644 index 0000000000..84f7aa7b7b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_METADATA_TRANSFER_HPP_INCLUDED +#define TORRENT_METADATA_TRANSFER_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + +#ifndef TORRENT_NO_DEPRECATE + // constructor function for the metadata transfer extension. This + // extension has been superceded by the ut_metadata extension and + // is deprecated. It can be either be passed in the + // add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_DEPRECATED_PREFIX + TORRENT_EXPORT boost::shared_ptr + create_metadata_plugin(torrent*, void*) TORRENT_DEPRECATED; +#endif +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_METADATA_TRANSFER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp new file mode 100644 index 0000000000..ba0ccb2137 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/smart_ban.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SMART_BAN_HPP_INCLUDED +#define TORRENT_SMART_BAN_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the smart ban extension. The extension keeps + // track of the data peers have sent us for failing pieces and once the + // piece completes and passes the hash check bans the peers that turned + // out to have sent corrupt data. + // This function can either be passed in the add_torrent_params::extensions + // field, or via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_smart_ban_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_SMART_BAN_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp new file mode 100644 index 0000000000..4daab9b6ad --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_metadata.hpp @@ -0,0 +1,68 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UT_METADATA_HPP_INCLUDED +#define TORRENT_UT_METADATA_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the ut_metadata extension. The ut_metadata + // extension allows peers to request the .torrent file (or more + // specifically the 'info'-dictionary of the .torrent file) from each + // other. This is the main building block in making magnet links work. + // This extension is enabled by default unless explicitly disabled in + // the session constructor. + // + // This can either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_ut_metadata_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS +#endif // TORRENT_UT_METADATA_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp new file mode 100644 index 0000000000..fc91ef7f22 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/extensions/ut_pex.hpp @@ -0,0 +1,67 @@ +/* + +Copyright (c) 2006, MassaRoddel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED +#define TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + + // constructor function for the ut_pex extension. The ut_pex + // extension allows peers to gossip about their connections, allowing + // the swarm stay well connected and peers aware of more peers in the + // swarm. This extension is enabled by default unless explicitly disabled in + // the session constructor. + // + // This can either be passed in the add_torrent_params::extensions field, or + // via torrent_handle::add_extension(). + TORRENT_EXPORT boost::shared_ptr create_ut_pex_plugin(torrent*, void*); +} + +#endif // TORRENT_DISABLE_EXTENSIONS + +#endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp new file mode 100644 index 0000000000..ca7b97e569 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file.hpp @@ -0,0 +1,333 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_HPP_INCLUDED +#define TORRENT_FILE_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/error_code.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" + +#ifdef TORRENT_WINDOWS +// windows part +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#else +// posix part +#define _FILE_OFFSET_BITS 64 + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +#include +#include +#include +#include +#include // for DIR + +#undef _FILE_OFFSET_BITS + +#endif + +namespace libtorrent +{ + struct file_status + { + size_type file_size; + boost::uint64_t atime; + boost::uint64_t mtime; + boost::uint64_t ctime; + enum { +#if defined TORRENT_WINDOWS + fifo = 0x1000, // named pipe (fifo) + character_special = 0x2000, // character special + directory = 0x4000, // directory + regular_file = 0x8000 // regular +#else + fifo = 0010000, // named pipe (fifo) + character_special = 0020000, // character special + directory = 0040000, // directory + block_special = 0060000, // block special + regular_file = 0100000, // regular + link = 0120000, // symbolic link + socket = 0140000 // socket +#endif + } modes_t; + int mode; + }; + + // internal flags for stat_file + enum { dont_follow_links = 1 }; + TORRENT_EXTRA_EXPORT void stat_file(std::string f, file_status* s + , error_code& ec, int flags = 0); + TORRENT_EXTRA_EXPORT void rename(std::string const& f + , std::string const& newf, error_code& ec); + TORRENT_EXTRA_EXPORT void create_directories(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void create_directory(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void remove_all(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void remove(std::string const& f, error_code& ec); + TORRENT_EXTRA_EXPORT bool exists(std::string const& f); + TORRENT_EXTRA_EXPORT size_type file_size(std::string const& f); + TORRENT_EXTRA_EXPORT bool is_directory(std::string const& f + , error_code& ec); + TORRENT_EXTRA_EXPORT void recursive_copy(std::string const& old_path + , std::string const& new_path, error_code& ec); + TORRENT_EXTRA_EXPORT void copy_file(std::string const& f + , std::string const& newf, error_code& ec); + + TORRENT_EXTRA_EXPORT std::string split_path(std::string const& f); + TORRENT_EXTRA_EXPORT char const* next_path_element(char const* p); + TORRENT_EXTRA_EXPORT std::string extension(std::string const& f); + TORRENT_EXTRA_EXPORT std::string remove_extension(std::string const& f); + TORRENT_EXTRA_EXPORT void replace_extension(std::string& f, std::string const& ext); + TORRENT_EXTRA_EXPORT bool is_root_path(std::string const& f); + + + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string parent_path(std::string const& f); + TORRENT_EXTRA_EXPORT bool has_parent_path(std::string const& f); + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string filename(std::string const& f); + TORRENT_EXTRA_EXPORT std::string combine_path(std::string const& lhs + , std::string const& rhs); + // internal used by create_torrent.hpp + TORRENT_EXPORT std::string complete(std::string const& f); + TORRENT_EXTRA_EXPORT bool is_complete(std::string const& f); + TORRENT_EXTRA_EXPORT std::string current_working_directory(); +#if TORRENT_USE_UNC_PATHS + TORRENT_EXTRA_EXPORT std::string canonicalize_path(std::string const& f); +#endif + + class TORRENT_EXTRA_EXPORT directory : public boost::noncopyable + { + public: + directory(std::string const& path, error_code& ec); + ~directory(); + void next(error_code& ec); + std::string file() const; + boost::uint64_t inode() const; + bool done() const { return m_done; } + private: +#ifdef TORRENT_WINDOWS + HANDLE m_handle; + int m_inode; +#if TORRENT_USE_WSTRING + WIN32_FIND_DATAW m_fd; +#else + WIN32_FIND_DATAA m_fd; +#endif +#else + DIR* m_handle; + // the dirent struct contains a zero-sized + // array at the end, it will end up referring + // to the m_name field + struct dirent m_dirent; + char m_name[TORRENT_MAX_PATH + 1]; // +1 to make room for null +#endif + bool m_done; + }; + + struct TORRENT_EXTRA_EXPORT file: boost::noncopyable, intrusive_ptr_base + { + // the open mode for files. Used for the file constructor or + // file::open(). + enum open_mode_t + { + // open the file for reading only + read_only = 0, + + // open the file for writing only + write_only = 1, + + // open the file for reading and writing + read_write = 2, + + // the mask for the bits determining read or write mode + rw_mask = read_only | write_only | read_write, + + // indicate that the file should be opened in + // *direct io* mode, i.e. bypassing the operating + // system's disk cache, or as much as possible of it + // depending on the system. + // when a file is opened with no_buffer, + // file offsets have to be aligned to + // pos_alignment() and buffer addresses + // to buf_alignment() and read/write sizes + // to size_alignment() + no_buffer = 4, + + // open the file in sparse mode (if supported by the + // filesystem). + sparse = 8, + + // don't update the access timestamps on the file (if + // supported by the operating system and filesystem). + // this generally improves disk performance. + no_atime = 16, + + // open the file for random acces. This disables read-ahead + // logic + random_access = 32, + + // prevent the file from being opened by another process + // while it's still being held open by this handle + lock_file = 64, + + // when creating a file, set the hidden attribute (windows only) + attribute_hidden = 0x1000, + + // when creating a file, set the executable attribute + attribute_executable = 0x2000, + + // the mask of all attribute bits + attribute_mask = attribute_hidden | attribute_executable + }; + +#ifdef TORRENT_WINDOWS + struct iovec_t + { + void* iov_base; + size_t iov_len; + }; +#else + typedef iovec iovec_t; +#endif + + // use a typedef for the type of iovec_t::iov_base + // since it may differ +#ifdef TORRENT_SOLARIS + typedef char* iovec_base_t; +#else + typedef void* iovec_base_t; +#endif + + file(); + file(std::string const& p, int m, error_code& ec); + ~file(); + + bool open(std::string const& p, int m, error_code& ec); + bool is_open() const; + void close(); + bool set_size(size_type size, error_code& ec); + + int open_mode() const { return m_open_mode; } + + // when opened in unbuffered mode, this is the + // required alignment of file_offsets. i.e. + // any (file_offset & (pos_alignment()-1)) == 0 + // is a precondition to read and write operations + int pos_alignment() const; + + // when opened in unbuffered mode, this is the + // required alignment of buffer addresses + int buf_alignment() const; + + // read/write buffer sizes needs to be aligned to + // this when in unbuffered mode + int size_alignment() const; + + size_type writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec); + size_type readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec); + void hint_read(size_type file_offset, int len); + + size_type get_size(error_code& ec) const; + + // return the offset of the first byte that + // belongs to a data-region + size_type sparse_end(size_type start) const; + + size_type phys_offset(size_type offset); + +#ifdef TORRENT_WINDOWS + HANDLE native_handle() const { return m_file_handle; } +#else + int native_handle() const { return m_fd; } +#endif + + private: + +#ifdef TORRENT_WINDOWS + HANDLE m_file_handle; +#if TORRENT_USE_WSTRING + std::wstring m_path; +#else + std::string m_path; +#endif // TORRENT_USE_WSTRING +#else // TORRENT_WINDOWS + int m_fd; +#endif // TORRENT_WINDOWS + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + static void init_file(); + static int m_page_size; +#endif + int m_open_mode; +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + mutable int m_sector_size; +#endif +#if defined TORRENT_WINDOWS + mutable int m_cluster_size; + + static bool has_manage_volume_privs; +#endif + }; + +} + +#endif // TORRENT_FILE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp new file mode 100644 index 0000000000..4062a1b6a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file_pool.hpp @@ -0,0 +1,123 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_POOL_HPP +#define TORRENT_FILE_POOL_HPP + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/file.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/file_storage.hpp" + +namespace libtorrent +{ + // this is an internal cache of open file handles. It's primarily used by + // storage_interface implementations. It provides semi weak guarantees of + // not opening more file handles than specified. Given multiple threads, + // each with the ability to lock a file handle (via smart pointer), there + // may be windows where more file handles are open. + struct TORRENT_EXPORT file_pool : boost::noncopyable + { + // ``size`` specifies the number of allowed files handles + // to hold open at any given time. + file_pool(int size = 40); + ~file_pool(); + + // return an open file handle to file at ``file_index`` in the + // file_storage ``fs`` opened at save path ``p``. ``m`` is the + // file open mode (see file::open_mode_t). + boost::intrusive_ptr open_file(void* st, std::string const& p + , int file_index, file_storage const& fs, int m, error_code& ec); + + // release all files belonging to the specified storage_interface (``st``) + // the overload that takes ``file_index`` releases only the file with + // that index in storage ``st``. + void release(void* st); + void release(void* st, int file_index); + + // update the allowed number of open file handles to ``size``. + void resize(int size); + + // returns the current limit of number of allowed open file handles held + // by the file_pool. + int size_limit() const { return m_size; } + + // internal + void set_low_prio_io(bool b) { m_low_prio_io = b; } + + private: + + void remove_oldest(); + + int m_size; + bool m_low_prio_io; + + struct lru_file_entry + { + lru_file_entry(): key(0), last_use(time_now()), mode(0) {} + mutable boost::intrusive_ptr file_ptr; + void* key; + ptime last_use; + int mode; + }; + + // maps storage pointer, file index pairs to the + // lru entry for the file + typedef std::map, lru_file_entry> file_set; + + file_set m_files; + mutex m_mutex; + +#if TORRENT_CLOSE_MAY_BLOCK + void closer_thread_fun(); + mutex m_closer_mutex; + std::vector > m_queued_for_close; + bool m_stop_thread; + + // used to close files + thread m_closer_thread; +#endif + }; +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp new file mode 100644 index 0000000000..0c26220e76 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/file_storage.hpp @@ -0,0 +1,586 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_STORAGE_HPP_INCLUDED +#define TORRENT_FILE_STORAGE_HPP_INCLUDED + +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + struct file; + + // information about a file in a file_storage + struct TORRENT_EXPORT file_entry + { + // hidden + file_entry(); + // hidden + ~file_entry(); + + // the full path of this file. The paths are unicode strings + // encoded in UTF-8. + std::string path; + + // the path which this is a symlink to, or empty if this is + // not a symlink. This field is only used if the ``symlink_attribute`` is set. + std::string symlink_path; + + // the offset of this file inside the torrent + size_type offset; + + // the size of the file (in bytes) and ``offset`` is the byte offset + // of the file within the torrent. i.e. the sum of all the sizes of the files + // before it in the list. + size_type size; + + // the offset in the file where the storage should start. The normal + // case is to have this set to 0, so that the storage starts saving data at the start + // if the file. In cases where multiple files are mapped into the same file though, + // the ``file_base`` should be set to an offset so that the different regions do + // not overlap. This is used when mapping "unselected" files into a so-called part + // file. + size_type file_base; + + // the modification time of this file specified in posix time. + std::time_t mtime; + + // a sha-1 hash of the content of the file, or zeroes, if no + // file hash was present in the torrent file. It can be used to potentially + // find alternative sources for the file. + sha1_hash filehash; + + // set to true for files that are not part of the data of the torrent. + // They are just there to make sure the next file is aligned to a particular byte offset + // or piece boundry. These files should typically be hidden from an end user. They are + // not written to disk. + bool pad_file:1; + + // true if the file was marked as hidden (on windows). + bool hidden_attribute:1; + + // true if the file was marked as executable (posix) + bool executable_attribute:1; + + // true if the file was a symlink. If this is the case + // the ``symlink_index`` refers to a string which specifies the original location + // where the data for this file was found. + bool symlink_attribute:1; + }; + + // only export this type if deprecated functions are enabled +#ifdef TORRENT_NO_DEPRECATE +#define TORRENT_DEPRECATED_EXPORT +#else +#define TORRENT_DEPRECATED_EXPORT TORRENT_EXPORT +#endif + + // internal + struct TORRENT_DEPRECATED_EXPORT internal_file_entry + { + friend class file_storage; +#ifdef TORRENT_DEBUG + // for torrent_info::invariant_check + friend class torrent_info; +#endif + + internal_file_entry() + : offset(0) + , symlink_index(not_a_symlink) + , no_root_dir(false) + , size(0) + , name_len(name_is_owned) + , pad_file(false) + , hidden_attribute(false) + , executable_attribute(false) + , symlink_attribute(false) + , name(NULL) + , path_index(-1) + {} + + internal_file_entry(file_entry const& e) + : offset(e.offset) + , symlink_index(not_a_symlink) + , no_root_dir(false) + , size(e.size) + , name_len(name_is_owned) + , pad_file(e.pad_file) + , hidden_attribute(e.hidden_attribute) + , executable_attribute(e.executable_attribute) + , symlink_attribute(e.symlink_attribute) + , name(NULL) + , path_index(-1) + { + set_name(e.path.c_str()); + } + + internal_file_entry(internal_file_entry const& fe); + internal_file_entry& operator=(internal_file_entry const& fe); + + ~internal_file_entry(); + + void set_name(char const* n, bool borrow_string = false, int string_len = 0); + std::string filename() const; + + enum { + name_is_owned = (1<<12)-1, + not_a_symlink = (1<<15)-1 + }; + + // the offset of this file inside the torrent + boost::uint64_t offset:48; + + // index into file_storage::m_symlinks or not_a_symlink + // if this is not a symlink + boost::uint64_t symlink_index:15; + + // if this is true, don't include m_name as part of the + // path to this file + boost::uint64_t no_root_dir:1; + + // the size of this file + boost::uint64_t size:48; + + // the number of characters in the name. If this is + // name_is_owned, name is null terminated and owned by this object + // (i.e. it should be freed in the destructor). If + // the len is not name_is_owned, the name pointer doesn not belong + // to this object, and it's not null terminated + boost::uint64_t name_len:12; + boost::uint64_t pad_file:1; + boost::uint64_t hidden_attribute:1; + boost::uint64_t executable_attribute:1; + boost::uint64_t symlink_attribute:1; + + // make it available for logging + private: + // This string is not necessarily null terminated! + // that's why it's private, to keep people away from it + char const* name; + public: + + // the index into file_storage::m_paths. To get + // the full path to this file, concatenate the path + // from that array with the 'name' field in + // this struct + // values for path_index include: + // -1 means no path (i.e. single file torrent) + // -2, it means the filename + // in this field contains the full, absolute path + // to the file + int path_index; + }; + + // represents a window of a file in a torrent. + // + // The ``file_index`` refers to the index of the file (in the torrent_info). + // To get the path and filename, use ``file_at()`` and give the ``file_index`` + // as argument. The ``offset`` is the byte offset in the file where the range + // starts, and ``size`` is the number of bytes this range is. The size + offset + // will never be greater than the file size. + struct TORRENT_EXPORT file_slice + { + // the index of the file + int file_index; + + // the offset from the start of the file, in bytes + size_type offset; + + // the size of the window, in bytes + size_type size; + }; + + // The ``file_storage`` class represents a file list and the piece + // size. Everything necessary to interpret a regular bittorrent storage + // file structure. + class TORRENT_EXPORT file_storage + { + friend class torrent_info; + public: + // hidden + file_storage(); + // hidden + ~file_storage(); + file_storage(file_storage const& f); + file_storage& operator=(file_storage const&); + + // 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 + // not. + bool is_valid() const { return m_piece_length > 0; } + + // file attribute flags + enum flags_t + { + // the file is a pad file. It's required to contain zeroes + // at it will not be saved to disk. Its purpose is to make + // the following file start on a piece boundary. + pad_file = 1, + + // this file has the hidden attribute set. This is primarily + // a windows attribute + attribute_hidden = 2, + + // this file has the executable attribute set. + attribute_executable = 4, + + // this file is a symbilic link. It should have a link + // target string associated with it. + attribute_symlink = 8 + }; + + // allocates space for ``num_files`` in the internal file list. This can + // be used to avoid reallocating the internal file list when the number + // of files to be added is known up-front. + void reserve(int num_files); + + // Adds a file to the file storage. The ``flags`` argument sets + // attributes on the file. The file attributes is an extension and may + // not work in all bittorrent clients. + // + // For possible file attributes, see file_storage::flags_t. + // + // If more files than one are added, certain restrictions to their paths + // apply. In a multi-file file storage (torrent), all files must share + // the same root directory. + // + // That is, the first path element of all files must be the same. + // This shared path element is also set to the name of the torrent. It + // can be changed by calling ``set_name``. + // + // The ``filehash`` argument is an optional pointer to a sha-1 hash (20 + // bytes) of the file. The hash is not copied into the file_storage + // object, but the pointer is expected to point to memory that stays + // valid throughout the life time of the file_storage. + // + // Currently, the ``filehash`` from ``file_entry`` is not used. + void add_file(file_entry const& e, char const* filehash = 0); + void add_file(std::string const& p, size_type size, int flags = 0 + , std::time_t mtime = 0, std::string const& s_p = ""); + + // renames the file at ``index`` to ``new_filename``. Keep in mind + // that filenames are expected to be UTF-8 encoded. + void rename_file(int index, std::string const& new_filename); + + // this is a low-level function that sets the name of a file + // by making it reference a buffer that is not owned by the file_storage. + // it's an optimization used when loading .torrent files, to not + // duplicate names in memory. + void rename_file_borrow(int index, char const* new_filename, int len); + +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + void add_file(std::wstring const& p, size_type size, int flags = 0 + , std::time_t mtime = 0, std::string const& s_p = "") TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_filename) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_name(std::wstring const& n) TORRENT_DEPRECATED; + + void rename_file_deprecated(int index, std::wstring const& new_filename); +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + // returns a list of file_slice objects representing the portions of + // files the specified piece index, byte offset and size range overlaps. + // this is the inverse mapping of map_file(). + std::vector map_block(int piece, size_type offset + , int size) const; + + // returns a peer_request representing the piece index, byte offset + // and size the specified file range overlaps. This is the inverse + // mapping ove map_block(). Note that the ``peer_request`` return type + // is meant to hold bittorrent block requests, which may not be larger + // than 16 kiB. Mapping a range larger than that may return an overflown + // integer. + peer_request map_file(int file, size_type offset, int size) const; + +#ifndef TORRENT_NO_DEPRECATE + // all functions depending on internal_file_entry + // were deprecated in 1.0. Use the variants that take an + // index instead + typedef std::vector::const_iterator iterator; + typedef std::vector::const_reverse_iterator reverse_iterator; + + TORRENT_DEPRECATED_PREFIX + iterator file_at_offset(size_type offset) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + iterator begin() const TORRENT_DEPRECATED { return m_files.begin(); } + TORRENT_DEPRECATED_PREFIX + iterator end() const TORRENT_DEPRECATED { return m_files.end(); } + TORRENT_DEPRECATED_PREFIX + reverse_iterator rbegin() const TORRENT_DEPRECATED { return m_files.rbegin(); } + TORRENT_DEPRECATED_PREFIX + reverse_iterator rend() const TORRENT_DEPRECATED { return m_files.rend(); } + TORRENT_DEPRECATED_PREFIX + internal_file_entry const& internal_at(int index) const TORRENT_DEPRECATED + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_files.size())); + return m_files[index]; + } + TORRENT_DEPRECATED_PREFIX + file_entry at(iterator i) const TORRENT_DEPRECATED; + + iterator begin_deprecated() const { return m_files.begin(); } + iterator end_deprecated() const { return m_files.end(); } + reverse_iterator rbegin_deprecated() const { return m_files.rbegin(); } + reverse_iterator rend_deprecated() const { return m_files.rend(); } + iterator file_at_offset_deprecated(size_type offset) const; +#endif // TORRENT_NO_DEPRECATE + + // returns the number of files in the file_storage + int num_files() const + { return int(m_files.size()); } + + // returns a file_entry with information about the file + // at ``index``. Index must be in the range [0, ``num_files()`` ). + file_entry at(int index) const; + + // returns the total number of bytes all the files in this torrent spans + size_type total_size() const { return m_total_size; } + + // set and get the number of pieces in the torrent + void set_num_pieces(int n) { m_num_pieces = n; } + int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; } + + // set and get the size of each piece in this torrent. This size is typically an even power + // of 2. It doesn't have to be though. It should be divisible by 16kiB however. + void set_piece_length(int l) { m_piece_length = l; } + int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; } + + // returns the piece size of ``index``. This will be the same as piece_length(), except + // for the last piece, which may be shorter. + int piece_size(int index) const; + + // set and get the name of this torrent. For multi-file torrents, this is also + // the name of the root directory all the files are stored in. + void set_name(std::string const& n) { m_name = n; } + const std::string& name() const { return m_name; } + + // swap all content of *this* with *ti*. + void swap(file_storage& ti) + { + using std::swap; + swap(ti.m_files, m_files); + swap(ti.m_file_hashes, m_file_hashes); + swap(ti.m_symlinks, m_symlinks); + swap(ti.m_mtime, m_mtime); + swap(ti.m_file_base, m_file_base); + swap(ti.m_paths, m_paths); + swap(ti.m_name, m_name); + swap(ti.m_total_size, m_total_size); + swap(ti.m_num_pieces, m_num_pieces); + swap(ti.m_piece_length, m_piece_length); + } + + // if pad_file_limit >= 0, files larger than that limit will be padded, + // default is to not add any padding (-1). The alignment specifies the + // alignment files should be padded to. This defaults to the piece size + // (-1) but it may also make sense to set it to 16 kiB, or something + // divisible by 16 kiB. + // If pad_file_limit is 0, every file will be padded (except empty ones). + void optimize(int pad_file_limit = -1, int alignment = -1); + + // These functions are used to query attributes of files at + // a given index. + // + // The ``hash()`` is a sha-1 hash of the file, or 0 if none was + // provided in the torrent file. This can potentially be used to + // join a bittorrent network with other file sharing networks. + // + // The ``mtime()`` is the modification time is the posix + // time when a file was last modified when the torrent + // was created, or 0 if it was not included in the torrent file. + // + // ``file_path()`` returns the full path to a file. + // + // ``file_size()`` returns the size of a file. + // + // ``pad_file_at()`` returns true if the file at the given + // index is a pad-file. + // + // ``file_name()`` returns *just* the name of the file, whereas + // ``file_path()`` returns the path (inside the torrent file) with + // the filename appended. + // + // ``file_offset()`` returns the byte offset within the torrent file + // where this file starts. It can be used to map the file to a piece + // index (given the piece size). + sha1_hash hash(int index) const; + std::string const& symlink(int index) const; + time_t mtime(int index) const; + std::string file_path(int index, std::string const& save_path = "") const; + std::string file_name(int index) const; + size_type file_size(int index) const; + bool pad_file_at(int index) const; + size_type file_offset(int index) const; + + // flags indicating various attributes for files in + // a file_storage. + enum file_flags_t + { + // this file is a pad file. The creator of the + // torrent promises the file is entirely filled with + // zeroes and does not need to be downloaded. The + // purpose is just to align the next file to either + // a block or piece boundary. + flag_pad_file = 1, + + // this file is hiddent (sets the hidden attribute + // on windows) + flag_hidden = 2, + + // this file is executable (sets the executable bit + // on posix like systems) + flag_executable = 4, + + // this file is a symlink. The symlink target is + // specified in a separate field + flag_symlink = 8 + }; + + // returns a bitmask of flags from file_flags_t that apply + // to file at ``index``. + int file_flags(int index) const; + + // The file base of a file is the offset within the file on the filsystem + // where it starts to write. For the most part, this is always 0. It's + // possible to map several files (in the torrent) into a single file on + // the filesystem by making them all point to the same filename, but with + // different file bases, so that they don't overlap. + // torrent_info::remap_files() can be used to use a new file layout. + size_type file_base(int index) const; + void set_file_base(int index, size_type off); + + // returns the index of the file at the given offset in the torrent + int file_index_at_offset(size_type offset) const; + + // low-level function. returns a pointer to the internal storage for + // the filename. This string may not be null terinated! + // the ``file_name_len()`` function returns the length of the filename. + char const* file_name_ptr(int index) const; + int file_name_len(int index) const; + +#ifndef TORRENT_NO_DEPRECATE + // these were deprecated in 1.0. Use the versions that take an index instead + TORRENT_DEPRECATED_PREFIX + sha1_hash hash(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string const& symlink(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + time_t mtime(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int file_index(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_base(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_file_base(internal_file_entry const& fe, size_type off) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string file_path(internal_file_entry const& fe, std::string const& save_path = "") const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::string file_name(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_size(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool pad_file_at(internal_file_entry const& fe) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + size_type file_offset(internal_file_entry const& fe) const TORRENT_DEPRECATED; +#endif + + private: + + void update_path_index(internal_file_entry& e); + void reorder_file(int index, int dst); + + // the list of files that this torrent consists of + std::vector m_files; + + // if there are sha1 hashes for each individual file + // there are as many entries in this array as the + // m_files array. Each entry in m_files has a corresponding + // hash pointer in this array. The reason to split it up + // in separate arrays is to save memory in case the torrent + // doesn't have file hashes + std::vector m_file_hashes; + + // for files that are symlinks, the symlink + // path_index in the internal_file_entry indexes + // this vector of strings + std::vector m_symlinks; + + // the modification times of each file. This vector + // is empty if no file have a modification time. + // each element corresponds to the file with the same + // index in m_files + std::vector m_mtime; + + // if any file has a non-zero file base (i.e. multiple + // files residing in the same physical file at different + // offsets) + std::vector m_file_base; + + // all unique paths files have. The internal_file_entry::path_index + // points into this array. The paths don't include the root directory + // name for multi-file torrents. The m_name field need to be + // prepended to these paths, and the filename of a specific file + // entry appended, to form full file paths + std::vector m_paths; + + // name of torrent. For multi-file torrents + // this is always the root directory + std::string m_name; + + // the sum of all filesizes + size_type m_total_size; + + // the number of pieces in the torrent + int m_num_pieces; + + int m_piece_length; + }; +} + +#endif // TORRENT_FILE_STORAGE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp new file mode 100644 index 0000000000..2527d5be8c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/fingerprint.hpp @@ -0,0 +1,129 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FINGERPRINT_HPP_INCLUDED +#define TORRENT_FINGERPRINT_HPP_INCLUDED + +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + + // The fingerprint class represents information about a client and its version. It is used + // to encode this information into the client's peer id. + struct fingerprint + { + + // The constructor takes a ``char const*`` that should point to a string constant containing + // exactly two characters. These are the characters that should be unique for your client. Make + // sure not to clash with anybody else. Here are some taken id's: + // + // +----------+-----------------------+ + // | id chars | client | + // +==========+=======================+ + // | 'AZ' | Azureus | + // +----------+-----------------------+ + // | 'LT' | libtorrent (default) | + // +----------+-----------------------+ + // | 'BX' | BittorrentX | + // +----------+-----------------------+ + // | 'MT' | Moonlight Torrent | + // +----------+-----------------------+ + // | 'TS' | Torrent Storm | + // +----------+-----------------------+ + // | 'SS' | Swarm Scope | + // +----------+-----------------------+ + // | 'XT' | Xan Torrent | + // +----------+-----------------------+ + // + // There's an informal directory of client id's here_. + // + // .. _here: http://wiki.theory.org/BitTorrentSpecification#peer_id + // + // The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the + // version of your client. + fingerprint(const char* id_string, int major, int minor, int revision, int tag) + : major_version(major) + , minor_version(minor) + , revision_version(revision) + , tag_version(tag) + { + TORRENT_ASSERT(id_string); + TORRENT_ASSERT(major >= 0); + TORRENT_ASSERT(minor >= 0); + TORRENT_ASSERT(revision >= 0); + TORRENT_ASSERT(tag >= 0); + TORRENT_ASSERT(std::strlen(id_string) == 2); + name[0] = id_string[0]; + name[1] = id_string[1]; + } + + // generates the actual string put in the peer-id, and return it. + std::string to_string() const + { + char s[100]; + snprintf(s, 100, "-%c%c%c%c%c%c-" + , name[0], name[1] + , version_to_char(major_version) + , version_to_char(minor_version) + , version_to_char(revision_version) + , version_to_char(tag_version)); + return s; + } + + char name[2]; + int major_version; + int minor_version; + int revision_version; + int tag_version; + + private: + + char version_to_char(int v) const + { + if (v >= 0 && v < 10) return '0' + v; + else if (v >= 10) return 'A' + (v - 10); + TORRENT_ASSERT(false); + return '0'; + } + + }; + +} + +#endif // TORRENT_FINGERPRINT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp new file mode 100644 index 0000000000..75678f0cf7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/gzip.hpp @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_GZIP_HPP_INCLUDED +#define TORRENT_GZIP_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" + +#include + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT void inflate_gzip( + char const* in, int size + , std::vector& buffer + , int maximum_size + , error_code& error); + + // get the ``error_category`` for zip errors + TORRENT_EXPORT boost::system::error_category& get_gzip_category(); + + namespace gzip_errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_gzip_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + + // the supplied gzip buffer has invalid header + invalid_gzip_header, + + // the gzip buffer would inflate to more bytes than the specified + // maximum size, and was rejected. + inflated_data_too_large, + + // available inflate data did not terminate + data_did_not_terminate, + + // output space exhausted before completing inflate + space_exhausted, + + // invalid block type (type == 3) + invalid_block_type, + + // stored block length did not match one's complement + invalid_stored_block_length, + + // dynamic block code description: too many length or distance codes + too_many_length_or_distance_codes, + + // dynamic block code description: code lengths codes incomplete + code_lengths_codes_incomplete, + + // dynamic block code description: repeat lengths with no first length + repeat_lengths_with_no_first_length, + + // dynamic block code description: repeat more than specified lengths + repeat_more_than_specified_lengths, + + // dynamic block code description: invalid literal/length code lengths + invalid_literal_length_code_lengths, + + // dynamic block code description: invalid distance code lengths + invalid_distance_code_lengths, + + // invalid literal/length or distance code in fixed or dynamic block + invalid_literal_code_in_block, + + // distance is too far back in fixed or dynamic block + distance_too_far_back_in_block, + + // an unknown error occurred during gzip inflation + unknown_gzip_error, + + // the number of error codes + error_code_max + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + +} + +#if BOOST_VERSION >= 103500 +namespace boost { namespace system { + +template<> +struct is_error_code_enum +{ static const bool value = true; }; + +template<> +struct is_error_condition_enum +{ static const bool value = true; }; + +} } +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp new file mode 100644 index 0000000000..dc4b0d07c4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/hasher.hpp @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HASHER_HPP_INCLUDED +#define TORRENT_HASHER_HPP_INCLUDED + +#include + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_USE_GCRYPT +#include + +#elif TORRENT_USE_COMMONCRYPTO + +#include + +#elif defined TORRENT_USE_OPENSSL + +extern "C" +{ +#include +} + +#else +// from sha1.cpp +namespace libtorrent +{ + + struct TORRENT_EXTRA_EXPORT sha_ctx + { + boost::uint32_t state[5]; + boost::uint32_t count[2]; + boost::uint8_t buffer[64]; + }; + + TORRENT_EXTRA_EXPORT void SHA1_init(sha_ctx* context); + TORRENT_EXTRA_EXPORT void SHA1_update(sha_ctx* context, boost::uint8_t const* data, boost::uint32_t len); + TORRENT_EXTRA_EXPORT void SHA1_final(boost::uint8_t* digest, sha_ctx* context); +} // namespace libtorrent + +#endif + +namespace libtorrent +{ + // this is a SHA-1 hash class. + // + // You use it by first instantiating it, then call ``update()`` to feed it + // with data. i.e. you don't have to keep the entire buffer of which you want to + // create the hash in memory. You can feed the hasher parts of it at a time. When + // You have fed the hasher with all the data, you call ``final()`` and it + // will return the sha1-hash of the data. + // + // The constructor that takes a ``char const*`` and an integer will construct the + // sha1 context and feed it the data passed in. + // + // If you want to reuse the hasher object once you have created a hash, you have to + // call ``reset()`` to reinitialize it. + // + // The sha1-algorithm used was implemented by Steve Reid and released as public domain. + // For more info, see ``src/sha1.cpp``. + class TORRENT_EXPORT hasher + { + public: + + hasher(); + + // this is the same as default constructing followed by a call to + // ``update(data, len)``. + hasher(const char* data, int len); + +#ifdef TORRENT_USE_GCRYPT + hasher(hasher const& h); + hasher& operator=(hasher const& h); +#endif + + // append the following bytes to what is being hashed + hasher& update(std::string const& data) { update(data.c_str(), int(data.size())); return *this; } + hasher& update(const char* data, int len); + + // returns the SHA-1 digest of the buffers previously passed to + // update() and the hasher constructor. + sha1_hash final(); + + // restore the hasher state to be as if the hasher has just been + // default constructed. + void reset(); + +#ifdef TORRENT_USE_GCRYPT + ~hasher(); +#endif + + private: + +#ifdef TORRENT_USE_GCRYPT + gcry_md_hd_t m_context; +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_CTX m_context; +#elif defined TORRENT_USE_OPENSSL + SHA_CTX m_context; +#else + sha_ctx m_context; +#endif + }; +} + +#endif // TORRENT_HASHER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp new file mode 100644 index 0000000000..16f0547e5e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_connection.hpp @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_CONNECTION +#define TORRENT_HTTP_CONNECTION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/session_settings.hpp" + +#include "libtorrent/i2p_stream.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +namespace libtorrent +{ + +struct http_connection; +class connection_queue; + +const int default_max_bottled_buffer_size = 2*1024*1024; + +typedef boost::function http_handler; + +typedef boost::function http_connect_handler; + +typedef boost::function&)> http_filter_handler; + +// when bottled, the last two arguments to the handler +// will always be 0 +struct TORRENT_EXTRA_EXPORT http_connection + : boost::enable_shared_from_this + , boost::noncopyable +{ + http_connection(io_service& ios, connection_queue& cc + , http_handler const& handler, bool bottled = true + , int max_bottled_buffer_size = default_max_bottled_buffer_size + , http_connect_handler const& ch = http_connect_handler() + , http_filter_handler const& fh = http_filter_handler() +#ifdef TORRENT_USE_OPENSSL + , boost::asio::ssl::context* ssl_ctx = 0 +#endif + ); + + ~http_connection(); + + void rate_limit(int limit); + + int rate_limit() const + { return m_rate_limit; } + + std::string sendbuffer; + + void get(std::string const& url, time_duration timeout = seconds(30) + , int prio = 0, proxy_settings const* ps = 0, int handle_redirects = 5 + , std::string const& user_agent = "", address const& bind_addr = address_v4::any() +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void start(std::string const& hostname, std::string const& port + , time_duration timeout, int prio = 0, proxy_settings const* ps = 0 + , bool ssl = false, int handle_redirect = 5 + , address const& bind_addr = address_v4::any() +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void close(bool force = false); + + socket_type const& socket() const { return m_sock; } + + std::list const& endpoints() const { return m_endpoints; } + +private: + +#if TORRENT_USE_I2P + void on_i2p_resolve(error_code const& e + , char const* destination); +#endif + void on_resolve(error_code const& e + , tcp::resolver::iterator i); + void queue_connect(); + void connect(int ticket, tcp::endpoint target_address); + void on_connect_timeout(); + void on_connect(error_code const& e); + void on_write(error_code const& e); + void on_read(error_code const& e, std::size_t bytes_transferred); + static void on_timeout(boost::weak_ptr p + , error_code const& e); + void on_assign_bandwidth(error_code const& e); + + void callback(error_code e, char* data = 0, int size = 0); + + std::vector m_recvbuffer; + socket_type m_sock; +#if TORRENT_USE_I2P + i2p_connection* m_i2p_conn; +#endif + int m_read_pos; + tcp::resolver m_resolver; + http_parser m_parser; + http_handler m_handler; + http_connect_handler m_connect_handler; + http_filter_handler m_filter_handler; + deadline_timer m_timer; + time_duration m_read_timeout; + time_duration m_completion_timeout; + ptime m_last_receive; + ptime m_start_time; + + // bottled means that the handler is called once, when + // everything is received (and buffered in memory). + // non bottled means that once the headers have been + // received, data is streamed to the handler + bool m_bottled; + + // maximum size of bottled buffer + int m_max_bottled_buffer_size; + + // set to true the first time the handler is called + bool m_called; + std::string m_hostname; + std::string m_port; + std::string m_url; + std::string m_user_agent; + + std::list m_endpoints; +#ifdef TORRENT_USE_OPENSSL + asio::ssl::context* m_ssl_ctx; + bool m_own_ssl_context; +#endif + + // the current download limit, in bytes per second + // 0 is unlimited. + int m_rate_limit; + + // the number of bytes we are allowed to receive + int m_download_quota; + + // only hand out new quota 4 times a second if the + // quota is 0. If it isn't 0 wait for it to reach + // 0 and continue to hand out quota at that time. + bool m_limiter_timer_active; + + // the timer fires every 250 millisecond as long + // as all the quota was used. + deadline_timer m_limiter_timer; + + // the number of redirects to follow (in sequence) + int m_redirects; + + int m_connection_ticket; + connection_queue& m_cc; + + // specifies whether or not the connection is + // configured to use a proxy + proxy_settings m_proxy; + + // true if the connection is using ssl + bool m_ssl; + + // the address to bind to. address_v4::any() + // means do not bind + address m_bind_addr; + + // the priority we have in the connection queue. + // 0 is normal, 1 is high + int m_priority; + + bool m_abort; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp new file mode 100644 index 0000000000..9682f50145 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_parser.hpp @@ -0,0 +1,180 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_PARSER_HPP_INCLUDED +#define TORRENT_HTTP_PARSER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/size_type.hpp" + +namespace libtorrent +{ + + // return true if the status code is 200, 206, or in the 300-400 range + bool is_ok_status(int http_status); + + // return true if the status code is a redirect + bool is_redirect(int http_status); + + TORRENT_EXTRA_EXPORT std::string resolve_redirect_location(std::string referrer + , std::string location); + + class TORRENT_EXTRA_EXPORT http_parser + { + public: + enum flags_t { dont_parse_chunks = 1 }; + http_parser(int flags = 0); + ~http_parser(); + std::string const& header(char const* key) const + { + static std::string empty; + std::multimap::const_iterator i + = m_header.find(key); + if (i == m_header.end()) return empty; + return i->second; + } + + std::string const& protocol() const { return m_protocol; } + int status_code() const { return m_status_code; } + std::string const& method() const { return m_method; } + std::string const& path() const { return m_path; } + std::string const& message() const { return m_server_message; } + buffer::const_interval get_body() const; + bool header_finished() const { return m_state == read_body; } + bool finished() const { return m_finished; } + boost::tuple incoming(buffer::const_interval recv_buffer + , bool& error); + int body_start() const { return m_body_start_pos; } + size_type content_length() const { return m_content_length; } + std::pair content_range() const + { return std::make_pair(m_range_start, m_range_end); } + + // returns true if this response is using chunked encoding. + // in this case the body is split up into chunks. You need + // to call parse_chunk_header() for each chunk, starting with + // the start of the body. + bool chunked_encoding() const { return m_chunked_encoding; } + + // removes the chunk headers from the supplied buffer. The buffer + // must be the stream received from the http server this parser + // instanced parsed. It will use the internal chunk list to determine + // where the chunks are in the buffer. It returns the new length of + // the buffer + int collapse_chunk_headers(char* buffer, int size) const; + + // returns false if the buffer doesn't contain a complete + // chunk header. In this case, call the function again with + // a bigger buffer once more bytes have been received. + // chunk_size is filled in with the number of bytes in the + // chunk that follows. 0 means the response terminated. In + // this case there might be additional headers in the parser + // object. + // header_size is filled in with the number of bytes the header + // itself was. Skip this number of bytes to get to the actual + // chunk data. + // if the function returns false, the chunk size and header + // size may still have been modified, but their values are + // undefined + bool parse_chunk_header(buffer::const_interval buf + , size_type* chunk_size, int* header_size); + + // reset the whole state and start over + void reset(); + + bool connection_close() const { return m_connection_close; } + + std::multimap const& headers() const { return m_header; } + std::vector > const& chunks() const { return m_chunked_ranges; } + + private: + size_type m_recv_pos; + int m_status_code; + std::string m_method; + std::string m_path; + std::string m_protocol; + std::string m_server_message; + + size_type m_content_length; + size_type m_range_start; + size_type m_range_end; + + enum { read_status, read_header, read_body, error_state } m_state; + + std::multimap m_header; + buffer::const_interval m_recv_buffer; + int m_body_start_pos; + + // this is true if the server is HTTP/1.0 or + // if it sent "connection: close" + bool m_connection_close; + bool m_chunked_encoding; + bool m_finished; + + // contains offsets of the first and one-past-end of + // each chunked range in the response + std::vector > m_chunked_ranges; + + // while reading a chunk, this is the offset where the + // current chunk will end (it refers to the first character + // in the chunk tail header or the next chunk header) + size_type m_cur_chunk_end; + + // the sum of all chunk headers read so far + int m_chunk_header_size; + + int m_partial_chunk_header; + + // controls some behaviors of the parser + int m_flags; + }; + +} + +#endif // TORRENT_HTTP_PARSER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp new file mode 100644 index 0000000000..1813091746 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_seed_connection.hpp @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + struct peer_request; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT http_seed_connection + : public web_connection_base + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + http_seed_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + + virtual int type() const { return peer_connection::http_seed_connection; } + + // called from the main loop when this connection has any + // work to do. + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual void disconnect(error_code const& ec, int error = 0); + + void write_request(peer_request const& r); + + private: + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + // this is const since it's used as a key in the web seed list in the torrent + // if it's changed referencing back into that list will fail + const std::string m_url; + + // the number of bytes left to receive of the response we're + // currently parsing + size_type m_response_left; + + // this is the offset inside the current receive + // buffer where the next chunk header will be. + // this is updated for each chunk header that's + // parsed. It does not necessarily point to a valid + // offset in the receive buffer, if we haven't received + // it yet. This offset never includes the HTTP header + size_type m_chunk_pos; + + // this is the number of bytes we've already received + // from the next chunk header we're waiting for + int m_partial_chunk_header; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp new file mode 100644 index 0000000000..8165102218 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_stream.hpp @@ -0,0 +1,124 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_STREAM_HPP_INCLUDED +#define TORRENT_HTTP_STREAM_HPP_INCLUDED + +#include +#include "libtorrent/proxy_base.hpp" +#include +#include + +namespace libtorrent { + +class http_stream : public proxy_base +{ +public: + + explicit http_stream(io_service& io_service) + : proxy_base(io_service) + , m_no_connect(false) + {} + + void set_no_connect(bool c) { m_no_connect = c; } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + void set_dst_name(std::string const& host) + { + m_dst_name = host; + } + + void close(error_code& ec) + { + m_dst_name.clear(); + proxy_base::close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_dst_name.clear(); + proxy_base::close(); + } +#endif + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. send HTTP CONNECT method and possibly username+password + // 4. read CONNECT response + + // to avoid unnecessary copying of the handler, + // store it in a shared_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &http_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void handshake1(error_code const& e, boost::shared_ptr h); + void handshake2(error_code const& e, boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + std::string m_dst_name; + + // this is true if the connection is HTTP based and + // want to talk directly to the proxy + bool m_no_connect; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp new file mode 100644 index 0000000000..fb672871f8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/i2p_stream.hpp" + +namespace libtorrent +{ + + struct http_connection; + class entry; + class http_parser; + class connection_queue; + struct session_settings; + namespace aux { struct session_impl; } + + class TORRENT_EXTRA_EXPORT http_tracker_connection + : public tracker_connection + { + friend class tracker_manager; + public: + + http_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl const& ses + , proxy_settings const& ps + , std::string const& password = "" +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn = 0 +#endif + ); + + void start(); + void close(); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_filter(http_connection& c, std::list& endpoints); + void on_connect(http_connection& c); + void on_response(error_code const& ec, http_parser const& parser + , char const* data, int size); + + virtual void on_timeout(error_code const& ec) {} + + void parse(int status_code, lazy_entry const& e); + bool extract_peer_info(lazy_entry const& e, peer_entry& ret); + + tracker_manager& m_man; + boost::shared_ptr m_tracker_connection; + aux::session_impl const& m_ses; + address m_tracker_ip; + proxy_settings const& m_ps; + connection_queue& m_cc; + io_service& m_ios; +#if TORRENT_USE_I2P + i2p_connection* m_i2p_conn; +#endif + }; + +} + +#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp new file mode 100644 index 0000000000..b82adf7dbc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/i2p_stream.hpp @@ -0,0 +1,238 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_I2P_STREAM_HPP_INCLUDED +#define TORRENT_I2P_STREAM_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_I2P + +#include +#include +#include +#include +#include +#include +#include +#include "libtorrent/proxy_base.hpp" +#include "libtorrent/session_settings.hpp" + +namespace libtorrent { + + namespace i2p_error { + + // error values for the i2p_category error_category. + enum i2p_error_code + { + no_error = 0, + parse_failed, + cant_reach_peer, + i2p_error, + invalid_key, + invalid_id, + timeout, + key_not_found, + duplicated_id, + num_errors + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(i2p_error_code e); + } + + // returns the error category for I2P errors + TORRENT_EXPORT boost::system::error_category& get_i2p_category(); + +class i2p_stream : public proxy_base +{ +public: + + explicit i2p_stream(io_service& io_service); + ~i2p_stream(); + + enum command_t + { + cmd_none, + cmd_create_session, + cmd_connect, + cmd_accept, + cmd_name_lookup, + cmd_incoming + }; + + void set_command(command_t c) { m_command = c; } + + void set_session_id(char const* id) { m_id = id; } + + void set_destination(std::string const& d) { m_dest = d; } + std::string const& destination() { return m_dest; } + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + // since we don't support regular endpoints, just ignore the one + // provided and use m_dest. + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to SAM bridge + // 4 send command message (CONNECT/ACCEPT) + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &i2p_stream::do_connect, this, _1, _2, h)); + } + + std::string name_lookup() const { return m_name_lookup; } + void set_name_lookup(char const* name) { m_name_lookup = name; } + + void send_name_lookup(boost::shared_ptr h); + +private: + + bool handle_error(error_code const& e, boost::shared_ptr const& h); + void do_connect(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void start_read_line(error_code const& e, boost::shared_ptr h); + void read_line(error_code const& e, boost::shared_ptr h); + void send_connect(boost::shared_ptr h); + void send_accept(boost::shared_ptr h); + void send_session_create(boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + char const* m_id; + int m_command; // 0 = connect, 1 = accept + std::string m_dest; + std::string m_name_lookup; + + enum state_t + { + read_hello_response, + read_connect_response, + read_accept_response, + read_session_create_response, + read_name_lookup_response + }; + + int m_state; +#if TORRENT_USE_ASSERTS + int m_magic; +#endif +}; + +class i2p_connection +{ +public: + i2p_connection(io_service& ios); + ~i2p_connection(); + + proxy_settings const& proxy() const { return m_sam_router; } + + bool is_open() const + { + return m_sam_socket + && m_sam_socket->is_open() + && m_state != sam_connecting; + } + void open(proxy_settings const& s, i2p_stream::handler_type const& h); + void close(error_code&); + + char const* session_id() const { return m_session_id.c_str(); } + std::string const& local_endpoint() const { return m_i2p_local_endpoint; } + + typedef boost::function name_lookup_handler; + void async_name_lookup(char const* name, name_lookup_handler handler); + +private: + + void on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h + , boost::shared_ptr); + void do_name_lookup(std::string const& name + , name_lookup_handler const& h); + void on_name_lookup(error_code const& ec + , name_lookup_handler handler + , boost::shared_ptr); + + void set_local_endpoint(error_code const& ec, char const* dest + , i2p_stream::handler_type const& h); + + // to talk to i2p SAM bridge + boost::shared_ptr m_sam_socket; + proxy_settings m_sam_router; + + // our i2p endpoint key + std::string m_i2p_local_endpoint; + std::string m_session_id; + + std::list > m_name_lookup; + + enum state_t + { + sam_connecting, + sam_name_lookup, + sam_idle + }; + + state_t m_state; + + io_service& m_io_service; +}; + +} + +#if BOOST_VERSION >= 103500 +namespace boost { namespace system { + +template<> +struct is_error_code_enum +{ static const bool value = true; }; + +template<> +struct is_error_condition_enum +{ static const bool value = true; }; + +} } +#endif // BOOST_VERSION + +#endif // TORRENT_USE_I2P + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp new file mode 100644 index 0000000000..ed8492b50d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/identify_client.hpp @@ -0,0 +1,67 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED +#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + // This function can can be used to extract a string describing a client + // version from its peer-id. It will recognize most clients that have this + // kind of identification in the peer-id. + TORRENT_EXPORT std::string identify_client(const peer_id& p); + + // Returns an optional fingerprint if any can be identified from the peer + // id. This can be used to automate the identification of clients. It will + // not be able to identify peers with non- standard encodings. Only Azureus + // style, Shadow's style and Mainline style. + TORRENT_EXPORT boost::optional client_fingerprint(peer_id const& p); + +} + +#endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp new file mode 100644 index 0000000000..c876634d78 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/instantiate_connection.hpp @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INSTANTIATE_CONNECTION +#define TORRENT_INSTANTIATE_CONNECTION + +#include "libtorrent/socket_type.hpp" +#include + +namespace libtorrent +{ + struct proxy_settings; + struct utp_socket_manager; + struct socket_type; + + // instantiate a socket_type (s) according to the specified criteria + TORRENT_EXTRA_EXPORT bool instantiate_connection(io_service& ios + , proxy_settings const& ps, socket_type& s + , void* ssl_context = 0 + , utp_socket_manager* sm = 0 + , bool peer_connection = false); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp new file mode 100644 index 0000000000..7ebe986154 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/intrusive_ptr_base.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INTRUSIVE_PTR_BASE +#define TORRENT_INTRUSIVE_PTR_BASE + +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + template + struct intrusive_ptr_base + { + intrusive_ptr_base(intrusive_ptr_base const&) + : m_refs(0) {} + + friend void intrusive_ptr_add_ref(intrusive_ptr_base const* s) + { + TORRENT_ASSERT(s != 0); + TORRENT_ASSERT(s->m_refs >= 0); + ++s->m_refs; + } + + friend void intrusive_ptr_release(intrusive_ptr_base const* s) + { + TORRENT_ASSERT(s != 0); + TORRENT_ASSERT(s->m_refs > 0); + if (--s->m_refs == 0) + boost::checked_delete(static_cast(s)); + } + + boost::intrusive_ptr self() + { return boost::intrusive_ptr((T*)this); } + + boost::intrusive_ptr self() const + { return boost::intrusive_ptr((T const*)this); } + + int refcount() const { return m_refs; } + + intrusive_ptr_base(): m_refs(0) {} + + private: + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp new file mode 100644 index 0000000000..388552954a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/invariant_check.hpp @@ -0,0 +1,81 @@ +// Copyright Daniel Wallin 2004. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TORRENT_INVARIANT_ACCESS_HPP_INCLUDED +#define TORRENT_INVARIANT_ACCESS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/config.hpp" + +#if TORRENT_USE_INVARIANT_CHECKS + +namespace libtorrent +{ + + class invariant_access + { + public: + template + static void check_invariant(T const& self) + { + self.check_invariant(); + } + }; + + template + void check_invariant(T const& x) + { + invariant_access::check_invariant(x); + } + + struct invariant_checker {}; + + template + struct invariant_checker_impl : invariant_checker + { + invariant_checker_impl(T const& self_) + : self(self_) + { + TORRENT_TRY + { + check_invariant(self); + } + TORRENT_CATCH_ALL + { + TORRENT_ASSERT(false); + } + } + + ~invariant_checker_impl() + { + TORRENT_TRY + { + check_invariant(self); + } + TORRENT_CATCH_ALL + { + TORRENT_ASSERT(false); + } + } + + T const& self; + }; + + template + invariant_checker_impl make_invariant_checker(T const& x) + { + return invariant_checker_impl(x); + } +} + +#define INVARIANT_CHECK \ + invariant_checker const& _invariant_check = make_invariant_checker(*this); \ + (void)_invariant_check; \ + do {} while (false) +#else +#define INVARIANT_CHECK do {} while (false) +#endif + +#endif // TORRENT_INVARIANT_ACCESS_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp new file mode 100644 index 0000000000..6e0c054fb2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io.hpp @@ -0,0 +1,170 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_HPP_INCLUDED +#define TORRENT_IO_HPP_INCLUDED + +#include +#include +#include // for copy +#include // for memcpy + +namespace libtorrent +{ + namespace detail + { + template struct type {}; + + // reads an integer from a byte stream + // in big endian byte order and converts + // it to native endianess + template + inline T read_impl(InIt& start, type) + { + T ret = 0; + for (int i = 0; i < (int)sizeof(T); ++i) + { + ret <<= 8; + ret |= static_cast(*start); + ++start; + } + return ret; + } + + template + boost::uint8_t read_impl(InIt& start, type) + { + return static_cast(*start++); + } + + template + boost::int8_t read_impl(InIt& start, type) + { + return static_cast(*start++); + } + + template + inline void write_impl(T val, OutIt& start) + { + for (int i = (int)sizeof(T)-1; i >= 0; --i) + { + *start = static_cast((val >> (i * 8)) & 0xff); + ++start; + } + } + + // -- adaptors + + template + boost::int64_t read_int64(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint64_t read_uint64(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint32_t read_uint32(InIt& start) + { return read_impl(start, type()); } + + template + boost::int32_t read_int32(InIt& start) + { return read_impl(start, type()); } + + template + boost::int16_t read_int16(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint16_t read_uint16(InIt& start) + { return read_impl(start, type()); } + + template + boost::int8_t read_int8(InIt& start) + { return read_impl(start, type()); } + + template + boost::uint8_t read_uint8(InIt& start) + { return read_impl(start, type()); } + + + template + void write_uint64(boost::uint64_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int64(boost::int64_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint32(boost::uint32_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int32(boost::int32_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint16(boost::uint16_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int16(boost::int16_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_uint8(boost::uint8_t val, OutIt& start) + { write_impl(val, start); } + + template + void write_int8(boost::int8_t val, OutIt& start) + { write_impl(val, start); } + + inline int write_string(std::string const& str, char*& start) + { + std::memcpy((void*)start, str.c_str(), str.size()); + start += str.size(); + return str.size(); + } + + template + int write_string(std::string const& val, OutIt& out) + { + for (std::string::const_iterator i = val.begin() + , end(val.end()); i != end; ++i) + *out++ = *i; + return int(val.length()); + } + } +} + +#endif // TORRENT_IO_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp new file mode 100644 index 0000000000..91ebdd5c05 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_SERVICE_HPP_INCLUDED +#define TORRENT_IO_SERVICE_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::io_service io_service; +#else + typedef boost::asio::io_service io_service; +#endif +} + +#endif + + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp new file mode 100644 index 0000000000..e083ef6636 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/io_service_fwd.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_SERVICE_FWD_HPP_INCLUDED +#define TORRENT_IO_SERVICE_FWD_HPP_INCLUDED + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#if BOOST_VERSION >= 103500 +namespace boost { +#endif +namespace asio { + +class io_service; + +} +#if BOOST_VERSION >= 103500 +} +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + typedef ::asio::io_service io_service; +#else + typedef boost::asio::io_service io_service; +#endif +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp new file mode 100644 index 0000000000..d868ea7cc9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_filter.hpp @@ -0,0 +1,362 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IP_FILTER_HPP +#define TORRENT_IP_FILTER_HPP + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/config.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + +// hidden +inline bool operator<=(address const& lhs + , address const& rhs) +{ + return lhs < rhs || lhs == rhs; +} + +template +struct ip_range +{ + Addr first; + Addr last; + int flags; +}; + +namespace detail +{ + + template + Addr zero() + { + Addr zero; + std::fill(zero.begin(), zero.end(), 0); + return zero; + } + + template<> + inline boost::uint16_t zero() { return 0; } + + template + Addr plus_one(Addr const& a) + { + Addr tmp(a); + for (int i = int(tmp.size()) - 1; i >= 0; --i) + { + if (tmp[i] < (std::numeric_limits::max)()) + { + tmp[i] += 1; + break; + } + tmp[i] = 0; + } + return tmp; + } + + inline boost::uint16_t plus_one(boost::uint16_t val) { return val + 1; } + + template + Addr minus_one(Addr const& a) + { + Addr tmp(a); + for (int i = int(tmp.size()) - 1; i >= 0; --i) + { + if (tmp[i] > 0) + { + tmp[i] -= 1; + break; + } + tmp[i] = (std::numeric_limits::max)(); + } + return tmp; + } + + inline boost::uint16_t minus_one(boost::uint16_t val) { return val - 1; } + + template + Addr max_addr() + { + Addr tmp; + std::fill(tmp.begin(), tmp.end() + , (std::numeric_limits::max)()); + return Addr(tmp); + } + + template<> + inline boost::uint16_t max_addr() + { return (std::numeric_limits::max)(); } + + // this is the generic implementation of + // a filter for a specific address type. + // it works with IPv4 and IPv6 + template + class filter_impl + { + public: + + filter_impl() + { + // make the entire ip-range non-blocked + m_access_list.insert(range(zero(), 0)); + } + + void add_rule(Addr first, Addr last, int flags) + { + TORRENT_ASSERT(!m_access_list.empty()); + TORRENT_ASSERT(first < last || first == last); + + typename range_t::iterator i = m_access_list.upper_bound(first); + typename range_t::iterator j = m_access_list.upper_bound(last); + + if (i != m_access_list.begin()) --i; + + TORRENT_ASSERT(j != m_access_list.begin()); + TORRENT_ASSERT(j != i); + + int first_access = i->access; + int last_access = boost::prior(j)->access; + + if (i->start != first && first_access != flags) + { + i = m_access_list.insert(i, range(first, flags)); + } + else if (i != m_access_list.begin() && boost::prior(i)->access == flags) + { + --i; + first_access = i->access; + } + TORRENT_ASSERT(!m_access_list.empty()); + TORRENT_ASSERT(i != m_access_list.end()); + + if (i != j) m_access_list.erase(boost::next(i), j); + if (i->start == first) + { + // we can do this const-cast because we know that the new + // start address will keep the set correctly ordered + const_cast(i->start) = first; + const_cast(i->access) = flags; + } + else if (first_access != flags) + { + m_access_list.insert(i, range(first, flags)); + } + + if ((j != m_access_list.end() + && minus_one(j->start) != last) + || (j == m_access_list.end() + && last != max_addr())) + { + TORRENT_ASSERT(j == m_access_list.end() || last < minus_one(j->start)); + if (last_access != flags) + j = m_access_list.insert(j, range(plus_one(last), last_access)); + } + + if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j); + TORRENT_ASSERT(!m_access_list.empty()); + } + + int access(Addr const& addr) const + { + TORRENT_ASSERT(!m_access_list.empty()); + typename range_t::const_iterator i = m_access_list.upper_bound(addr); + if (i != m_access_list.begin()) --i; + TORRENT_ASSERT(i != m_access_list.end()); + TORRENT_ASSERT(i->start <= addr && (boost::next(i) == m_access_list.end() + || addr < boost::next(i)->start)); + return i->access; + } + + template + std::vector > export_filter() const + { + std::vector > ret; + ret.reserve(m_access_list.size()); + + for (typename range_t::const_iterator i = m_access_list.begin() + , end(m_access_list.end()); i != end;) + { + ip_range r; + r.first = ExternalAddressType(i->start); + r.flags = i->access; + + ++i; + if (i == end) + r.last = ExternalAddressType(max_addr()); + else + r.last = ExternalAddressType(minus_one(i->start)); + + ret.push_back(r); + } + return ret; + } + + private: + + struct range + { + range(Addr addr, int a = 0): start(addr), access(a) {} + bool operator<(range const& r) const + { return start < r.start; } + bool operator<(Addr const& a) const + { return start < a; } + Addr start; + // the end of the range is implicit + // and given by the next entry in the set + int access; + }; + + typedef std::set range_t; + range_t m_access_list; + + }; + +} + +// The ``ip_filter`` class is a set of rules that uniquely categorizes all +// ip addresses as allowed or disallowed. The default constructor creates +// a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for +// the IPv4 range, and the equivalent range covering all addresses for the +// IPv6 range). +// +// A default constructed ip_filter does not filter any address. +struct TORRENT_EXPORT ip_filter +{ + // the flags defined for an IP range + enum access_flags + { + // indicates that IPs in this range should not be connected + // to nor accepted as incoming connections + blocked = 1 + }; + + // Adds a rule to the filter. ``first`` and ``last`` defines a range of + // ip addresses that will be marked with the given flags. The ``flags`` + // can currently be 0, which means allowed, or ``ip_filter::blocked``, which + // means disallowed. + // + // precondition: + // ``first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()`` + // + // postcondition: + // ``access(x) == flags`` for every ``x`` in the range [``first``, ``last``] + // + // This means that in a case of overlapping ranges, the last one applied takes + // precedence. + void add_rule(address first, address last, int flags); + + // Returns the access permissions for the given address (``addr``). The permission + // can currently be 0 or ``ip_filter::blocked``. The complexity of this operation + // is O(``log`` n), where n is the minimum number of non-overlapping ranges to describe + // the current filter. + int access(address const& addr) const; + +#if TORRENT_USE_IPV6 + typedef boost::tuple > + , std::vector > > filter_tuple_t; +#else + typedef std::vector > filter_tuple_t; +#endif + + // This function will return the current state of the filter in the minimum number of + // ranges possible. They are sorted from ranges in low addresses to high addresses. Each + // entry in the returned vector is a range with the access control specified in its + // ``flags`` field. + // + // The return value is a tuple containing two range-lists. One for IPv4 addresses + // and one for IPv6 addresses. + filter_tuple_t export_filter() const; + +// void print() const; + +private: + + detail::filter_impl m_filter4; +#if TORRENT_USE_IPV6 + detail::filter_impl m_filter6; +#endif +}; + +// the port filter maps non-overlapping port ranges to flags. This +// is primarily used to indicate whether a range of ports should +// be connected to or not. The default is to have the full port +// range (0-65535) set to flag 0. +class TORRENT_EXPORT port_filter +{ +public: + + // the defined flags for a port range + enum access_flags + { + // this flag indicates that destination ports in the + // range should not be connected to + blocked = 1 + }; + + // set the flags for the specified port range (``first``, ``last``) to + // ``flags`` overwriting any existing rule for those ports. The range + // is inclusive, i.e. the port ``last`` also has the flag set on it. + void add_rule(boost::uint16_t first, boost::uint16_t last, int flags); + + // test the specified port (``port``) for whether it is blocked + // or not. The returned value is the flags set for this port. + // see acces_flags. + int access(boost::uint16_t port) const; + +private: + + detail::filter_impl m_filter; + +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp new file mode 100644 index 0000000000..9d63942572 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ip_voter.hpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2013-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IP_VOTER_HPP_INCLUDED +#define TORRENT_IP_VOTER_HPP_INCLUDED + +#include +#include "libtorrent/address.hpp" +#include "libtorrent/bloom_filter.hpp" +#include "libtorrent/time.hpp" // for ptime + +namespace libtorrent +{ + // this is an object that keeps the state for a single external IP + // based on peoples votes + struct TORRENT_EXTRA_EXPORT ip_voter + { + ip_voter(); + + // returns true if a different IP is the top vote now + // i.e. we changed our idea of what our external IP is + bool cast_vote(address const& ip, int source_type, address const& sorce); + + address external_address() const { return m_external_address; } + + private: + + bool maybe_rotate(); + + struct external_ip_t + { + external_ip_t(): sources(0), num_votes(0) {} + + bool add_vote(sha1_hash const& k, int type); + + // we want to sort decending + bool operator<(external_ip_t const& rhs) const + { + if (num_votes > rhs.num_votes) return true; + if (num_votes < rhs.num_votes) return false; + return sources > rhs.sources; + } + + // this is a bloom filter of the IPs that have + // reported this address + bloom_filter<16> voters; + // this is the actual external address + address addr; + // a bitmask of sources the reporters have come from + boost::uint16_t sources; + // the total number of votes for this IP + boost::uint16_t num_votes; + }; + + // this is a bloom filter of all the IPs that have + // been the first to report an external address. Each + // IP only gets to add a new item once. + bloom_filter<32> m_external_address_voters; + + std::vector m_external_addresses; + address m_external_address; + + // the total number of unique IPs that have voted + int m_total_votes; + + // this is true from the first time we rotate. Before + // we rotate for the first time, we keep updating the + // external address as we go, since we don't have any + // stable setting to fall back on. Once this is true, + // we stop updating it on the fly, and just use the + // address from when we rotated. + bool m_valid_external; + + // the last time we rotated this ip_voter. i.e. threw + // away all the votes and started from scratch, in case + // our IP has changed + ptime m_last_rotate; + }; + + // this keeps track of multiple external IPs (for now, just IPv6 and IPv4, but + // it could be extended to deal with loopback and local network addresses as well) + struct TORRENT_EXTRA_EXPORT external_ip + { + // returns true if a different IP is the top vote now + // i.e. we changed our idea of what our external IP is + bool cast_vote(address const& ip, int source_type, address const& source); + + // the external IP as it would be observed from `ip` + address external_address(address const& ip) const; + + private: + + // for now, assume one external IPv4 and one external IPv6 address + // 0 = IPv4 1 = IPv6 + // TODO: 1 instead, have one instance per possible subnet, global IPv4, global IPv6, loopback, 192.168.x.x, 10.x.x.x, etc. + ip_voter m_vote_group[2]; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp new file mode 100644 index 0000000000..02fb347cec --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_observer.hpp @@ -0,0 +1,48 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef DHT_OBSERVER_HPP +#define DHT_OBSERVER_HPP + +#include "libtorrent/address.hpp" + +namespace libtorrent { namespace dht +{ + struct dht_observer + { + virtual void set_external_address(address const& addr + , address const& source) = 0; + }; +}} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp new file mode 100644 index 0000000000..691169b51f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp @@ -0,0 +1,191 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_DHT + +#ifndef TORRENT_DHT_TRACKER +#define TORRENT_DHT_TRACKER + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/traversal_algorithm.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +namespace libtorrent +{ + namespace aux { struct session_impl; } + struct lazy_entry; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(dht_tracker); +#endif + + struct dht_tracker; + + TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(dht_tracker const*); + TORRENT_EXTRA_EXPORT void intrusive_ptr_release(dht_tracker const*); + + struct dht_tracker : udp_socket_interface, udp_socket_observer + { + friend void intrusive_ptr_add_ref(dht_tracker const*); + friend void intrusive_ptr_release(dht_tracker const*); + + dht_tracker(libtorrent::aux::session_impl& ses, rate_limited_udp_socket& sock + , dht_settings const& settings, entry const* state = 0); + virtual ~dht_tracker(); + + void start(entry const& bootstrap + , find_data::nodes_callback const& f); + void stop(); + + void add_node(udp::endpoint node); + void add_node(std::pair const& node); + void add_router_node(udp::endpoint const& node); + + entry state() const; + + enum flags_t { flag_seed = 1, flag_implied_port = 2 }; + void announce(sha1_hash const& ih, int listen_port, int flags + , boost::function const&)> f); + + void get_item(sha1_hash const& target + , boost::function cb); + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void get_item(char const* key + , boost::function cb + , std::string salt = std::string()); + + void put_item(entry data + , boost::function cb); + + void put_item(char const* key + , boost::function cb, std::string salt = std::string()); + + void dht_status(session_status& s); + void network_stats(int& sent, int& received); + + // translate bittorrent kademlia message into the generic kademlia message + // used by the library + virtual bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_name_lookup(error_code const& e + , udp::resolver::iterator host); + void on_router_name_lookup(error_code const& e + , udp::resolver::iterator host); + void connection_timeout(error_code const& e); + void refresh_timeout(error_code const& e); + void tick(error_code const& e); + + // implements udp_socket_interface + virtual bool send_packet(libtorrent::entry& e, udp::endpoint const& addr + , int send_flags); + + node_impl m_dht; + rate_limited_udp_socket& m_sock; + + std::vector m_send_buf; + + ptime m_last_new_key; + deadline_timer m_timer; + deadline_timer m_connection_timer; + deadline_timer m_refresh_timer; + dht_settings const& m_settings; + int m_refresh_bucket; + + bool m_abort; + + // used to resolve hostnames for nodes + udp::resolver m_host_resolver; + + // sent and received bytes since queried last time + int m_sent_bytes; + int m_received_bytes; + + // used to ignore abusive dht nodes + struct node_ban_entry + { + node_ban_entry(): count(0) {} + address src; + ptime limit; + int count; + }; + + enum { num_ban_nodes = 20 }; + + node_ban_entry m_ban_nodes[num_ban_nodes]; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int m_replies_sent[5]; + int m_queries_received[5]; + int m_replies_bytes_sent[5]; + int m_queries_bytes_received[5]; + int m_counter; + + int m_total_message_input; + int m_total_in_bytes; + int m_total_out_bytes; + + int m_queries_out_bytes; +#endif + }; +}} + +#endif +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp new file mode 100644 index 0000000000..45a7ad3079 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/find_data.hpp @@ -0,0 +1,100 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef FIND_DATA_050323_HPP +#define FIND_DATA_050323_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +typedef std::vector packet_t; + +class rpc_manager; +class node_impl; + +// -------- find data ----------- + +struct find_data : traversal_algorithm +{ + typedef boost::function > const&)> nodes_callback; + + find_data(node_impl& node, node_id target + , nodes_callback const& ncallback); + + void got_write_token(node_id const& n, std::string const& write_token); + + virtual void start(); + + virtual char const* name() const; + + node_id const target() const { return m_target; } + +protected: + + virtual void done(); + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep + , node_id const& id); + + nodes_callback m_nodes_callback; + std::map m_write_tokens; + bool m_done; +}; + +struct find_data_observer : traversal_observer +{ + find_data_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : traversal_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // FIND_DATA_050323_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp new file mode 100644 index 0000000000..ee547a2c72 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_item.hpp @@ -0,0 +1,92 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_GET_ITEM_HPP +#define LIBTORRENT_GET_ITEM_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class get_item : public find_data +{ +public: + typedef boost::function data_callback; + + void got_data(lazy_entry const* v, + char const* pk, + boost::uint64_t seq, + char const* sig); + + // for immutable itms + get_item(node_impl& node + , node_id target + , data_callback const& dcallback); + + // for mutable items + get_item(node_impl& node + , char const* pk + , std::string const& salt + , data_callback const& dcallback); + + virtual char const* name() const; + +protected: + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); + virtual bool invoke(observer_ptr o); + virtual void done(); + + void put(std::vector > const& v); + + data_callback m_data_callback; + item m_data; + std::string m_salt; +}; + +class get_item_observer : public find_data_observer +{ +public: + get_item_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : find_data_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_GET_ITEM_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp new file mode 100644 index 0000000000..a80f6d6e9f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/get_peers.hpp @@ -0,0 +1,108 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_GET_PEERS_HPP +#define LIBTORRENT_GET_PEERS_HPP + +#include + +namespace libtorrent { namespace dht +{ + +struct get_peers : find_data +{ + typedef boost::function const&)> data_callback; + + void got_peers(std::vector const& peers); + + get_peers(node_impl& node, node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds); + + virtual char const* name() const; + +protected: + virtual bool invoke(observer_ptr o); + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); + + data_callback m_data_callback; + bool m_noseeds; +}; + +struct obfuscated_get_peers : get_peers +{ + typedef get_peers::nodes_callback done_callback; + + obfuscated_get_peers(node_impl& node, node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds); + + virtual char const* name() const; + +protected: + + virtual observer_ptr new_observer(void* ptr, udp::endpoint const& ep, + node_id const& id); + virtual bool invoke(observer_ptr o); + virtual void done(); +private: + // when set to false, we no longer obfuscate + // the target hash, and send regular get_peers + bool m_obfuscated; +}; + +struct get_peers_observer : find_data_observer +{ + get_peers_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : find_data_observer(algorithm, ep, id) + {} + + virtual void reply(msg const&); +}; + +struct obfuscated_get_peers_observer : traversal_observer +{ + obfuscated_get_peers_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : traversal_observer(algorithm, ep, id) + {} + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_GET_PEERS_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp new file mode 100644 index 0000000000..350c7e4660 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/item.hpp @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_ITEM_HPP +#define LIBTORRENT_ITEM_HPP + +#include +#include +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +// calculate the target hash for an immutable item. +sha1_hash TORRENT_EXTRA_EXPORT item_target_id( + std::pair v); + +// calculate the target hash for a mutable item. +sha1_hash TORRENT_EXTRA_EXPORT item_target_id(std::pair salt + , char const* pk); + +bool TORRENT_EXTRA_EXPORT verify_mutable_item( + std::pair v + , std::pair salt + , boost::uint64_t seq + , char const* pk + , char const* sig); + +// TODO: since this is a public function, it should probably be moved +// out of this header and into one with other public functions. + +// given a byte range ``v`` and an optional byte range ``salt``, a +// sequence number, public key ``pk`` (must be 32 bytes) and a secret key +// ``sk`` (must be 64 bytes), this function produces a signature which +// is written into a 64 byte buffer pointed to by ``sig``. The caller +// is responsible for allocating the destination buffer that's passed in +// as the ``sig`` argument. Typically it would be allocated on the stack. +void TORRENT_EXPORT sign_mutable_item( + std::pair v + , std::pair salt + , boost::uint64_t seq + , char const* pk + , char const* sk + , char* sig); + +enum +{ + item_pk_len = 32, + item_sk_len = 64, + item_sig_len = 64 +}; + +class TORRENT_EXTRA_EXPORT item +{ +public: + item() : m_seq(0), m_mutable(false) {} + item(char const* pk, std::string const& salt); + item(entry const& v) { assign(v); } + item(entry const& v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk); + item(lazy_entry const* v) { assign(v); } + + void assign(entry const& v) + { + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); + } + void assign(entry const& v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk); + void assign(lazy_entry const* v) + { + assign(v, std::pair(static_cast(NULL) + , 0), 0, NULL, NULL); + } + bool assign(lazy_entry const* v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sig); + void assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig); + + void clear() { m_value = entry(); } + bool empty() const { return m_value.type() == entry::undefined_t; } + + bool is_mutable() const { return m_mutable; } + + entry const& value() const { return m_value; } + boost::array const& pk() const + { return m_pk; } + boost::array const& sig() const + { return m_sig; } + boost::uint64_t seq() const { return m_seq; } + std::string const& salt() const { return m_salt; } + +private: + entry m_value; + std::string m_salt; + boost::array m_pk; + boost::array m_sig; + boost::uint64_t m_seq; + bool m_mutable; +}; + +} } // namespace libtorrent::dht + +#endif // LIBTORRENT_ITEM_HPP diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp new file mode 100644 index 0000000000..f10529a21e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/logging.hpp @@ -0,0 +1,140 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LOGGING_HPP +#define TORRENT_LOGGING_HPP + +#include "libtorrent/config.hpp" + +#if TORRENT_USE_IOSTREAM + +#include +#include +#include "libtorrent/ptime.hpp" + +namespace libtorrent { namespace dht +{ + +class log +{ +public: + log(char const* id, std::ostream& stream) + : m_id(id) + , m_enabled(true) + , m_stream(stream) + { + } + + char const* id() const + { + return m_id; + } + + bool enabled() const + { + return m_enabled; + } + + void enable(bool e) + { + m_enabled = e; + } + + void flush() { m_stream.flush(); } + + template + log& operator<<(T const& x) + { + m_stream << x; + return *this; + } + +private: + char const* m_id; + bool m_enabled; + std::ostream& m_stream; +}; + +class log_event +{ +public: + log_event(log& log); + ~log_event(); + + template + log_event& operator<<(T const& x) + { + log_ << x; + return *this; + } + + operator bool() const + { + return log_.enabled(); + } + +private: + log& log_; +}; + +class inverted_log_event : public log_event +{ +public: + inverted_log_event(log& log) : log_event(log) {} + + operator bool() const + { + return !log_event::operator bool(); + } +}; + +} } // namespace libtorrent::dht + +#define TORRENT_DECLARE_LOG(name) \ + libtorrent::dht::log& name ## _log() + +#define TORRENT_DEFINE_LOG(name) \ + libtorrent::dht::log& name ## _log() \ + { \ + static std::ofstream log_file("dht.log", std::ios::app); \ + static libtorrent::dht::log instance(#name, log_file); \ + return instance; \ + } + +#define TORRENT_LOG(name) \ + if (libtorrent::dht::inverted_log_event event_object__ = name ## _log()); \ + else static_cast(event_object__) + +#endif // TORRENT_USE_IOSTREAM + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp new file mode 100644 index 0000000000..e9dc6e1c16 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/msg.hpp @@ -0,0 +1,65 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef MSG_HPP +#define MSG_HPP + +#include +#include +#include "libtorrent/lazy_entry.hpp" +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +namespace libtorrent { +namespace dht { + +typedef std::vector packet_t; +typedef std::vector nodes_t; +typedef std::vector peers_t; + +struct msg +{ + msg(lazy_entry const& m, udp::endpoint const& ep): message(m), addr(ep) {} + // the message + lazy_entry const& message; + + // the address of the process sending or receiving + // the message. + udp::endpoint addr; +}; + +} } + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp new file mode 100644 index 0000000000..52703032f4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node.hpp @@ -0,0 +1,330 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NODE_HPP +#define NODE_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "libtorrent/socket.hpp" + +namespace libtorrent { + class alert_manager; + struct alert_dispatcher; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(node); +#endif + +struct traversal_algorithm; +struct dht_observer; + +struct key_desc_t +{ + char const* name; + int type; + int size; + int flags; + + enum { + // this argument is optional, parsing will not + // fail if it's not present + optional = 1, + // for dictionaries, the following entries refer + // to child nodes to this node, up until and including + // the next item that has the last_child flag set. + // these flags are nestable + parse_children = 2, + // this is the last item in a child dictionary + last_child = 4, + // the size argument refers to that the size + // has to be divisible by the number, instead + // of having that exact size + size_divisible = 8 + }; +}; + +bool TORRENT_EXTRA_EXPORT verify_message(lazy_entry const* msg, key_desc_t const desc[] + , lazy_entry const* ret[], int size , char* error, int error_size); + +// this is the entry for every peer +// the timestamp is there to make it possible +// to remove stale peers +struct peer_entry +{ + ptime added; + tcp::endpoint addr; + bool seed; +}; + +// this is a group. It contains a set of group members +struct torrent_entry +{ + std::string name; + std::set peers; +}; + +struct dht_immutable_item +{ + dht_immutable_item() : value(0), num_announcers(0), size(0) {} + // malloced space for the actual value + char* value; + // this counts the number of IPs we have seen + // announcing this item, this is used to determine + // popularity if we reach the limit of items to store + bloom_filter<128> ips; + // the last time we heard about this + ptime last_seen; + // number of IPs in the bloom filter + int num_announcers; + // size of malloced space pointed to by value + int size; +}; + +struct ed25519_public_key { char bytes[item_pk_len]; }; + +struct dht_mutable_item : dht_immutable_item +{ + char sig[item_sig_len]; + boost::uint64_t seq; + ed25519_public_key key; + char* salt; + int salt_size; +}; + +// internal +inline bool operator<(ed25519_public_key const& lhs, ed25519_public_key const& rhs) +{ + return memcmp(lhs.bytes, rhs.bytes, sizeof(lhs.bytes)) < 0; +} + +// internal +inline bool operator<(peer_entry const& lhs, peer_entry const& rhs) +{ + return lhs.addr.address() == rhs.addr.address() + ? lhs.addr.port() < rhs.addr.port() + : lhs.addr.address() < rhs.addr.address(); +} + +struct null_type {}; + +class announce_observer : public observer +{ +public: + announce_observer(boost::intrusive_ptr const& algo + , udp::endpoint const& ep, node_id const& id) + : observer(algo, ep, id) + {} + + void reply(msg const&) { flags |= flag_done; } +}; + +struct count_peers +{ + int& count; + count_peers(int& c): count(c) {} + void operator()(std::pair const& t) + { + count += t.second.peers.size(); + } +}; + +struct udp_socket_interface +{ + virtual bool send_packet(entry& e, udp::endpoint const& addr, int flags) = 0; +}; + +class TORRENT_EXTRA_EXPORT node_impl : boost::noncopyable +{ +typedef std::map table_t; +typedef std::map dht_immutable_table_t; +typedef std::map dht_mutable_table_t; + +public: + node_impl(alert_dispatcher* alert_disp, udp_socket_interface* sock + , libtorrent::dht_settings const& settings, node_id nid, address const& external_address + , dht_observer* observer); + + virtual ~node_impl() {} + + void tick(); + void bootstrap(std::vector const& nodes + , find_data::nodes_callback const& f); + void add_router_node(udp::endpoint router); + + void unreachable(udp::endpoint const& ep); + void incoming(msg const& m); + + int num_torrents() const { return m_map.size(); } + int num_peers() const + { + int ret = 0; + std::for_each(m_map.begin(), m_map.end(), count_peers(ret)); + return ret; + } + + int bucket_size(int bucket); + + node_id const& nid() const { return m_id; } + + boost::tuple size() const { return m_table.size(); } + size_type num_global_nodes() const + { return m_table.num_global_nodes(); } + + int data_size() const { return int(m_map.size()); } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + void print_state(std::ostream& os) const + { m_table.print_state(os); } +#endif + + enum flags_t { flag_seed = 1, flag_implied_port = 2 }; + void announce(sha1_hash const& info_hash, int listen_port, int flags + , boost::function const&)> f); + + void get_item(sha1_hash const& target, boost::function f); + void get_item(char const* pk, std::string const& salt, boost::function f); + + bool verify_token(std::string const& token, char const* info_hash + , udp::endpoint const& addr); + + std::string generate_token(udp::endpoint const& addr, char const* info_hash); + + // the returned time is the delay until connection_timeout() + // should be called again the next time + time_duration connection_timeout(); + + // generates a new secret number used to generate write tokens + void new_write_key(); + + // pings the given node, and adds it to + // the routing table if it respons and if the + // bucket is not full. + void add_node(udp::endpoint node); + + void replacement_cache(bucket_t& nodes) const + { m_table.replacement_cache(nodes); } + + int branch_factor() const { return m_settings.search_branching; } + + void add_traversal_algorithm(traversal_algorithm* a) + { + mutex_t::scoped_lock l(m_mutex); + m_running_requests.insert(a); + } + + void remove_traversal_algorithm(traversal_algorithm* a) + { + mutex_t::scoped_lock l(m_mutex); + m_running_requests.erase(a); + } + + void status(libtorrent::session_status& s); + + libtorrent::dht_settings const& settings() const { return m_settings; } + +protected: + + void send_single_refresh(udp::endpoint const& ep, int bucket + , node_id const& id = node_id()); + void lookup_peers(sha1_hash const& info_hash, entry& reply + , bool noseed, bool scrape) const; + bool lookup_torrents(sha1_hash const& target, entry& reply + , char* tags) const; + + libtorrent::dht_settings const& m_settings; + +private: + typedef libtorrent::mutex mutex_t; + mutex_t m_mutex; + + // this list must be destructed after the rpc manager + // since it might have references to it + std::set m_running_requests; + + void incoming_request(msg const& h, entry& e); + + node_id m_id; + +public: + routing_table m_table; + rpc_manager m_rpc; + +private: + dht_observer* m_observer; + + table_t m_map; + dht_immutable_table_t m_immutable_table; + dht_mutable_table_t m_mutable_table; + + ptime m_last_tracker_tick; + + // the last time we issued a bootstrap or a refresh on our own ID, to expand + // the routing table buckets close to us. + ptime m_last_self_refresh; + + // secret random numbers used to create write tokens + int m_secret[2]; + + alert_dispatcher* m_post_alert; + udp_socket_interface* m_sock; +}; + + +} } // namespace libtorrent::dht + +#endif // NODE_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp new file mode 100644 index 0000000000..6bc5acada3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_entry.hpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef KADEMLIA_NODE_ENTRY_HPP +#define KADEMLIA_NODE_ENTRY_HPP + +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/union_endpoint.hpp" +#include "libtorrent/time.hpp" // for time_now() + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include "libtorrent/time.hpp" +#endif + +namespace libtorrent { namespace dht +{ + +struct node_entry +{ + node_entry(node_id const& id_, udp::endpoint ep, int roundtriptime = 0xffff + , bool pinged = false) + : last_queried(pinged ? time_now() : min_time()) + , id(id_) + , a(ep.address().to_v4().to_bytes()) + , p(ep.port()) + , rtt(roundtriptime & 0xffff) + , timeout_count(pinged ? 0 : 0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + node_entry(udp::endpoint ep) + : last_queried(min_time()) + , id(0) + , a(ep.address().to_v4().to_bytes()) + , p(ep.port()) + , rtt(0xffff) + , timeout_count(0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + node_entry() + : last_queried(min_time()) + , id(0) + , p(0) + , rtt(0xffff) + , timeout_count(0xff) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + first_seen = time_now(); +#endif + } + + bool pinged() const { return timeout_count != 0xff; } + void set_pinged() { if (timeout_count == 0xff) timeout_count = 0; } + void timed_out() { if (pinged() && timeout_count < 0xfe) ++timeout_count; } + int fail_count() const { return pinged() ? timeout_count : 0; } + void reset_fail_count() { if (pinged()) timeout_count = 0; } + udp::endpoint ep() const { return udp::endpoint(address_v4(a), p); } + bool confirmed() const { return timeout_count == 0; } + void update_rtt(int new_rtt) + { + TORRENT_ASSERT(new_rtt <= 0xffff); + TORRENT_ASSERT(new_rtt >= 0); + if (new_rtt == 0xffff) return; + if (rtt == 0xffff) rtt = new_rtt; + else rtt = int(rtt) * 2 / 3 + int(new_rtt) / 3; + } + address addr() const { return address_v4(a); } + int port() const { return p; } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ptime first_seen; +#endif + + // the time we last received a response for a request to this peer + ptime last_queried; + + node_id id; + + address_v4::bytes_type a; + boost::uint16_t p; + + // the average RTT of this node + boost::uint16_t rtt; + + // the number of times this node has failed to + // respond in a row + boost::uint8_t timeout_count; +}; + +} } // namespace libtorrent::dht + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp new file mode 100644 index 0000000000..d4dd84a57c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/node_id.hpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef NODE_ID_HPP +#define NODE_ID_HPP + +#include + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/address.hpp" + +namespace libtorrent { namespace dht +{ + +struct node_entry; + +typedef libtorrent::sha1_hash node_id; + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id TORRENT_EXTRA_EXPORT distance(node_id const& n1, node_id const& n2); + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool TORRENT_EXTRA_EXPORT compare_ref(node_id const& n1, node_id const& n2, node_id const& ref); + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// useful for finding out which bucket a node belongs to +// the value that's returned is the number of trailing bits +// after the shared bit prefix of ``n1`` and ``n2``. +// if the first bits are different, that's 160. +int TORRENT_EXTRA_EXPORT distance_exp(node_id const& n1, node_id const& n2); + +node_id TORRENT_EXTRA_EXPORT generate_id(address const& external_ip); +node_id TORRENT_EXTRA_EXPORT generate_random_id(); +void TORRENT_EXTRA_EXPORT make_id_secret(node_id& in); +node_id TORRENT_EXTRA_EXPORT generate_secret_id(); +bool TORRENT_EXTRA_EXPORT verify_secret_id(node_id const& nid); +node_id TORRENT_EXTRA_EXPORT generate_id_impl(address const& ip_, boost::uint32_t r); + +bool TORRENT_EXTRA_EXPORT verify_id(node_id const& nid, address const& source_ip); +bool TORRENT_EXTRA_EXPORT matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index); +node_id TORRENT_EXTRA_EXPORT generate_prefix_mask(int bits); + +} } // namespace libtorrent::dht + +#endif // NODE_ID_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp new file mode 100644 index 0000000000..61c6d714ed --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/observer.hpp @@ -0,0 +1,181 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef OBSERVER_HPP +#define OBSERVER_HPP + +#include +#include +#include +#include +#include +#include + +namespace libtorrent { +namespace dht { + +struct observer; +struct msg; +struct traversal_algorithm; + +// defined in rpc_manager.cpp +TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); +TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); + +// intended struct layout (on 32 bit architectures) +// offset size alignment field +// 0 8 8 sent +// 8 8 4 m_refs +// 16 4 4 pool_allocator +// 20 16 4 m_addr +// 36 2 2 m_port +// 38 1 1 flags +// 39 1 1 +// 40 + +struct observer : boost::noncopyable +{ + friend TORRENT_EXTRA_EXPORT void intrusive_ptr_add_ref(observer const*); + friend TORRENT_EXTRA_EXPORT void intrusive_ptr_release(observer const*); + + observer(boost::intrusive_ptr const& a + , udp::endpoint const& ep, node_id const& id) + : m_sent() + , m_refs(0) + , m_algorithm(a) + , m_id(id) + , m_port(0) + , m_transaction_id() + , flags(0) + { + TORRENT_ASSERT(a); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + m_in_constructor = true; + m_was_sent = false; + m_was_abandoned = false; + m_in_use = true; +#endif + set_target(ep); + } + + // defined in rpc_manager.cpp + virtual ~observer(); + + // this is called when a reply is received + virtual void reply(msg const& m) = 0; + + // this is called if no response has been received after + // a few seconds, before the request has timed out + void short_timeout(); + + bool has_short_timeout() const { return (flags & flag_short_timeout) != 0; } + + // this is called when no reply has been received within + // some timeout + void timeout(); + + // if this is called the destructor should + // not invoke any new messages, and should + // only clean up. It means the rpc-manager + // is being destructed + void abort(); + + ptime sent() const { return m_sent; } + + void set_target(udp::endpoint const& ep); + address target_addr() const; + udp::endpoint target_ep() const; + + void set_id(node_id const& id); + node_id const& id() const { return m_id; } + + void set_transaction_id(boost::uint16_t tid) + { m_transaction_id = tid; } + + boost::uint16_t transaction_id() const + { return m_transaction_id; } + + enum { + flag_queried = 1, + flag_initial = 2, + flag_no_id = 4, + flag_short_timeout = 8, + flag_failed = 16, + flag_ipv6_address = 32, + flag_alive = 64, + flag_done = 128 + }; + +#ifndef TORRENT_DHT_VERBOSE_LOGGING +protected: +#endif + + void done(); + + ptime m_sent; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + + const boost::intrusive_ptr m_algorithm; + + node_id m_id; + + TORRENT_UNION addr_t + { +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + address_v4::bytes_type v4; + } m_addr; + + boost::uint16_t m_port; + + // the transaction ID for this call + boost::uint16_t m_transaction_id; +public: + unsigned char flags; + +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + bool m_in_constructor:1; + bool m_was_sent:1; + bool m_was_abandoned:1; + bool m_in_use:1; +#endif +}; + +typedef boost::intrusive_ptr observer_ptr; + +} } + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp new file mode 100644 index 0000000000..3285997971 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/refresh.hpp @@ -0,0 +1,71 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef REFRESH_050324_HPP +#define REFRESH_050324_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class routing_table; +class rpc_manager; + +class bootstrap : public get_peers +{ +public: + typedef get_peers::nodes_callback done_callback; + + bootstrap(node_impl& node, node_id target + , done_callback const& callback); + virtual char const* name() const; + + observer_ptr new_observer(void* ptr, udp::endpoint const& ep + , node_id const& id); + + void trim_seed_nodes(); + +protected: + + virtual bool invoke(observer_ptr o); + + virtual void done(); + +}; + +} } // namespace libtorrent::dht + +#endif // REFRESH_050324_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp new file mode 100644 index 0000000000..b40fb76989 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/routing_table.hpp @@ -0,0 +1,235 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ROUTING_TABLE_HPP +#define ROUTING_TABLE_HPP + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace libtorrent +{ + struct session_status; +} + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(table); +#endif + + +typedef std::vector bucket_t; + +struct routing_table_node +{ + bucket_t replacements; + bucket_t live_nodes; +}; + +// differences in the implementation from the description in +// the paper: +// +// * Nodes are not marked as being stale, they keep a counter +// that tells how many times in a row they have failed. When +// a new node is to be inserted, the node that has failed +// the most times is replaced. If none of the nodes in the +// bucket has failed, then it is put in the replacement +// cache (just like in the paper). + +class TORRENT_EXTRA_EXPORT routing_table : boost::noncopyable +{ +public: + typedef std::vector table_t; + + routing_table(node_id const& id, int bucket_size + , dht_settings const& settings); + + void status(session_status& s) const; + + void node_failed(node_id const& id, udp::endpoint const& ep); + + // adds an endpoint that will never be added to + // the routing table + void add_router_node(udp::endpoint router); + + // iterates over the router nodes added + typedef std::set::const_iterator router_iterator; + router_iterator router_begin() const { return m_router_nodes.begin(); } + router_iterator router_end() const { return m_router_nodes.end(); } + + enum add_node_status_t { + failed_to_add = 0, + node_added, + need_bucket_split + }; + add_node_status_t add_node_impl(node_entry e); + + bool add_node(node_entry e); + + // this function is called every time the node sees + // a sign of a node being alive. This node will either + // be inserted in the k-buckets or be moved to the top + // of its bucket. + bool node_seen(node_id const& id, udp::endpoint ep, int rtt); + + // this may add a node to the routing table and mark it as + // not pinged. If the bucket the node falls into is full, + // the node will be ignored. + void heard_about(node_id const& id, udp::endpoint const& ep); + + node_entry const* next_refresh(); + + enum + { + // nodes that have not been pinged are considered failed by this flag + include_failed = 1 + }; + + // fills the vector with the count nodes from our buckets that + // are nearest to the given id. + void find_node(node_id const& id, std::vector& l + , int options, int count = 0); + void remove_node(node_entry* n + , table_t::iterator bucket) ; + + int bucket_size(int bucket) const + { + int num_buckets = m_buckets.size(); + if (num_buckets == 0) return 0; + if (bucket < num_buckets) bucket = num_buckets - 1; + table_t::const_iterator i = m_buckets.begin(); + std::advance(i, bucket); + return (int)i->live_nodes.size(); + } + + void for_each_node(void (*)(void*, node_entry const&) + , void (*)(void*, node_entry const&), void* userdata) const; + + int bucket_size() const { return m_bucket_size; } + + // returns the number of nodes in the main buckets, number of nodes in the + // replacement buckets and the number of nodes in the main buckets that have + // been pinged and confirmed up + boost::tuple size() const; + + size_type num_global_nodes() const; + + // the number of bits down we have full buckets + // i.e. essentially the number of full buckets + // we have + int depth() const; + + int num_active_buckets() const { return m_buckets.size(); } + + void replacement_cache(bucket_t& nodes) const; + +#if defined TORRENT_DHT_VERBOSE_LOGGING || defined TORRENT_DEBUG + // used for debug and monitoring purposes. This will print out + // the state of the routing table to the given stream + void print_state(std::ostream& os) const; +#endif + + int bucket_limit(int bucket) const; + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +private: + + table_t::iterator find_bucket(node_id const& id); + + void split_bucket(); + + // return a pointer the node_entry with the given endpoint + // or 0 if we don't have such a node. Both the address and the + // port has to match + node_entry* find_node(udp::endpoint const& ep + , routing_table::table_t::iterator* bucket); + + dht_settings const& m_settings; + + // constant called k in paper + int m_bucket_size; + + // (k-bucket, replacement cache) pairs + // the first entry is the bucket the furthest + // away from our own ID. Each time the bucket + // closest to us (m_buckets.back()) has more than + // bucket size nodes in it, another bucket is + // added to the end and it's split up between them + table_t m_buckets; + + node_id m_id; // our own node id + + // the last seen depth (i.e. levels in the routing table) + // it's mutable because it's updated by depth(), which is const + mutable int m_depth; + + // the last time we refreshed our own bucket + // refreshed every 15 minutes + mutable ptime m_last_self_refresh; + + // this is a set of all the endpoints that have + // been identified as router nodes. They will + // be used in searches, but they will never + // be added to the routing table. + std::set m_router_nodes; + + // these are all the IPs that are in the routing + // table. It's used to only allow a single entry + // per IP in the whole table. Currently only for + // IPv4 + std::multiset m_ips; +}; + +} } // namespace libtorrent::dht + +#endif // ROUTING_TABLE_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp new file mode 100644 index 0000000000..4e277079c0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp @@ -0,0 +1,126 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef RPC_MANAGER_HPP +#define RPC_MANAGER_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "libtorrent/ptime.hpp" + +namespace libtorrent { namespace aux { struct session_impl; } } + +namespace libtorrent { struct dht_settings; } + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(rpc); +#endif + +struct udp_socket_interface; + +struct null_observer : public observer +{ + null_observer(boost::intrusive_ptr const& a + , udp::endpoint const& ep, node_id const& id): observer(a, ep, id) {} + virtual void reply(msg const&) { flags |= flag_done; } +}; + +class routing_table; + +class TORRENT_EXTRA_EXPORT rpc_manager +{ +public: + + rpc_manager(node_id const& our_id + , routing_table& table, udp_socket_interface* sock); + ~rpc_manager(); + + void unreachable(udp::endpoint const& ep); + + // returns true if the node needs a refresh + // if so, id is assigned the node id to refresh + bool incoming(msg const&, node_id* id, libtorrent::dht_settings const& settings); + time_duration tick(); + + bool invoke(entry& e, udp::endpoint target + , observer_ptr o); + + void add_our_id(entry& e); + +#if TORRENT_USE_ASSERTS + size_t allocation_size() const; +#endif +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + void* allocate_observer(); + void free_observer(void* ptr); + + int num_allocated_observers() const { return m_allocated_observers; } + +private: + + boost::uint32_t calc_connection_id(udp::endpoint addr); + + mutable boost::pool<> m_pool_allocator; + + typedef std::deque transactions_t; + transactions_t m_transactions; + + udp_socket_interface* m_sock; + routing_table& m_table; + ptime m_timer; + node_id m_our_id; + int m_allocated_observers; + bool m_destructing; +}; + +} } // namespace libtorrent::dht + +#endif + + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp new file mode 100644 index 0000000000..def31d8adc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp @@ -0,0 +1,143 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TRAVERSAL_ALGORITHM_050324_HPP +#define TRAVERSAL_ALGORITHM_050324_HPP + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace libtorrent { struct dht_lookup; } +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(traversal); +#endif + +class rpc_manager; +class node_impl; + +// this class may not be instantiated as a stack object +struct traversal_algorithm : boost::noncopyable +{ + void traverse(node_id const& id, udp::endpoint addr); + void finished(observer_ptr o); + + enum flags_t { prevent_request = 1, short_timeout = 2 }; + void failed(observer_ptr o, int flags = 0); + virtual ~traversal_algorithm(); + void status(dht_lookup& l); + void abort(); + + void* allocate_observer(); + void free_observer(void* ptr); + + virtual char const* name() const; + virtual void start(); + + node_id const& target() const { return m_target; } + + void resort_results(); + void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); + + traversal_algorithm(node_impl& node, node_id target); + int invoke_count() const { return m_invoke_count; } + int branch_factor() const { return m_branch_factor; } + + node_impl& node() const { return m_node; } + +protected: + + // returns true if we're done + bool add_requests(); + + void add_router_entries(); + void init(); + + virtual void done(); + // should construct an algorithm dependent + // observer in ptr. + virtual observer_ptr new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id); + + virtual bool invoke(observer_ptr o) { return false; } + + friend void intrusive_ptr_add_ref(traversal_algorithm* p) + { + p->m_ref_count++; + } + + friend void intrusive_ptr_release(traversal_algorithm* p) + { + if (--p->m_ref_count == 0) + delete p; + } + + int m_ref_count; + + node_impl& m_node; + node_id const m_target; + std::vector m_results; + int m_invoke_count; + int m_branch_factor; + int m_responses; + int m_timeouts; + int m_num_target_nodes; +}; + +struct traversal_observer : observer +{ + traversal_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : observer(algorithm, ep, id) + {} + + // parses out "nodes" and keeps traversing + virtual void reply(msg const&); +}; + +} } // namespace libtorrent::dht + +#endif // TRAVERSAL_ALGORITHM_050324_HPP + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp new file mode 100644 index 0000000000..8c029c6714 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/lazy_entry.hpp @@ -0,0 +1,453 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LAZY_ENTRY_HPP_INCLUDED +#define TORRENT_LAZY_ENTRY_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + struct lazy_entry; + + // This function decodes bencoded_ data. + // + // .. _bencoded: http://wiki.theory.org/index.php/BitTorrentSpecification + // + // Whenever possible, ``lazy_bdecode()`` should be preferred over ``bdecode()``. + // It is more efficient and more secure. It supports having constraints on the + // amount of memory is consumed by the parser. + // + // *lazy* refers to the fact that it doesn't copy any actual data out of the + // bencoded buffer. It builds a tree of ``lazy_entry`` which has pointers into + // the bencoded buffer. This makes it very fast and efficient. On top of that, + // it is not recursive, which saves a lot of stack space when parsing deeply + // nested trees. However, in order to protect against potential attacks, the + // ``depth_limit`` and ``item_limit`` control how many levels deep the tree is + // allowed to get. With recursive parser, a few thousand levels would be enough + // to exhaust the threads stack and terminate the process. The ``item_limit`` + // protects against very large structures, not necessarily deep. Each bencoded + // item in the structure causes the parser to allocate some amount of memory, + // this memory is constant regardless of how much data actually is stored in + // the item. One potential attack is to create a bencoded list of hundreds of + // thousands empty strings, which would cause the parser to allocate a significant + // amount of memory, perhaps more than is available on the machine, and effectively + // provide a denial of service. The default item limit is set as a reasonable + // upper limit for desktop computers. Very few torrents have more items in them. + // The limit corresponds to about 25 MB, which might be a bit much for embedded + // systems. + // + // ``start`` and ``end`` defines the bencoded buffer to be decoded. ``ret`` is + // the ``lazy_entry`` which is filled in with the whole decoded tree. ``ec`` + // is a reference to an ``error_code`` which is set to describe the error encountered + // in case the function fails. ``error_pos`` is an optional pointer to an int, + // which will be set to the byte offset into the buffer where an error occurred, + // in case the function fails. + TORRENT_EXPORT int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, error_code& ec, int* error_pos = 0 + , int depth_limit = 1000, int item_limit = 1000000); + +#ifndef TORRENT_NO_DEPRECATE + // for backwards compatibility, does not report error code + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + TORRENT_EXPORT int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, int depth_limit = 1000, int item_limit = 1000000) TORRENT_DEPRECATED; +#endif + + // this is a string that is not NULL-terminated. Instead it + // comes with a length, specified in bytes. This is particularly + // useful when parsing bencoded structures, because strings are + // not NULL-terminated internally, and requiring NULL termination + // would require copying the string. + // + // see lazy_entry::string_pstr(). + struct TORRENT_EXPORT pascal_string + { + // construct a string pointing to the characters at ``p`` + // of length ``l`` characters. No NULL termination is required. + pascal_string(char const* p, int l): len(l), ptr(p) {} + + // the number of characters in the string. + int len; + + // the pointer to the first character in the string. This is + // not NULL terminated, but instead consult the ``len`` field + // to know how many characters follow. + char const* ptr; + + // lexicographical comparison of strings. Order is consisten + // with memcmp. + bool operator<(pascal_string const& rhs) const + { + return std::memcmp(ptr, rhs.ptr, (std::min)(len, rhs.len)) < 0 + || len < rhs.len; + } + }; + + struct lazy_dict_entry; + + // this object represent a node in a bencoded structure. It is a variant + // type whose concrete type is one of: + // + // 1. dictionary (maps strings -> lazy_entry) + // 2. list (sequence of lazy_entry, i.e. heterogenous) + // 3. integer + // 4. string + // + // There is also a ``none`` type, which is used for uninitialized + // lazy_entries. + struct TORRENT_EXPORT lazy_entry + { + // The different types a lazy_entry can have + enum entry_type_t + { + none_t, dict_t, list_t, string_t, int_t + }; + + // internal + lazy_entry() : m_begin(0), m_len(0), m_size(0), m_capacity(0), m_type(none_t) + { m_data.start = 0; } + + // tells you which specific type this lazy entry has. + // See entry_type_t. The type determines which subset of + // member functions are valid to use. + entry_type_t type() const { return (entry_type_t)m_type; } + + // start points to the first decimal digit + // length is the number of digits + void construct_int(char const* start, int length) + { + TORRENT_ASSERT(m_type == none_t); + m_type = int_t; + m_data.start = start; + m_size = length; + m_begin = start - 1; // include 'i' + m_len = length + 2; // include 'e' + } + + // requires the type to be an integer. return the integer value + boost::int64_t int_value() const; + + // internal + void construct_string(char const* start, int length); + + // the string is not null-terminated! + // use string_length() to determine how many bytes + // are part of the string. + char const* string_ptr() const + { + TORRENT_ASSERT(m_type == string_t); + return m_data.start; + } + + // this will return a null terminated string + // it will write to the source buffer! + char const* string_cstr() const + { + TORRENT_ASSERT(m_type == string_t); + const_cast(m_data.start)[m_size] = 0; + return m_data.start; + } + + // if this is a string, returns a pascal_string + // representing the string value. + pascal_string string_pstr() const + { + TORRENT_ASSERT(m_type == string_t); + return pascal_string(m_data.start, m_size); + } + + // if this is a string, returns the string as a std::string. + // (which requires a copy) + std::string string_value() const + { + TORRENT_ASSERT(m_type == string_t); + return std::string(m_data.start, m_size); + } + + // if the lazy_entry is a string, returns the + // length of the string, in bytes. + int string_length() const + { return m_size; } + + // internal + void construct_dict(char const* begin) + { + TORRENT_ASSERT(m_type == none_t); + m_type = dict_t; + m_size = 0; + m_capacity = 0; + m_begin = begin; + } + + // internal + lazy_entry* dict_append(char const* name); + // internal + void pop(); + + // if this is a dictionary, look for a key ``name``, and return + // a pointer to its value, or NULL if there is none. + lazy_entry* dict_find(char const* name); + lazy_entry const* dict_find(char const* name) const + { return const_cast(this)->dict_find(name); } + lazy_entry* dict_find(std::string const& name); + lazy_entry const* dict_find(std::string const& name) const + { return const_cast(this)->dict_find(name); } + lazy_entry const* dict_find_string(char const* name) const; + + // if this is a dictionary, look for a key ``name`` whose value + // is a string. If such key exist, return a pointer to + // its value, otherwise NULL. + std::string dict_find_string_value(char const* name) const; + pascal_string dict_find_pstr(char const* name) const; + + // if this is a dictionary, look for a key ``name`` whose value + // is an int. If such key exist, return a pointer to its value, + // otherwise NULL. + boost::int64_t dict_find_int_value(char const* name, boost::int64_t default_val = 0) const; + lazy_entry const* dict_find_int(char const* name) const; + + // these functions require that ``this`` is a dictionary. + // (this->type() == dict_t). They look for an element with the + // specified name in the dictionary. ``dict_find_dict`` only + // finds dictionaries and ``dict_find_list`` only finds lists. + // if no key with the corresponding value of the right type is + // found, NULL is returned. + lazy_entry const* dict_find_dict(char const* name) const; + lazy_entry const* dict_find_dict(std::string const& name) const; + lazy_entry const* dict_find_list(char const* name) const; + + // if this is a dictionary, return the key value pair at + // position ``i`` from the dictionary. + std::pair dict_at(int i) const; + + // requires that ``this`` is a dictionary. return the + // number of items in it + int dict_size() const + { + TORRENT_ASSERT(m_type == dict_t); + return m_size; + } + + // internal + void construct_list(char const* begin) + { + TORRENT_ASSERT(m_type == none_t); + m_type = list_t; + m_size = 0; + m_capacity = 0; + m_begin = begin; + } + + // internal + lazy_entry* list_append(); + + // requires that ``this`` is a list. return + // the item at index ``i``. + lazy_entry* list_at(int i) + { + TORRENT_ASSERT(m_type == list_t); + TORRENT_ASSERT(i < int(m_size)); + return &m_data.list[i]; + } + lazy_entry const* list_at(int i) const + { return const_cast(this)->list_at(i); } + + // these functions require ``this`` to have the type list. + // (this->type() == list_t). ``list_string_value_at`` returns + // the string at index ``i``. ``list_pstr_at`` + // returns a pascal_string of the string value at index ``i``. + // if the element at ``i`` is not a string, an empty string + // is returned. + std::string list_string_value_at(int i) const; + pascal_string list_pstr_at(int i) const; + + // this function require ``this`` to have the type list. + // (this->type() == list_t). returns the integer value at + // index ``i``. If the element at ``i`` is not an integer + // ``default_val`` is returned, which defaults to 0. + boost::int64_t list_int_value_at(int i, boost::int64_t default_val = 0) const; + + // if this is a list, return the number of items in it. + int list_size() const + { + TORRENT_ASSERT(m_type == list_t); + return int(m_size); + } + + // internal: end points one byte passed last byte in the source + // buffer backing the bencoded structure. + void set_end(char const* end) + { + TORRENT_ASSERT(end > m_begin); + TORRENT_ASSERT(end - m_begin < INT_MAX); + m_len = int(end - m_begin); + } + + // internal + void clear(); + + // internal: releases ownership of any memory allocated + void release() + { + m_data.start = 0; + m_size = 0; + m_capacity = 0; + m_type = none_t; + } + + // internal + ~lazy_entry() + { clear(); } + + // returns pointers into the source buffer where + // this entry has its bencoded data + std::pair data_section() const; + + // swap values of ``this`` and ``e``. + void swap(lazy_entry& e) + { + using std::swap; + boost::uint32_t tmp = e.m_type; + e.m_type = m_type; + m_type = tmp; + tmp = e.m_capacity; + e.m_capacity = m_capacity; + m_capacity = tmp; + swap(m_data.start, e.m_data.start); + swap(m_size, e.m_size); + swap(m_begin, e.m_begin); + swap(m_len, e.m_len); + } + + private: + + union data_t + { + lazy_dict_entry* dict; + lazy_entry* list; + char const* start; + } m_data; + + // used for dictionaries and lists to record the range + // in the original buffer they are based on + char const* m_begin; + // the number of bytes this entry extends in the + // bencoded byffer + boost::uint32_t m_len; + + // if list or dictionary, the number of items + boost::uint32_t m_size; + // if list or dictionary, allocated number of items + boost::uint32_t m_capacity:29; + // element type (dict, list, int, string) + boost::uint32_t m_type:3; + + // non-copyable + lazy_entry(lazy_entry const&); + lazy_entry const& operator=(lazy_entry const&); + }; + + struct lazy_dict_entry + { + char const* name; + lazy_entry val; + }; + + // print the bencoded structure in a human-readable format to a stting + // that's returned. + TORRENT_EXPORT std::string print_entry(lazy_entry const& e + , bool single_line = false, int indent = 0); + + // get the ``error_category`` for bdecode errors + TORRENT_EXPORT boost::system::error_category& get_bdecode_category(); + + namespace bdecode_errors + { + // libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has + // its own error category get_bdecode_category() whith the error codes defined by error_code_enum. + enum error_code_enum + { + // Not an error + no_error = 0, + // expected string in bencoded string + expected_string, + // expected colon in bencoded string + expected_colon, + // unexpected end of file in bencoded string + unexpected_eof, + // expected value (list, dict, int or string) in bencoded string + expected_value, + // bencoded recursion depth limit exceeded + depth_exceeded, + // bencoded item count limit exceeded + limit_exceeded, + // integer overflow + overflow, + + // the number of error codes + error_code_max + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + + TORRENT_EXTRA_EXPORT char const* parse_int(char const* start + , char const* end, char delimiter, boost::int64_t& val + , bdecode_errors::error_code_enum& ec); + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp new file mode 100644 index 0000000000..37a7aec2b9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/lsd.hpp @@ -0,0 +1,109 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LSD_HPP +#define TORRENT_LSD_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +typedef boost::function peer_callback_t; + +class lsd : public intrusive_ptr_base +{ +public: + lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb); + ~lsd(); + +// void rebind(address const& listen_interface); + + void announce(sha1_hash const& ih, int listen_port, bool broadcast = false); + void close(); + +private: + + void announce_impl(sha1_hash const& ih, int listen_port + , bool broadcast, int retry_count); + void resend_announce(error_code const& e, sha1_hash const& ih + , int listen_port, int retry_count); + void on_announce(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred); + + peer_callback_t m_callback; + + // the udp socket used to send and receive + // multicast messages on + broadcast_socket m_socket; +#if TORRENT_USE_IPV6 + broadcast_socket m_socket6; +#endif + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + // this is a random (presumably unique) + // ID for this LSD node. It is used to + // ignore our own broadcast messages. + // There's no point in adding ourselves + // as a peer + int m_cookie; + + bool m_disabled; +#if TORRENT_USE_IPV6 + bool m_disabled6; +#endif +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + FILE* m_log; +#endif +}; + +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp new file mode 100644 index 0000000000..74024cf937 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/magnet_uri.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_MAGNET_URI_HPP_INCLUDED +#define TORRENT_MAGNET_URI_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/add_torrent_params.hpp" + +namespace libtorrent +{ + struct torrent_handle; + class session; + + // Generates a magnet URI from the specified torrent. If the torrent + // handle is invalid, an empty string is returned. + // + // For more information about magnet links, see magnet-links_. + // + std::string TORRENT_EXPORT make_magnet_uri(torrent_handle const& handle); + std::string TORRENT_EXPORT make_magnet_uri(torrent_info const& info); + +#ifndef TORRENT_NO_DEPRECATE +#ifndef BOOST_NO_EXCEPTIONS + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , std::string const& save_path + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; + + // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p) TORRENT_DEPRECATED; +#endif + + // deprecated in 0.16. Instead, pass in the magnet link as add_torrent_params::url + TORRENT_DEPRECATED_PREFIX + torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) TORRENT_DEPRECATED; + +#endif + + // This function parses out information from the magnet link and populates the + // add_torrent_params object. + TORRENT_EXPORT void parse_magnet_uri(std::string const& uri, add_torrent_params& p, error_code& ec); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp new file mode 100644 index 0000000000..f6c88002cd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/max.hpp @@ -0,0 +1,123 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_MAX_TYPE +#define TORRENT_MAX_TYPE + +namespace libtorrent +{ + + template + struct max { enum { value = v1>v2?v1:v2 }; }; + + template + struct max3 + { + enum + { + temp = max::value, + value = max::value + }; + }; + + template + struct max4 + { + enum + { + temp1 = max::value, + temp2 = max::value, + value = max::value + }; + }; + + template + struct max5 + { + enum + { + temp = max4::value, + value = max::value + }; + }; + + template + struct max6 + { + enum + { + temp1 = max::value, + temp2 = max::value, + temp3 = max::value, + value = max3::value + }; + }; + + template + struct max7 + { + enum + { + temp1 = max::value, + temp2 = max::value, + temp3 = max3::value, + value = max3::value + }; + }; + + template + struct max8 + { + enum + { + temp1 = max::value, + temp2 = max3::value, + temp3 = max3::value, + value = max3::value + }; + }; + + template + struct max9 + { + enum + { + temp1 = max3::value, + temp2 = max3::value, + temp3 = max3::value, + value = max3::value + }; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp new file mode 100644 index 0000000000..81c9198242 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/natpmp.hpp @@ -0,0 +1,177 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_NATPMP_HPP +#define TORRENT_NATPMP_HPP + +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include + +namespace libtorrent +{ + +// int: port mapping index +// int: external port +// std::string: error message +typedef boost::function portmap_callback_t; +typedef boost::function log_callback_t; + +class natpmp : public intrusive_ptr_base +{ +public: + natpmp(io_service& ios, address const& listen_interface + , portmap_callback_t const& cb + , log_callback_t const& lcb); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + enum protocol_type { none = 0, udp = 1, tcp = 2 }; + int add_mapping(protocol_type p, int external_port, int local_port); + void delete_mapping(int mapping_index); + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; + + void close(); + +private: + + void update_mapping(int i, mutex::scoped_lock& l); + void send_map_request(int i, mutex::scoped_lock& l); + void send_get_ip_address_request(mutex::scoped_lock& l); + void resend_request(int i, error_code const& e); + void on_reply(error_code const& e + , std::size_t bytes_transferred); + void try_next_mapping(int i, mutex::scoped_lock& l); + void update_expiration_timer(mutex::scoped_lock& l); + void mapping_expired(error_code const& e, int i); + void close_impl(mutex::scoped_lock& l); + + void log(char const* msg, mutex::scoped_lock& l); + void disable(error_code const& ec, mutex::scoped_lock& l); + + struct mapping_t + { + enum action_t { action_none, action_add, action_delete }; + mapping_t() + : action(action_none) + , local_port(0) + , external_port(0) + , protocol(none) + , map_sent(false) + , outstanding_request(false) + {} + + // indicates that the mapping has changed + // and needs an update + int action; + + // the time the port mapping will expire + ptime expires; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + int protocol; + + // set to true when the first map request is sent + bool map_sent; + + // set to true while we're waiting for a response + bool outstanding_request; + }; + + portmap_callback_t m_callback; + log_callback_t m_log_callback; + + std::vector m_mappings; + + // the endpoint to the nat router + udp::endpoint m_nat_endpoint; + + // this is the mapping that is currently + // being updated. It is -1 in case no + // mapping is being updated at the moment + int m_currently_mapping; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_response_buffer[16]; + + // router external IP address + address m_external_ip; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to communicate + // with the NAT router + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_send_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + // the mapping index that will expire next + int m_next_refresh; + + bool m_disabled; + + bool m_abort; + + mutable mutex m_mutex; +}; + +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp new file mode 100644 index 0000000000..10b909958d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/packet_buffer.hpp @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg, Daniel Wallin. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PACKET_BUFFER_HPP_INCLUDED +#define TORRENT_PACKET_BUFFER_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "boost/cstdint.hpp" +#include + +namespace libtorrent +{ + // this is a circular buffer that automatically resizes + // itself as elements are inserted. Elements are indexed + // by integers and are assumed to be sequential. Unless the + // old elements are removed when new elements are inserted, + // the buffer will be resized. + + // m_capacity is the number of elements in m_array + // and must be an even 2^x. + // m_first is the lowest index that has an element + // it also determines which indices the other slots + // refers to. Since it's a circular buffer, it wraps + // around. For example + + // m_first = 9 + // | refers to index 14 + // | | + // V V + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | | | | | | | | | | | | | | | | mask = (m_capacity-1) + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ^ + // | + // refers to index 15 + + // whenever the element at the cursor is removed, the + // cursor is bumped to the next occupied element + + class TORRENT_EXTRA_EXPORT packet_buffer + { + public: + typedef boost::uint32_t index_type; + + packet_buffer(); + ~packet_buffer(); + + void* insert(index_type idx, void* value); + + std::size_t size() const + { return m_size; } + + std::size_t capacity() const + { return m_capacity; } + + void* at(index_type idx) const; + + void* remove(index_type idx); + + void reserve(std::size_t size); + + index_type cursor() const + { return m_first; } + + index_type span() const + { return (m_last - m_first) & 0xffff; } + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + private: + void** m_storage; + std::size_t m_capacity; + + // this is the total number of elements that are occupied + // in the array + std::size_t m_size; + + // This defines the first index that is part of the m_storage. + // last is one passed the last used slot + index_type m_first; + index_type m_last; + }; +} + +#endif // TORRENT_PACKET_BUFFER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp new file mode 100644 index 0000000000..f4a6ac8b72 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/parse_url.hpp @@ -0,0 +1,61 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PARSE_URL_HPP_INCLUDED +#define TORRENT_PARSE_URL_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + + // returns protocol, auth, hostname, port, path + TORRENT_EXTRA_EXPORT boost::tuple + parse_url_components(std::string url, error_code& ec); + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp new file mode 100644 index 0000000000..fbd2b2a28f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/pe_crypto.hpp @@ -0,0 +1,205 @@ +/* + +Copyright (c) 2007-2014, Un Shyam & Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_PE_CRYPTO_HPP_INCLUDED +#define TORRENT_PE_CRYPTO_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +#ifdef TORRENT_USE_GCRYPT +#include +#elif defined TORRENT_USE_OPENSSL +#include +#else +// RC4 state from libtomcrypt +struct rc4 { + int x, y; + unsigned char buf[256]; +}; + +void TORRENT_EXTRA_EXPORT rc4_init(const unsigned char* in, unsigned long len, rc4 *state); +unsigned long TORRENT_EXTRA_EXPORT rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state); +#endif + +#include "libtorrent/peer_id.hpp" // For sha1_hash +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + class TORRENT_EXTRA_EXPORT dh_key_exchange + { + public: + dh_key_exchange(); + bool good() const { return true; } + + // Get local public key, always 96 bytes + char const* get_local_key() const; + + // read remote_pubkey, generate and store shared secret in + // m_dh_shared_secret. + int compute_secret(const char* remote_pubkey); + + char const* get_secret() const { return m_dh_shared_secret; } + + sha1_hash const& get_hash_xor_mask() const { return m_xor_mask; } + + private: + + int get_local_key_size() const + { return sizeof(m_dh_local_key); } + + char m_dh_local_key[96]; + char m_dh_local_secret[96]; + char m_dh_shared_secret[96]; + sha1_hash m_xor_mask; + }; + + struct encryption_handler + { + virtual void set_incoming_key(unsigned char const* key, int len) = 0; + virtual void set_outgoing_key(unsigned char const* key, int len) = 0; + virtual void encrypt(char* pos, int len) = 0; + virtual void decrypt(char* pos, int len) = 0; + virtual ~encryption_handler() {} + }; + + struct rc4_handler : encryption_handler + { + public: + // Input longkeys must be 20 bytes + rc4_handler() + : m_encrypt(false) + , m_decrypt(false) + { +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_open(&m_rc4_incoming, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_open(&m_rc4_outgoing, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); +#endif + }; + + void set_incoming_key(unsigned char const* key, int len) + { + m_decrypt = true; +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_incoming); + gcry_cipher_open(&m_rc4_incoming, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_setkey(m_rc4_incoming, key, len); +#elif defined TORRENT_USE_OPENSSL + RC4_set_key(&m_remote_key, len, key); +#else + rc4_init(key, len, &m_rc4_incoming); +#endif + // Discard first 1024 bytes + char buf[1024]; + decrypt(buf, 1024); + } + + void set_outgoing_key(unsigned char const* key, int len) + { + m_encrypt = true; +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_outgoing); + gcry_cipher_open(&m_rc4_outgoing, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0); + gcry_cipher_setkey(m_rc4_outgoing, key, len); +#elif defined TORRENT_USE_OPENSSL + RC4_set_key(&m_local_key, len, key); +#else + rc4_init(key, len, &m_rc4_outgoing); +#endif + // Discard first 1024 bytes + char buf[1024]; + encrypt(buf, 1024); + } + + ~rc4_handler() + { +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_close(m_rc4_incoming); + gcry_cipher_close(m_rc4_outgoing); +#endif + }; + + void encrypt(char* pos, int len) + { + if (!m_encrypt) return; + + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(pos); + +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_encrypt(m_rc4_outgoing, pos, len, 0, 0); +#elif defined TORRENT_USE_OPENSSL + RC4(&m_local_key, len, (const unsigned char*)pos, (unsigned char*)pos); +#else + rc4_encrypt((unsigned char*)pos, len, &m_rc4_outgoing); +#endif + } + + void decrypt(char* pos, int len) + { + if (!m_decrypt) return; + + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(pos); + +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_decrypt(m_rc4_incoming, pos, len, 0, 0); +#elif defined TORRENT_USE_OPENSSL + RC4(&m_remote_key, len, (const unsigned char*)pos, (unsigned char*)pos); +#else + rc4_encrypt((unsigned char*)pos, len, &m_rc4_incoming); +#endif + } + + private: +#ifdef TORRENT_USE_GCRYPT + gcry_cipher_hd_t m_rc4_incoming; + gcry_cipher_hd_t m_rc4_outgoing; +#elif defined TORRENT_USE_OPENSSL + RC4_KEY m_local_key; // Key to encrypt outgoing data + RC4_KEY m_remote_key; // Key to decrypt incoming data +#else + rc4 m_rc4_incoming; + rc4 m_rc4_outgoing; +#endif + // determines whether or not encryption and decryption is enabled + bool m_encrypt; + bool m_decrypt; + }; + +} // namespace libtorrent + +#endif // TORRENT_PE_CRYPTO_HPP_INCLUDED +#endif // TORRENT_DISABLE_ENCRYPTION + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp new file mode 100644 index 0000000000..33baf52d34 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_HPP_INCLUDED +#define TORRENT_PEER_HPP_INCLUDED + +#include + +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXTRA_EXPORT peer_entry + { + std::string ip; + int port; + peer_id pid; + + bool operator==(const peer_entry& p) const + { + return pid == p.pid; + } + + bool operator<(const peer_entry& p) const + { + return pid < p.pid; + } + }; + +} + +#endif // TORRENT_PEER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp new file mode 100644 index 0000000000..2321b48782 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_connection.hpp @@ -0,0 +1,1316 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING +#include "libtorrent/debug.hpp" +#endif + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type_fwd.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/chained_buffer.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/bandwidth_socket.hpp" +#include "libtorrent/socket_type_fwd.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/sliding_average.hpp" +#include "libtorrent/io_service_fwd.hpp" + +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent +{ + class torrent; + struct peer_info; + struct disk_io_job; +#ifndef TORRENT_DISABLE_EXTENSIONS + struct peer_plugin; +#endif + + namespace detail + { + struct session_impl; + } + + struct pending_block + { + pending_block(piece_block const& b) + : block(b), skipped(0), not_wanted(false) + , timed_out(false), busy(false) {} + + piece_block block; + + // the number of times the request + // has been skipped by out of order blocks + boost::uint16_t skipped:13; + + // if any of these are set to true, this block + // is not allocated + // in the piece picker anymore, and open for + // other peers to pick. This may be caused by + // it either timing out or being received + // unexpectedly from the peer + bool not_wanted:1; + bool timed_out:1; + + // the busy flag is set if the block was + // requested from another peer when this + // request was queued. We only allow a single + // busy request at a time in each peer's queue + bool busy:1; + + bool operator==(pending_block const& b) + { + return b.skipped == skipped && b.block == block + && b.not_wanted == not_wanted && b.timed_out == timed_out; + } + }; + + struct has_block + { + has_block(piece_block const& b): block(b) {} + piece_block const& block; + bool operator()(pending_block const& pb) const + { return pb.block == block; } + }; + + class TORRENT_EXTRA_EXPORT peer_connection + : public bandwidth_socket + , public boost::noncopyable + { + friend class invariant_access; + public: + + enum connection_type + { + bittorrent_connection = 0, + url_seed_connection = 1, + http_seed_connection = 2 + }; + + virtual int type() const = 0; + + enum channels + { + upload_channel, + download_channel, + num_channels + }; + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , bool outgoing = true); + + // this function is called after it has been constructed and properly + // reference counted. It is safe to call self() in this function + // and schedule events with references to itself (that is not safe to + // do in the constructor). + virtual void start(); + + virtual ~peer_connection(); + + void set_peer_info(policy::peer* pi) + { + TORRENT_ASSERT(m_peer_info == 0 || pi == 0 ); + m_peer_info = pi; + } + + // this is called when the peer object is created, in case + // it was let in by the connections limit slack. This means + // the peer needs to, as soon as the handshake is done, either + // disconnect itself or another peer. + void peer_exceeds_limit() + { m_exceeded_limit = true; } + + // this is called if this peer causes another peer + // to be disconnected, in which case it has fulfilled + // its requirement. + void peer_disconnected_other() + { m_exceeded_limit = false; } + + policy::peer* peer_info_struct() const + { return m_peer_info; } + + enum peer_speed_t { slow = 1, medium, fast }; + peer_speed_t peer_speed(); + + void send_allowed_set(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); + peer_plugin const* find_plugin(char const* type); +#endif + + // this function is called once the torrent associated + // with this peer connection has retrieved the meta- + // data. If the torrent was spawned with metadata + // this is called from the constructor. + void init(); + + // this is called when the metadata is retrieved + // and the files has been checked + virtual void on_metadata() {}; + + void on_metadata_impl(); + + int get_upload_limit() const; + int get_download_limit() const; + void set_upload_limit(int limit); + void set_download_limit(int limit); + + int upload_limit() const { return m_upload_limit; } + int download_limit() const { return m_download_limit; } + + int prefer_whole_pieces() const + { + if (on_parole()) return 1; + return m_prefer_whole_pieces; + } + + bool on_parole() const + { return peer_info_struct() && peer_info_struct()->on_parole; } + + int picker_options() const; + + void prefer_whole_pieces(int num) + { m_prefer_whole_pieces = num; } + + bool request_large_blocks() const + { return m_request_large_blocks; } + + void request_large_blocks(bool b) + { m_request_large_blocks = b; } + + void set_endgame(bool b) { m_endgame_mode = b; } + bool endgame() const { return m_endgame_mode; } + + bool no_download() const { return m_no_download; } + void no_download(bool b) { m_no_download = b; } + + bool ignore_stats() const { return m_ignore_stats; } + void ignore_stats(bool b) { m_ignore_stats = b; } + + void set_priority(int p) + { + TORRENT_ASSERT(p > 0); + TORRENT_ASSERT(m_priority <= 255); + if (p > 255) p = 255; + m_priority = p; + } + + boost::uint32_t peer_rank() const; + + void fast_reconnect(bool r); + bool fast_reconnect() const { return m_fast_reconnect; } + + // this adds an announcement in the announcement queue + // it will let the peer know that we have the given piece + void announce_piece(int index); + + // this will tell the peer to announce the given piece + // and only allow it to request that piece + void superseed_piece(int replace_piece, int new_piece); + bool super_seeded_piece(int index) const + { + return m_superseed_piece[0] == index + || m_superseed_piece[1] == index; + } + + // tells if this connection has data it want to send + // and has enough upload bandwidth quota left to send it. + bool can_write() const; + bool can_read(boost::uint8_t* state = 0) const; + + bool is_seed() const; + int num_have_pieces() const { return m_num_pieces; } + + void set_share_mode(bool m); + bool share_mode() const { return m_share_mode; } + + void set_upload_only(bool u); + bool upload_only() const { return m_upload_only; } + + void set_holepunch_mode() + { + m_holepunch_mode = true; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** HOLEPUNCH MODE ***"); +#endif + } + + // will send a keep-alive message to the peer + void keep_alive(); + + peer_id const& pid() const { return m_peer_id; } + void set_pid(const peer_id& peer_id) { m_peer_id = peer_id; } + bool has_piece(int i) const; + + std::vector const& download_queue() const; + std::vector const& request_queue() const; + std::vector const& upload_queue() const; + + void clear_request_queue(); + + // estimate of how long it will take until we have + // received all piece requests that we have sent + // if extra_bytes is specified, it will include those + // bytes as if they've been requested + time_duration download_queue_time(int extra_bytes = 0) const; + + bool is_interesting() const { return m_interesting; } + bool is_choked() const { return m_choked; } + + bool is_peer_interested() const { return m_peer_interested; } + bool has_peer_choked() const { return m_peer_choked; } + + void update_interest(); + + virtual void get_peer_info(peer_info& p) const; + + // returns the torrent this connection is a part of + // may be zero if the connection is an incoming connection + // and it hasn't received enough information to determine + // which torrent it should be associated with + boost::weak_ptr associated_torrent() const + { return m_torrent; } + + const stat& statistics() const { return m_statistics; } + void add_stat(size_type downloaded, size_type uploaded); + + // is called once every second by the main loop + void second_tick(int tick_interval_ms); + + void timeout_requests(); + + boost::shared_ptr get_socket() const { return m_socket; } + tcp::endpoint const& remote() const { return m_remote; } + + bitfield const& get_bitfield() const; + std::vector const& allowed_fast(); + std::vector const& suggested_pieces() const { return m_suggested_pieces; } + + ptime connected_time() const { return m_connect; } + ptime last_received() const { return m_last_receive; } + + void on_timeout(); + // this will cause this peer_connection to be disconnected. + virtual void disconnect(error_code const& ec, int error = 0); + // called when a connect attempt fails (not when an + // established connection fails) + void connect_failed(error_code const& e); + bool is_disconnecting() const { return m_disconnecting; } + + // this is called when the connection attempt has succeeded + // and the peer_connection is supposed to set m_connecting + // to false, and stop monitor writability + void on_connection_complete(error_code const& e); + + // returns true if this connection is still waiting to + // finish the connection attempt + bool is_connecting() const { return m_connecting; } + + // returns true if the socket of this peer hasn't been + // attempted to connect yet (i.e. it's queued for + // connection attempt). + bool is_queued() const { return m_queued; } + + // called when it's time for this peer_conncetion to actually + // initiate the tcp connection. This may be postponed until + // the library isn't using up the limitation of half-open + // tcp connections. + void on_connect(int ticket); + + // This is called for every peer right after the upload + // bandwidth has been distributed among them + // It will reset the used bandwidth to 0. + void reset_upload_quota(); + + // trust management. + virtual void received_valid_data(int index); + // returns false if the peer should not be + // disconnected + virtual bool received_invalid_data(int index, bool single_peer); + + // a connection is local if it was initiated by us. + // if it was an incoming connection, it is remote + bool is_outgoing() const { return m_outgoing; } + + bool received_listen_port() const { return m_received_listen_port; } + void received_listen_port() + { m_received_listen_port = true; } + + bool on_local_network() const; + bool ignore_bandwidth_limits() const + { return m_ignore_bandwidth_limits; } + void ignore_bandwidth_limits(bool i) + { m_ignore_bandwidth_limits = i; } + + bool ignore_unchoke_slots() const; + void ignore_unchoke_slots(bool i) + { m_ignore_unchoke_slots = i; } + + bool failed() const { return m_failed; } + + int desired_queue_size() const + { + // this peer is in end-game mode we only want + // one outstanding request + return (m_endgame_mode || m_snubbed) ? 1 : m_desired_queue_size; + } + + bool bittyrant_unchoke_compare( + boost::intrusive_ptr const& p) const; + // compares this connection against the given connection + // for which one is more eligible for an unchoke. + // returns true if this is more eligible + bool unchoke_compare(boost::intrusive_ptr const& p) const; + bool upload_rate_compare(peer_connection const* p) const; + + // resets the byte counters that are used to measure + // the number of bytes transferred within unchoke cycles + void reset_choke_counters(); + + // if this peer connection is useless (neither party is + // interested in the other), disconnect it + void disconnect_if_redundant(); + + void increase_est_reciprocation_rate(); + void decrease_est_reciprocation_rate(); + int est_reciprocation_rate() const { return m_est_reciprocation_rate; } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + void peer_log(char const* fmt, ...) const; + boost::shared_ptr m_logger; +#endif + + // the message handlers are called + // each time a recv() returns some new + // data, the last time it will be called + // is when the entire packet has been + // received, then it will no longer + // be called. i.e. most handlers need + // to check how much of the packet they + // have received before any processing + void incoming_keepalive(); + void incoming_choke(); + void incoming_unchoke(); + void incoming_interested(); + void incoming_not_interested(); + void incoming_have(int piece_index); + void incoming_dont_have(int piece_index); + void incoming_bitfield(bitfield const& bits); + void incoming_request(peer_request const& r); + void incoming_piece(peer_request const& p, disk_buffer_holder& data); + void incoming_piece(peer_request const& p, char const* data); + void incoming_piece_fragment(int bytes); + void start_receive_piece(peer_request const& r); + void incoming_cancel(peer_request const& r); + + bool can_disconnect(error_code const& ec) const; + void incoming_dht_port(int listen_port); + + void incoming_reject_request(peer_request const& r); + void incoming_have_all(); + void incoming_have_none(); + void incoming_allowed_fast(int index); + void incoming_suggest(int index); + + void set_has_metadata(bool m) { m_has_metadata = m; } + bool has_metadata() const { return m_has_metadata; } + + // the following functions appends messages + // to the send buffer + bool send_choke(); + bool send_unchoke(); + void send_interested(); + void send_not_interested(); + void send_suggest(int piece); + + void snub_peer(); + + bool can_request_time_critical() const; + + // returns true if the specified block was actually made time-critical. + // if the block was already time-critical, it returns false. + bool make_time_critical(piece_block const& block); + + // adds a block to the request queue + // returns true if successful, false otherwise + enum flags_t { req_time_critical = 1, req_busy = 2 }; + bool add_request(piece_block const& b, int flags = 0); + + // clears the request queue and sends cancels for all messages + // in the download queue + void cancel_all_requests(); + + // removes a block from the request queue or download queue + // sends a cancel message if appropriate + // refills the request queue, and possibly ignoring pieces requested + // by peers in the ignore list (to avoid recursion) + // if force is true, the blocks is also freed from the piece + // picker, allowing another peer to request it immediately + void cancel_request(piece_block const& b, bool force = false); + void send_block_requests(); + + int bandwidth_throttle(int channel) const + { return m_bandwidth_channel[channel].throttle(); } + + void assign_bandwidth(int channel, int amount); + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + // is true until we can be sure that the other end + // speaks our protocol (be it bittorrent or http). + virtual bool in_handshake() const = 0; + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + virtual boost::optional + downloading_piece_progress() const + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** downloading_piece_progress() dispatched to the base class!"); +#endif + return boost::optional(); + } + + // these functions are virtual to let bt_peer_connection hook into them + // and encrypt the content + enum message_type_flags { message_type_request = 1 }; + virtual void send_buffer(char const* begin, int size, int flags = 0 + , void (*fun)(char*, int, void*) = 0, void* userdata = 0); + virtual void setup_send(); + + void cork_socket() { TORRENT_ASSERT(!m_corked); m_corked = true; } + void uncork_socket(); + +#ifdef TORRENT_DISK_STATS + void log_buffer_usage(char* buffer, int size, char const* label); +#endif + + template + void append_send_buffer(char* buffer, int size, Destructor const& destructor + , bool encrypted) + { +#if defined TORRENT_DISK_STATS + log_buffer_usage(buffer, size, "queued send buffer"); +#endif + // bittorrent connections should never use this function, since + // they might be encrypted and this would circumvent the actual + // encryption. bt_peer_connection overrides this function with + // its own version. + TORRENT_ASSERT(encrypted || type() != bittorrent_connection); + m_send_buffer.append_buffer(buffer, size, size, destructor); + } + + virtual void append_const_send_buffer(char const* buffer, int size); + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void set_country(char const* c) + { + TORRENT_ASSERT(strlen(c) == 2); + m_country[0] = c[0]; + m_country[1] = c[1]; + } + bool has_country() const { return m_country[0] != 0; } +#endif + + int outstanding_bytes() const { return m_outstanding_bytes; } + + int send_buffer_size() const + { return m_send_buffer.size(); } + + int send_buffer_capacity() const + { return m_send_buffer.capacity(); } + + int packet_size() const { return m_packet_size; } + + bool packet_finished() const + { return m_packet_size <= m_recv_pos; } + + int receive_pos() const { return m_recv_pos; } + + void max_out_request_queue(int s); + int max_out_request_queue() const; + +#ifdef TORRENT_DEBUG + bool piece_failed; +#endif + + time_t last_seen_complete() const { return m_last_seen_complete; } + void set_last_seen_complete(int ago) { m_last_seen_complete = time(0) - ago; } + + size_type uploaded_in_last_round() const + { return m_statistics.total_payload_upload() - m_uploaded_at_last_round; } + + size_type downloaded_in_last_round() const + { return m_statistics.total_payload_download() - m_downloaded_at_last_round; } + + size_type uploaded_since_unchoked() const + { return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; } + + // called when the disk write buffer is drained again, and we can + // start downloading payload again + void on_disk(); + + int num_reading_bytes() const { return m_reading_bytes; } + + enum sync_t { read_async, read_sync }; + void setup_receive(sync_t sync = read_sync); + + protected: + + size_t try_read(sync_t s, error_code& ec); + + virtual void get_specific_peer_info(peer_info& p) const = 0; + + virtual void write_choke() = 0; + virtual void write_unchoke() = 0; + virtual void write_interested() = 0; + virtual void write_not_interested() = 0; + virtual void write_request(peer_request const& r) = 0; + virtual void write_cancel(peer_request const& r) = 0; + virtual void write_have(int index) = 0; + virtual void write_keepalive() = 0; + virtual void write_piece(peer_request const& r, disk_buffer_holder& buffer) = 0; + virtual void write_suggest(int piece) = 0; + + virtual void write_reject_request(peer_request const& r) = 0; + virtual void write_allow_fast(int piece) = 0; + + virtual void on_connected() = 0; + virtual void on_tick() {} + + virtual void on_receive(error_code const& error + , std::size_t bytes_transferred) = 0; + virtual void on_sent(error_code const& error + , std::size_t bytes_transferred) = 0; + +#ifndef TORRENT_DISABLE_ENCRYPTION + buffer::interval wr_recv_buffer() + { + if (m_recv_buffer.empty()) + { + TORRENT_ASSERT(m_recv_pos == 0); + return buffer::interval(0,0); + } + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(m_disk_recv_buffer_size == 0); + int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); + return buffer::interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + rcv_pos); + } + + std::pair wr_recv_buffers(int bytes); +#endif + + buffer::const_interval receive_buffer() const + { + if (m_recv_buffer.empty()) + { + TORRENT_ASSERT(m_recv_pos == 0); + return buffer::interval(0,0); + } + int rcv_pos = (std::min)(m_recv_pos, int(m_recv_buffer.size())); + return buffer::const_interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + rcv_pos); + } + + bool allocate_disk_receive_buffer(int disk_buffer_size); + char* release_disk_receive_buffer(); + bool has_disk_receive_buffer() const { return m_disk_recv_buffer; } + void cut_receive_buffer(int size, int packet_size, int offset = 0); + void reset_recv_buffer(int packet_size); + void set_soft_packet_size(int size) { m_soft_packet_size = size; } + + // if allow_encrypted is false, and the torrent 'ih' turns out + // to be an encrypted torrent (AES-256 encrypted) the peer will + // be disconnected. This is to prevent non-encrypted peers to + // attach to an encrypted torrent + void attach_to_torrent(sha1_hash const& ih, bool allow_encrypted); + + bool verify_piece(peer_request const& p) const; + + void update_desired_queue_size(); + + void set_timeout(int s) { m_timeout = s; } + + boost::intrusive_ptr self() + { + TORRENT_ASSERT(!m_in_constructor); + return boost::intrusive_ptr(this); + } + + // TODO: make this private + public: + + // upload and download channel state + // enum from peer_info::bw_state + boost::uint8_t m_channel_state[2]; + + private: + + // is true if we learn the incoming connections listening + // during the extended handshake + bool m_received_listen_port:1; + + // this is set to true when a have_all + // message is received. This information + // is used to fill the bitmask in init() + bool m_have_all:1; + + // other side says that it's interested in downloading + // from us. + bool m_peer_interested:1; + + // the other side has told us that it won't send anymore + // data to us for a while + bool m_peer_choked:1; + + // the peer has pieces we are interested in + bool m_interesting:1; + + // we have choked the upload to the peer + bool m_choked:1; + + // this is set to true if the connection timed + // out or closed the connection. In that + // case we will not try to reconnect to + // this peer + bool m_failed:1; + + // this is true if this connection has been added + // to the list of connections that will be closed. + bool m_disconnecting:1; + + // this is set to true once the bitfield is received + bool m_bitfield_received:1; + + // this is set to true if the last time we tried to + // pick a piece to download, we could only find + // blocks that were already requested from other + // peers. In this case, we should not try to pick + // another piece until the last one we requested is done + bool m_endgame_mode:1; + + // set to true when we've sent the first round of suggests + bool m_sent_suggests:1; + + // set to true while we're trying to holepunch + bool m_holepunch_mode:1; + + // when this is set, the transfer stats for this connection + // is not included in the torrent or session stats + bool m_ignore_stats:1; + + // when this is set, the peer_connection socket is + // corked, similar to the linux TCP feature TCP_CORK. + // we won't send anything to the actual socket, just + // buffer messages up in the application layer send + // buffer, and send it once we're uncorked. + bool m_corked:1; + + // set to true if this peer has metadata, and false + // otherwise. + bool m_has_metadata:1; + + // this is set to true if this peer was accepted exceeding + // the connection limit. It means it has to disconnect + // itself, or some other peer, as soon as it's completed + // the handshake. We need to wait for the handshake in + // order to know which torrent it belongs to, to know which + // other peers to compare it to. + bool m_exceeded_limit:1; + + // TODO: make these private as well + protected: + + // number of bytes this peer can send and receive + int m_quota[2]; + + // statistics about upload and download speeds + // and total amount of uploads and downloads for + // this peer + stat m_statistics; + + // a back reference to the session + // the peer belongs to. + aux::session_impl& m_ses; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + + // called from the main loop when this connection has any + // work to do. + void on_send_data(error_code const& error + , std::size_t bytes_transferred); + void on_receive_data(error_code const& error + , std::size_t bytes_transferred); + + // the average rate of receiving complete piece messages + sliding_average<20> m_piece_rate; + sliding_average<20> m_send_rate; + + private: + + std::pair preferred_caching() const; + void fill_send_buffer(); + void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request r, boost::shared_ptr t); + int request_upload_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 = 0 + , bandwidth_channel* bwc3 = 0 + , bandwidth_channel* bwc4 = 0); + int request_download_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 = 0 + , bandwidth_channel* bwc3 = 0 + , bandwidth_channel* bwc4 = 0); + + // keep the io_service running as long as we + // have peer connections + io_service::work m_work; + + // the time when we last got a part of a + // piece packet from this peer + ptime m_last_piece; + + // the time we sent a request to + // this peer the last time + ptime m_last_request; + // the time we received the last + // piece request from the peer + ptime m_last_incoming_request; + // the time when we unchoked this peer + ptime m_last_unchoke; + + // if we're unchoked by this peer, this + // was the time + ptime m_last_unchoked; + + // the time we last choked this peer. min_time() in + // case we never unchoked it + ptime m_last_choke; + + // timeouts + ptime m_last_receive; + ptime m_last_sent; + + // the time when the first entry in the + // request queue was requested, increased + // for each entry that is popped from the + // download queue. Used for request timeout + ptime m_requested; + + // a timestamp when the remote download rate + // was last updated + ptime m_remote_dl_update; + + // the time when async_connect was called + // or when the incoming connection was established + ptime m_connect; + + // the time when this peer sent us a not_interested message + // the last time. + ptime m_became_uninterested; + + // the time when we sent a not_interested message to + // this peer the last time. + ptime m_became_uninteresting; + + // the total payload download bytes + // at the last unchoke round. This is used to + // measure the number of bytes transferred during + // an unchoke cycle, to unchoke peers the more bytes + // they sent us + size_type m_downloaded_at_last_round; + size_type m_uploaded_at_last_round; + + // this is the number of bytes we had uploaded the + // last time this peer was unchoked. This does not + // reset each unchoke interval/round. This is used to + // track upload across rounds, for the full duration of + // the peer being unchoked. Specifically, it's used + // for the round-robin unchoke algorithm. + size_type m_uploaded_at_last_unchoke; + + template + struct handler_storage + { +#ifdef TORRENT_DEBUG + handler_storage() + : used(false) + {} + + bool used; +#else + handler_storage() {} +#endif + boost::aligned_storage bytes; + }; + + handler_storage m_read_handler_storage; + handler_storage m_write_handler_storage; + +#ifndef TORRENT_DISABLE_GEO_IP + std::string m_inet_as_name; +#endif + + buffer m_recv_buffer; + + // if this peer is receiving a piece, this + // points to a disk buffer that the data is + // read into. This eliminates a memcopy from + // the receive buffer into the disk buffer + disk_buffer_holder m_disk_recv_buffer; + + chained_buffer m_send_buffer; + + boost::shared_ptr m_socket; + + // this is the torrent this connection is + // associated with. If the connection is an + // incoming connection, this is set to zero + // until the info_hash is received. Then it's + // set to the torrent it belongs to. + boost::weak_ptr m_torrent; + + // the pieces the other end have + bitfield m_have_piece; + + // the queue of requests we have got + // from this peer that haven't been issued + // to the disk thread yet + std::vector m_requests; + + // the blocks we have reserved in the piece + // picker and will request from this peer. + std::vector m_request_queue; + + // the queue of blocks we have requested + // from this peer + std::vector m_download_queue; + + // the pieces we will send to the peer + // if requested (regardless of choke state) + std::vector m_accept_fast; + + // a sent-piece counter for the allowed fast set + // to avoid exploitation. Each slot is a counter + // for one of the pieces from the allowed-fast set + std::vector m_accept_fast_piece_cnt; + + // the pieces the peer will send us if + // requested (regardless of choke state) + std::vector m_allowed_fast; + + // pieces that has been suggested to be + // downloaded from this peer + std::vector m_suggested_pieces; + + // a list of byte offsets inside the send buffer + // the piece requests + std::vector m_requests_in_buffer; + + // this peer's peer info struct. This may + // be 0, in case the connection is incoming + // and hasn't been added to a torrent yet. + policy::peer* m_peer_info; + + // the time when this peer last saw a complete copy + // of this torrent + time_t m_last_seen_complete; + + // the block we're currently receiving. Or + // (-1, -1) if we're not receiving one + piece_block m_receiving_block; + + // this is the peer we're actually talking to + // it may not necessarily be the peer we're + // connected to, in case we use a proxy + tcp::endpoint m_remote; + + // the bandwidth channels, upload and download + // keeps track of the current quotas + bandwidth_channel m_bandwidth_channel[num_channels]; + + // remote peer's id + peer_id m_peer_id; + + // if the timeout is extended for the outstanding + // requests, this is the number of seconds it was + // extended. + int m_timeout_extend; + + // the number of bytes that the other + // end has to send us in order to respond + // to all outstanding piece requests we + // have sent to it + int m_outstanding_bytes; + + // the number of outstanding bytes expected + // to be received by extensions + int m_extension_outstanding_bytes; + + // the number of time critical requests + // queued up in the m_request_queue that + // soon will be committed to the download + // queue. This is included in download_queue_time() + // so that it can be used while adding more + // requests and take the previous requests + // into account without submitting it all + // immediately + int m_queued_time_critical; + + // the number of pieces this peer + // has. Must be the same as + // std::count(m_have_piece.begin(), + // m_have_piece.end(), true) + int m_num_pieces; + + // the timeout in seconds + int m_timeout; + + // the size (in bytes) of the bittorrent message + // we're currently receiving + int m_packet_size; + + // some messages needs to be read from the socket + // buffer in multiple stages. This soft packet + // size limits the read size between message handler + // dispatch. Ignored when set to 0 + int m_soft_packet_size; + + // the number of bytes of the bittorrent payload + // we've received so far + int m_recv_pos; + + int m_disk_recv_buffer_size; + + // the number of bytes we are currently reading + // from disk, that will be added to the send + // buffer as soon as they complete + int m_reading_bytes; + + // the number of invalid piece-requests + // we have got from this peer. If the request + // queue gets empty, and there have been + // invalid requests, we can assume the + // peer is waiting for those pieces. + // we can then clear its download queue + // by sending choke, unchoke. + int m_num_invalid_requests; + + // this is the priority with which this peer gets + // download bandwidth quota assigned to it. + int m_priority; + + int m_upload_limit; + int m_download_limit; + + // this is a measurement of how fast the peer + // it allows some variance without changing + // back and forth between states + peer_speed_t m_speed; + + // the ticket id from the connection queue. + // This is used to identify the connection + // so that it can be removed from the queue + // once the connection completes + int m_connection_ticket; + + // if [0] is -1, superseeding is not active. If it is >= 0 + // this is the piece that is available to this peer. Only + // these two pieces can be downloaded from us by this peer. + // This will remain the current piece for this peer until + // another peer sends us a have message for this piece + int m_superseed_piece[2]; + + // pieces downloaded since last second + // timer timeout; used for determining + // approx download rate + int m_remote_pieces_dled; + + // approximate peer download rate + int m_remote_dl_rate; + + // the number of bytes send to the disk-io + // thread that hasn't yet been completely written. + int m_outstanding_writing_bytes; + + // max transfer rates seen on this peer + int m_download_rate_peak; + int m_upload_rate_peak; + + // this is the limit on the number of outstanding requests + // we have to this peer. This is initialized to the settings + // in the session_settings structure. But it may be lowered + // if the peer is known to require a smaller limit (like BitComet). + // or if the extended handshake sets a limit. + // web seeds also has a limit on the queue size. + int m_max_out_request_queue; + + // when using the BitTyrant choker, this is our + // estimated reciprocation rate. i.e. the rate + // we need to send to this peer for it to unchoke + // us + int m_est_reciprocation_rate; + + // estimated round trip time to this peer + // based on the time from when async_connect + // was called to when on_connection_complete + // was called. The rtt is specified in milliseconds + boost::uint16_t m_rtt; + + // the number of request we should queue up + // at the remote end. + boost::uint16_t m_desired_queue_size; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + // in case the session settings is set + // to resolve countries, this is set to + // the two character country code this + // peer resides in. + char m_country[2]; +#endif + + // if set to non-zero, this peer will always prefer + // to request entire n pieces, rather than blocks. + // where n is the value of this variable. + // if it is 0, the download rate limit setting + // will be used to determine if whole pieces + // are preferred. + boost::uint8_t m_prefer_whole_pieces; + + // if this is true, the disconnection + // timestamp is not updated when the connection + // is closed. This means the time until we can + // reconnect to this peer is shorter, and likely + // immediate. + bool m_fast_reconnect:1; + + // is true if it was we that connected to the peer + // and false if we got an incoming connection + // could be considered: true = local, false = remote + bool m_outgoing:1; + + // if this is set to true, the peer will not + // request bandwidth from the limiter, but instead + // just send and receive as much as possible. + bool m_ignore_bandwidth_limits:1; + + // set to true if this peer controls its unchoke + // state individually, regardless of the global + // unchoker + bool m_ignore_unchoke_slots:1; + + // this is true until this socket has become + // writable for the first time (i.e. the + // connection completed). While connecting + // the timeout will not be triggered. This is + // because windows XP SP2 may delay connection + // attempts, which means that the connection + // may not even have been attempted when the + // time out is reached. + bool m_connecting:1; + + // This is true until connect is called on the + // peer_connection's socket. It is false on incoming + // connections. + bool m_queued:1; + + // if this is true, the blocks picked by the piece + // picker will be merged before passed to the + // request function. i.e. subsequent blocks are + // merged into larger blocks. This is used by + // the http-downloader, to request whole pieces + // at a time. + bool m_request_large_blocks:1; + + // set to true if this peer is in share mode + bool m_share_mode:1; + + // set to true when this peer is only uploading + bool m_upload_only:1; + + // set to true when a piece request times out. The + // result is that the desired pending queue size + // is set to 1 + bool m_snubbed:1; + + // if this is set to true, the client will not + // pick any pieces from this peer + bool m_no_download:1; + + template + struct allocating_handler + { + allocating_handler( + Handler const& h, handler_storage& s + ) + : handler(h) + , storage(s) + {} + + template + void operator()(A0 const& a0) const + { + handler(a0); + } + + template + void operator()(A0 const& a0, A1 const& a1) const + { + handler(a0, a1); + } + + template + void operator()(A0 const& a0, A1 const& a1, A2 const& a2) const + { + handler(a0, a1, a2); + } + + friend void* asio_handler_allocate( + std::size_t size, allocating_handler* ctx) + { + TORRENT_ASSERT(size <= Size); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(!ctx->storage.used); + ctx->storage.used = true; +#endif + return &ctx->storage.bytes; + } + + friend void asio_handler_deallocate( + void*, std::size_t, allocating_handler* ctx) + { +#ifdef TORRENT_DEBUG + ctx->storage.used = false; +#endif + } + + Handler handler; + handler_storage& storage; + }; + + template + allocating_handler + make_read_handler(Handler const& handler) + { + return allocating_handler( + handler, m_read_handler_storage + ); + } + + template + allocating_handler + make_write_handler(Handler const& handler) + { + return allocating_handler( + handler, m_write_handler_storage + ); + } + +#if TORRENT_USE_ASSERTS + public: + bool m_in_constructor:1; + bool m_disconnect_started:1; + bool m_initialized:1; + int m_in_use; + int m_received_in_piece; +#endif + }; + + struct cork + { + cork(peer_connection& p): m_pc(p) { m_pc.cork_socket(); } + ~cork() { m_pc.uncork_socket(); } + peer_connection& m_pc; + }; + +} + +#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp new file mode 100644 index 0000000000..089f43438c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_id.hpp @@ -0,0 +1,48 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_ID_HPP_INCLUDED +#define TORRENT_PEER_ID_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/sha1_hash.hpp" + +namespace libtorrent +{ + typedef sha1_hash peer_id; +#ifndef TORRENT_NO_DEPRECATE + typedef sha1_hash big_number; +#endif +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp new file mode 100644 index 0000000000..508c600dc5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_info.hpp @@ -0,0 +1,444 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_INFO_HPP_INCLUDED +#define TORRENT_PEER_INFO_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/bitfield.hpp" + +namespace libtorrent +{ + // holds information and statistics about one peer + // that libtorrent is connected to + struct TORRENT_EXPORT peer_info + { + // flags for the peer_info::flags field. Indicates various states + // the peer may be in. These flags are not mutually exclusive, but + // not every combination of them makes sense either. + enum peer_flags_t + { + // **we** are interested in pieces from this peer. + interesting = 0x1, + + // **we** have choked this peer. + choked = 0x2, + + // the peer is interested in **us** + remote_interested = 0x4, + + // the peer has choked **us**. + remote_choked = 0x8, + + // means that this peer supports the + // `extension protocol`__. + // + // __ extension_protocol.html + supports_extensions = 0x10, + + // The connection was initiated by us, the peer has a + // listen port open, and that port is the same as in the + // address of this peer. If this flag is not set, this + // peer connection was opened by this peer connecting to + // us. + local_connection = 0x20, + + // The connection is opened, and waiting for the + // handshake. Until the handshake is done, the peer + // cannot be identified. + handshake = 0x40, + + // The connection is in a half-open state (i.e. it is + // being connected). + connecting = 0x80, + + // The connection is currently queued for a connection + // attempt. This may happen if there is a limit set on + // the number of half-open TCP connections. + queued = 0x100, + + // The peer has participated in a piece that failed the + // hash check, and is now "on parole", which means we're + // only requesting whole pieces from this peer until + // it either fails that piece or proves that it doesn't + // send bad data. + on_parole = 0x200, + + // This peer is a seed (it has all the pieces). + seed = 0x400, + + // This peer is subject to an optimistic unchoke. It has + // been unchoked for a while to see if it might unchoke + // us in return an earn an upload/unchoke slot. If it + // doesn't within some period of time, it will be choked + // and another peer will be optimistically unchoked. + optimistic_unchoke = 0x800, + + // This peer has recently failed to send a block within + // the request timeout from when the request was sent. + // We're currently picking one block at a time from this + // peer. + snubbed = 0x1000, + + // This peer has either explicitly (with an extension) + // or implicitly (by becoming a seed) told us that it + // will not downloading anything more, regardless of + // which pieces we have. + upload_only = 0x2000, + + // This means the last time this peer picket a piece, + // it could not pick as many as it wanted because there + // were not enough free ones. i.e. all pieces this peer + // has were already requested from other peers. + endgame_mode = 0x4000, + + // This flag is set if the peer was in holepunch mode + // when the connection succeeded. This typically only + // happens if both peers are behind a NAT and the peers + // connect via the NAT holepunch mechanism. + holepunched = 0x8000, + + // indicates that this socket is runnin on top of the + // I2P transport. + i2p_socket = 0x10000, + + // indicates that this socket is a uTP socket + utp_socket = 0x20000, + + // indicates that this socket is running on top of an SSL + // (TLS) channel + ssl_socket = 0x40000, + + // this connection is obfuscated with RC4 + rc4_encrypted = 0x100000, + + // the handshake of this connection was obfuscated + // with a diffie-hellman exchange + plaintext_encrypted = 0x200000 + }; + + // tells you in which state the peer is in. It is set to + // any combination of the peer_flags_t enum. + boost::uint32_t flags; + + // the flags indicating which sources a peer can + // have come from. A peer may have been seen from + // multiple sources + enum peer_source_flags + { + // The peer was received from the tracker. + tracker = 0x1, + + // The peer was received from the kademlia DHT. + dht = 0x2, + + // The peer was received from the peer exchange + // extension. + pex = 0x4, + + // The peer was received from the local service + // discovery (The peer is on the local network). + lsd = 0x8, + + // The peer was added from the fast resume data. + resume_data = 0x10, + + // we received an incoming connection from this peer + incoming = 0x20 + }; + + // a combination of flags describing from which sources this peer + // was received. + int source; + + // bits for the read_state and write_state + enum bw_state + { + // The peer is not waiting for any external events to + // send or receive data. + bw_idle = 0, + + // The peer is waiting for the rate limiter. + bw_limit = 1, + + // The peer has quota and is currently waiting for a + // network read or write operation to complete. This is + // the state all peers are in if there are no bandwidth + // limits. + bw_network = 2, + + // The peer is waiting for the disk I/O thread to catch + // up writing buffers to disk before downloading more. + bw_disk = 4 + }; +#ifndef TORRENT_NO_DEPRECATE + enum bw_state_deprecated { bw_torrent = bw_limit, bw_global = bw_limit }; +#endif + + // bitmasks indicating what state this peer is in with regards to sending + // and receiving data. The states are declared in the bw_state enum. + char read_state; + char write_state; + + // the IP-address to this peer. The type is an asio endpoint. For + // more info, see the asio_ documentation. + // + // .. _asio: http://asio.sourceforge.net/asio-0.3.8/doc/asio/reference.html + tcp::endpoint ip; + + // the current upload and download speed we have to and from this peer + // (including any protocol messages). updated about once per second + int up_speed; + int down_speed; + + // The transfer rates of payload data only updated about once per second + int payload_up_speed; + int payload_down_speed; + + // the total number of bytes downloaded from and uploaded to this peer. + // These numbers do not include the protocol chatter, but only the + // payload data. + size_type total_download; + size_type total_upload; + + // the peer's id as used in the bit torrent protocol. This id can be used + // to extract 'fingerprints' from the peer. Sometimes it can tell you + // which client the peer is using. See identify_client()_ + peer_id pid; + + // a bitfield, with one bit per piece in the torrent. + // Each bit tells you if the peer has that piece (if it's set to 1) + // or if the peer miss that piece (set to 0). + bitfield pieces; + + // the number of bytes per second we are allowed to send to or receive + // from this peer. It may be -1 if there's no local limit on the peer. + // The global limit and the torrent limit may also be enforced. + int upload_limit; + int download_limit; + + // the time since we last sent a request + // to this peer and since any transfer occurred with this peer + time_duration last_request; + time_duration last_active; + + // the time until all blocks in the request + // queue will be d + time_duration download_queue_time; + int queue_bytes; + + // the number of seconds until the current front piece request will time + // out. This timeout can be adjusted through + // ``session_settings::request_timeout``. + // -1 means that there is not outstanding request. + int request_timeout; + + // the number of bytes allocated + // and used for the peer's send buffer, respectively. + int send_buffer_size; + int used_send_buffer; + + // the number of bytes + // allocated and used as receive buffer, respectively. + int receive_buffer_size; + int used_receive_buffer; + + // the number of pieces this peer has participated in + // sending us that turned out to fail the hash check. + int num_hashfails; + + // the two letter `ISO 3166 country code`__ for the country the peer is + // connected from. If the country hasn't been resolved yet, both chars + // are set to 0. If the resolution failed for some reason, the field is + // set to "--". If the resolution service returns an invalid country + // code, it is set to "!!". The ``countries.nerd.dk`` service is used to + // look up countries. This field will remain set to 0 unless the torrent + // is set to resolve countries, see `resolve_countries()`_. + // + // __ http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html + char country[2]; + + // the name of the AS this peer is located in. This might be + // an empty string if there is no name in the geo ip database. + std::string inet_as_name; + + // the AS number the peer is located in. + int inet_as; + + // this is the number of requests + // we have sent to this peer + // that we haven't got a response + // for yet + int download_queue_length; + + // the number of block requests that have + // timed out, and are still in the download + // queue + int timed_out_requests; + + // the number of busy requests in the download + // queue. A budy request is a request for a block + // we've also requested from a different peer + int busy_requests; + + // the number of requests messages that are currently in the + // send buffer waiting to be sent. + int requests_in_buffer; + + // the number of requests that is + // tried to be maintained (this is + // typically a function of download speed) + int target_dl_queue_length; + + // the number of piece-requests we have received from this peer + // that we haven't answered with a piece yet. + int upload_queue_length; + + // the number of times this peer has "failed". i.e. failed to connect or + // disconnected us. The failcount is decremented when we see this peer in + // a tracker response or peer exchange message. + int failcount; + + // You can know which piece, and which part of that piece, that is + // currently being downloaded from a specific peer by looking at these + // four members. ``downloading_piece_index`` is the index of the piece + // that is currently being downloaded. This may be set to -1 if there's + // currently no piece downloading from this peer. If it is >= 0, the + // other three members are valid. ``downloading_block_index`` is the + // index of the block (or sub-piece) that is being downloaded. + // ``downloading_progress`` is the number of bytes of this block we have + // received from the peer, and ``downloading_total`` is the total number + // of bytes in this block. + int downloading_piece_index; + int downloading_block_index; + int downloading_progress; + int downloading_total; + + // a string describing the software at the other end of the connection. + // In some cases this information is not available, then it will contain + // a string that may give away something about which software is running + // in the other end. In the case of a web seed, the server type and + // version will be a part of this string. + std::string client; + + // the kind of connection this is. Used for the connection_type field. + enum connection_type_t + { + // Regular bittorrent connection over TCP + standard_bittorrent = 0, + + // HTTP connection using the `BEP 19`_ protocol + web_seed = 1, + + // HTTP connection using the `BEP 17`_ protocol + http_seed = 2 + }; + + // the kind of connection this peer uses. See connection_type_t. + int connection_type; + + // an estimate of the rate this peer is downloading at, in + // bytes per second. + int remote_dl_rate; + + // the number of bytes this peer has pending in the disk-io thread. + // Downloaded and waiting to be written to disk. This is what is capped + // by ``session_settings::max_queued_disk_bytes``. + int pending_disk_bytes; + + // the number of bytes this peer has been assigned to be allowed to send + // and receive until it has to request more quota from the bandwidth + // manager. + int send_quota; + int receive_quota; + + // an estimated round trip time to this peer, in milliseconds. It is + // estimated by timing the the tcp ``connect()``. It may be 0 for + // incoming connections. + int rtt; + + // the number of pieces this peer has. + int num_pieces; + + // the highest download and upload rates seen on this connection. They + // are given in bytes per second. This number is reset to 0 on reconnect. + int download_rate_peak; + int upload_rate_peak; + + // the progress of the peer in the range [0, 1]. This is always 0 when + // floating point operations are diabled, instead use ``progress_ppm``. + float progress; // [0, 1] + + // indicates the download progress of the peer in the range [0, 1000000] + // (parts per million). + int progress_ppm; + + // this is an estimation of the upload rate, to this peer, where it will + // unchoke us. This is a coarse estimation based on the rate at which + // we sent right before we were choked. This is primarily used for the + // bittyrant choking algorithm. + int estimated_reciprocation_rate; + + // the IP and port pair the socket is bound to locally. i.e. the IP + // address of the interface it's going out over. This may be useful for + // multi-homed clients with multiple interfaces to the internet. + tcp::endpoint local_endpoint; + }; + + // internal + struct TORRENT_EXPORT peer_list_entry + { + // internal + enum flags_t + { + banned = 1 + }; + + // internal + tcp::endpoint ip; + // internal + int flags; + // internal + boost::uint8_t failcount; + // internal + boost::uint8_t source; + }; + + // defined in policy.cpp + int source_rank(int source_bitmask); +} + +#endif // TORRENT_PEER_INFO_HPP_INCLUDED diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp new file mode 100644 index 0000000000..c8a19727e2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/peer_request.hpp @@ -0,0 +1,58 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_REQUEST_HPP_INCLUDED +#define TORRENT_PEER_REQUEST_HPP_INCLUDED + +namespace libtorrent +{ + + // represents a byte range within a piece. Internally this is + // is used for incoming piece requests. + struct TORRENT_EXPORT peer_request + { + // the index of the piece in which the range starts. + int piece; + // the offset within that piece where the range starts. + int start; + // the size of the range, in bytes. + int length; + + // returns true if the right hand side peer_request refers to the same + // range as this does. + bool operator==(peer_request const& r) const + { return piece == r.piece && start == r.start && length == r.length; } + }; +} + +#endif // TORRENT_PEER_REQUEST_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp new file mode 100644 index 0000000000..b2f791a57d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_block_progress.hpp @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED +#define TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct piece_block_progress + { + // the piece and block index + // determines exactly which + // part of the torrent that + // is currently being downloaded + int piece_index; + int block_index; + // the number of bytes we have received + // of this block + int bytes_downloaded; + // the number of bytes in the block + int full_block_bytes; + }; +} + +#endif // TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp new file mode 100644 index 0000000000..9980f840d9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/piece_picker.hpp @@ -0,0 +1,654 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef TORRENT_PIECE_PICKER_HPP_INCLUDED +#define TORRENT_PIECE_PICKER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/time.hpp" + +// #define TORRENT_DEBUG_REFCOUNTS + +#ifdef TORRENT_DEBUG_REFCOUNTS +#include +#endif + +namespace libtorrent +{ + + class torrent; + class peer_connection; + struct bitfield; + + struct TORRENT_EXTRA_EXPORT piece_block + { + const static piece_block invalid; + + piece_block() {} + piece_block(boost::uint32_t p_index, boost::uint16_t b_index) + : piece_index(p_index) + , block_index(b_index) + { + TORRENT_ASSERT(p_index < (1 << 19)); + TORRENT_ASSERT(b_index < (1 << 13)); + } + boost::uint32_t piece_index:19; + boost::uint32_t block_index:13; + + bool operator<(piece_block const& b) const + { + if (piece_index < b.piece_index) return true; + if (piece_index == b.piece_index) return block_index < b.block_index; + return false; + } + + bool operator==(piece_block const& b) const + { return piece_index == b.piece_index && block_index == b.block_index; } + + bool operator!=(piece_block const& b) const + { return piece_index != b.piece_index || block_index != b.block_index; } + }; + + class TORRENT_EXTRA_EXPORT piece_picker + { + public: + + struct piece_pos; + + enum + { + // the number of priority levels + priority_levels = 8, + // priority factor + prio_factor = priority_levels - 4 + }; + + struct block_info + { + block_info(): peer(0), num_peers(0), state(state_none) {} + // the peer this block was requested or + // downloaded from. This is a pointer to + // a policy::peer object + void* peer; + // the number of peers that has this block in their + // download or request queues + unsigned num_peers:14; + // the state of this block + enum { state_none, state_requested, state_writing, state_finished }; + unsigned state:2; +#if TORRENT_USE_ASSERTS + // to allow verifying the invariant of blocks belonging to the right piece + int piece_index; +#endif + }; + + // the peers that are downloading this piece + // are considered fast peers or slow peers. + // none is set if the blocks were downloaded + // in a previous session + enum piece_state_t + { none, slow, medium, fast }; + + enum options_t + { + // pick rarest first + rarest_first = 1, + // pick the most common first, or the last pieces if sequential + reverse = 2, + // only pick pieces exclusively requested from this peer + on_parole = 4, + // always pick partial pieces before any other piece + prioritize_partials = 8, + // pick pieces in sequential order + sequential = 16, + // have affinity to pieces with the same speed category + speed_affinity = 32, + // ignore the prefer_whole_pieces parameter + ignore_whole_pieces = 64, + // treat pieces with priority 6 and below as filtered + // to trigger end-game mode until all prio 7 pieces are + // completed + time_critical_mode = 128 + }; + + struct downloading_piece + { + downloading_piece(): info(NULL), index(-1) + , finished(0), writing(0), requested(0) + , state(none) {} + + bool operator<(downloading_piece const& rhs) const { return index < rhs.index; } + + // info about each block + // this is a pointer into the m_block_info + // vector owned by the piece_picker + block_info* info; + // the index of the piece + int index; + // the number of blocks in the finished state + boost::int16_t finished; + // the number of blocks in the writing state + boost::int16_t writing; + // the number of blocks in the requested state + boost::int16_t requested; + + // one of piece_state_t values + // really just needs 2 bits + boost::uint16_t state; + }; + + piece_picker(); + + void get_availability(std::vector& avail) const; + + // increases the peer count for the given piece + // (is used when a HAVE message is received) + void inc_refcount(int index, const void* peer); + void dec_refcount(int index, const void* peer); + + // increases the peer count for the given piece + // (is used when a BITFIELD message is received) + void inc_refcount(bitfield const& bitmask, const void* peer); + // decreases the peer count for the given piece + // (used when a peer disconnects) + void dec_refcount(bitfield const& bitmask, const void* peer); + + // these will increase and decrease the peer count + // of all pieces. They are used when seeds join + // or leave the swarm. + void inc_refcount_all(const void* peer); + void dec_refcount_all(const void* peer); + + // This indicates that we just received this piece + // it means that the refcounter will indicate that + // we are not interested in this piece anymore + // (i.e. we don't have to maintain a refcount) + void we_have(int index); + void we_dont_have(int index); + + int cursor() const { return m_cursor; } + int reverse_cursor() const { return m_reverse_cursor; } + int sparse_regions() const { return m_sparse_regions; } + + // sets all pieces to dont-have + void init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces); + int num_pieces() const { return int(m_piece_map.size()); } + + bool have_piece(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + return m_piece_map[index].index == piece_pos::we_have_index; + } + + bool is_downloading(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + piece_pos const& p = m_piece_map[index]; + return p.downloading; + } + + // sets the priority of a piece. + // returns true if the priority was changed from 0 to non-0 + // or vice versa + bool set_piece_priority(int index, int prio); + + // returns the priority for the piece at 'index' + int piece_priority(int index) const; + + // returns the current piece priorities for all pieces + void piece_priorities(std::vector& pieces) const; + + // ========== start deprecation ============== + + // fills the bitmask with 1's for pieces that are filtered + void filtered_pieces(std::vector& mask) const; + + // ========== end deprecation ============== + + // pieces should be the vector that represents the pieces a + // client has. It returns a list of all pieces that this client + // has and that are interesting to download. It returns them in + // priority order. It doesn't care about the download flag. + // The user of this function must lookup if any piece is + // marked as being downloaded. If the user of this function + // decides to download a piece, it must mark it as being downloaded + // itself, by using the mark_as_downloading() member function. + // THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION! + // The last argument is the policy::peer pointer for the peer that + // we'll download from. + void pick_pieces(bitfield const& pieces + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces + , int num_peers) const; + + // picks blocks from each of the pieces in the piece_list + // vector that is also in the piece bitmask. The blocks + // are added to interesting_blocks, and busy blocks are + // added to backup_blocks. num blocks is the number of + // blocks to be picked. Blocks are not picked from pieces + // that are being downloaded + int add_blocks(int piece, bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const; + + // picks blocks only from downloading pieces + int add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, piece_state_t speed + , int options) const; + + // clears the peer pointer in all downloading pieces with this + // peer pointer + void clear_peer(void* peer); + + // returns true if any client is currently downloading this + // piece-block, or if it's queued for downloading by some client + // or if it already has been successfully downloaded + bool is_requested(piece_block block) const; + // returns true if the block has been downloaded + bool is_downloaded(piece_block block) const; + // returns true if the block has been downloaded and written to disk + bool is_finished(piece_block block) const; + + // marks this piece-block as queued for downloading + bool mark_as_downloading(piece_block block, void* peer + , piece_state_t s); + // returns true if the block was marked as writing, + // and false if the block is already finished or writing + bool mark_as_writing(piece_block block, void* peer); + + void mark_as_finished(piece_block block, void* peer); + void write_failed(piece_block block); + int num_peers(piece_block block) const; + + // returns information about the given piece + void piece_info(int index, piece_picker::downloading_piece& st) const; + + piece_pos const& piece_stats(int index) const + { + TORRENT_ASSERT(index >= 0 && index < int(m_piece_map.size())); + return m_piece_map[index]; + } + + // if a piece had a hash-failure, it must be restored and + // made available for redownloading + void restore_piece(int index); + + // clears the given piece's download flag + // this means that this piece-block can be picked again + void abort_download(piece_block block, void* peer = 0); + + bool is_piece_finished(int index) const; + + // returns the number of blocks there is in the given piece + int blocks_in_piece(int index) const; + + // the number of downloaded blocks that hasn't passed + // the hash-check yet + int unverified_blocks() const; + + void get_downloaders(std::vector& d, int index) const; + + std::vector const& get_download_queue() const + { return m_downloads; } + + int num_downloading_pieces() const { return int(m_downloads.size()); } + + void* get_downloader(piece_block block) const; + + // the number of filtered pieces we don't have + int num_filtered() const { return m_num_filtered; } + + // the number of filtered pieces we already have + int num_have_filtered() const { return m_num_have_filtered; } + + int num_have() const { return m_num_have; } + + // the number of pieces we want and don't have + int num_want_left() const { return num_pieces() - m_num_have - m_num_filtered; } + +#if TORRENT_USE_INVARIANT_CHECKS + // used in debug mode + void verify_priority(int start, int end, int prio) const; + void verify_pick(std::vector const& picked + , bitfield const& bits) const; + void check_invariant(const torrent* t = 0) const; +#endif +#if defined TORRENT_PICKER_LOG + void print_pieces() const; +#endif + + // functor that compares indices on downloading_pieces + struct has_index + { + has_index(int i): index(i) { TORRENT_ASSERT(i >= 0); } + bool operator()(const downloading_piece& p) const + { return p.index == index; } + int index; + }; + + int blocks_in_last_piece() const + { return m_blocks_in_last_piece; } + + std::pair distributed_copies() const; + + private: + + friend struct piece_pos; + + bool can_pick(int piece, bitfield const& bitmask) const; + bool is_piece_free(int piece, bitfield const& bitmask) const; + std::pair expand_piece(int piece, int whole_pieces + , bitfield const& have) const; + + public: + + struct piece_pos + { + piece_pos() {} + piece_pos(int peer_count_, int index_) + : peer_count(peer_count_) + , downloading(0) + , full(0) + , piece_priority(1) + , index(index_) + { + TORRENT_ASSERT(peer_count_ >= 0); + TORRENT_ASSERT(index_ >= 0); + } + + // the number of peers that has this piece + // (availability) +#if TORRENT_COMPACT_PICKER + boost::uint32_t peer_count : 9; +#else + boost::uint32_t peer_count : 16; +#endif + // is 1 if the piece is marked as being downloaded + boost::uint32_t downloading : 1; + // set when downloading, but no free blocks to request left + boost::uint32_t full : 1; + // is 0 if the piece is filtered (not to be downloaded) + // 1 is normal priority (default) + // 2 is higher priority than pieces at the same availability level + // 3 is same priority as partial pieces + // 4 is higher priority than partial pieces + // 5 and 6 same priority as availability 1 (ignores availability) + // 7 is maximum priority (ignores availability) + boost::uint32_t piece_priority : 3; + // index in to the piece_info vector +#if TORRENT_COMPACT_PICKER + boost::uint32_t index : 18; +#else + boost::uint32_t index; +#endif + +#ifdef TORRENT_DEBUG_REFCOUNTS + // all the peers that have this piece + std::set have_peers; +#endif + enum + { + // index is set to this to indicate that we have the + // piece. There is no entry for the piece in the + // buckets if this is the case. +#if TORRENT_COMPACT_PICKER + we_have_index = 0x3ffff, +#else + we_have_index = 0xffffffff, +#endif + // the priority value that means the piece is filtered + filter_priority = 0, + // the max number the peer count can hold +#if TORRENT_COMPACT_PICKER + max_peer_count = 0x1ff +#else + max_peer_count = 0xffff +#endif + }; + + bool have() const { return index == we_have_index; } + void set_have() { index = we_have_index; TORRENT_ASSERT(have()); } + void set_not_have() { index = 0; TORRENT_ASSERT(!have()); } + + bool filtered() const { return piece_priority == filter_priority; } + + // prio 7 is always top priority + // prio 0 is always -1 (don't pick) + // downloading pieces are always on an even prio_factor priority + // + // availability x, downloading + // | availability x, prio 3; availability 2x, prio 6 + // | | availability x, prio 2; availability 2x, prio 5 + // | | | availability x, prio 1; availability 2x, prio 4 + // | | | | + // +---+---+---+---+ + // | 0 | 1 | 2 | 3 | + // +---+---+---+---+ + + int priority(piece_picker const* picker) const + { + // filtered pieces (prio = 0), pieces we have or pieces with + // availability = 0 should not be present in the piece list + // returning -1 indicates that they shouldn't. + if (filtered() || have() || peer_count + picker->m_seeds == 0) + return -1; + + // prio 7 disregards availability + if (piece_priority == priority_levels - 1) return 1 - downloading; + + // prio 4,5,6 halves the availability of a piece + int availability = peer_count; + int p = piece_priority; + if (piece_priority >= priority_levels / 2) + { + availability /= 2; + p -= (priority_levels - 2) / 2; + } + + if (downloading) return availability * prio_factor; + return availability * prio_factor + (priority_levels / 2) - p; + } + + bool operator!=(piece_pos p) const + { return index != p.index || peer_count != p.peer_count; } + + bool operator==(piece_pos p) const + { return index == p.index && peer_count == p.peer_count; } + }; + + void set_num_pad_files(int n) { m_num_pad_files = n; } + + private: + +#ifndef TORRENT_DEBUG_REFCOUNTS +#if TORRENT_COMPACT_PICKER + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); +#else + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 8); +#endif +#endif + + void break_one_seed(); + + void update_pieces() const; + + // fills in the range [start, end) of pieces in + // m_pieces that have priority 'prio' + void priority_range(int prio, int* start, int* end); + + // adds the piece 'index' to m_pieces + void add(int index); + // removes the piece with the given priority and the + // elem_index in the m_pieces vector + void remove(int priority, int elem_index); + // updates the position of the piece with the given + // priority and the elem_index in the m_pieces vector + void update(int priority, int elem_index); + // shuffles the given piece inside it's priority range + void shuffle(int priority, int elem_index); + +// void sort_piece(std::vector::iterator dp); + + downloading_piece& add_download_piece(int index); + void erase_download_piece(std::vector::iterator i); + + std::vector::const_iterator find_dl_piece(int index) const; + std::vector::iterator find_dl_piece(int index); + + void update_full(downloading_piece& dp); + + // some compilers (e.g. gcc 2.95, does not inherit access + // privileges to nested classes) + public: + // the number of seeds. These are not added to + // the availability counters of the pieces + int m_seeds; + private: + + // the following vectors are mutable because they sometimes may + // be updated lazily, triggered by const functions + + // this vector contains all piece indices that are pickable + // sorted by priority. Pieces are in random random order + // among pieces with the same priority + mutable std::vector m_pieces; + + // these are indices to the priority boundries inside + // the m_pieces vector. priority 0 always start at + // 0, priority 1 starts at m_priority_boundries[0] etc. + mutable std::vector m_priority_boundries; + + // this maps indices to number of peers that has this piece and + // index into the m_piece_info vectors. + // piece_pos::we_have_index means that we have the piece, so it + // doesn't exist in the piece_info buckets + // pieces with the filtered flag set doesn't have entries in + // the m_piece_info buckets either + mutable std::vector m_piece_map; + + // each piece that's currently being downloaded + // has an entry in this list with block allocations. + // i.e. it says wich parts of the piece that + // is being downloaded. This list is ordered + // by piece index to make lookups efficient + std::vector m_downloads; + + // this holds the information of the + // blocks in partially downloaded pieces. + // the first m_blocks_per_piece entries + // in the vector belongs to the first + // entry in m_downloads, the second + // m_blocks_per_piece entries to the + // second entry in m_downloads and so on. + std::vector m_block_info; + + boost::uint16_t m_blocks_per_piece; + boost::uint16_t m_blocks_in_last_piece; + + // the number of filtered pieces that we don't already + // have. total_number_of_pieces - number_of_pieces_we_have + // - num_filtered is supposed to the number of pieces + // we still want to download + int m_num_filtered; + + // the number of pieces we have that also are filtered + int m_num_have_filtered; + + // the number of pieces we have + int m_num_have; + + // we have all pieces in the range [0, m_cursor) + // m_cursor is the first piece we don't have + int m_cursor; + + // we have all pieces in the range [m_reverse_cursor, end) + // m_reverse_cursor is the first piece where we also have + // all the subsequent pieces + int m_reverse_cursor; + + // the number of regions of pieces we don't have. + int m_sparse_regions; + + // this is the number of partial download pieces + // that may be caused by pad files. We raise the limit + // of number of partial pieces by this amount, to not + // prioritize pieces that intersect pad files for no + // apparent reason + int m_num_pad_files; + + // if this is set to true, it means update_pieces() + // has to be called before accessing m_pieces. + mutable bool m_dirty; + public: + +#if TORRENT_COMPACT_PICKER + enum { max_pieces = piece_pos::we_have_index - 1 }; +#else + // still limited by piece_block + enum { max_pieces = (1 << 19) - 2 }; +#endif + + }; +} + +#endif // TORRENT_PIECE_PICKER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp new file mode 100644 index 0000000000..2cd265caec --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/policy.hpp @@ -0,0 +1,549 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_POLICY_HPP_INCLUDED +#define TORRENT_POLICY_HPP_INCLUDED + +#include +#include +#include "libtorrent/string_util.hpp" // for allocate_string_copy + +#include "libtorrent/peer.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + struct external_ip; + + // this is compressed as an unsigned floating point value + // the top 13 bits are the mantissa and the low + // 3 bits is the unsigned exponent. The exponent + // has an implicit + 4 as well. + // This means that the resolution is no less than 16 + // The actual rate is: (upload_rate >> 4) << ((upload_rate & 0xf) + 4) + // the resolution gets worse the higher the value is + // min value is 0, max value is 16775168 + struct ufloat16 + { + ufloat16():m_val(0) {} + ufloat16(int v) + { *this = v; } + operator int() + { + return (m_val >> 3) << ((m_val & 7) + 4); + } + + ufloat16& operator=(int v) + { + if (v > 0x1fff << (7 + 4)) m_val = 0xffff; + else if (v <= 0) m_val = 0; + else + { + int exp = 4; + v >>= 4; + while (v > 0x1fff) + { + v >>= 1; + ++exp; + } + TORRENT_ASSERT(exp <= 7); + m_val = (v << 3) || (exp & 7); + } + return *this; + } + private: + boost::uint16_t m_val; + }; + + enum + { + // the limits of the download queue size + min_request_queue = 2 + }; + + // calculate the priority of a peer based on its address. One of the + // endpoint should be our own. The priority is symmetric, so it doesn't + // matter which is which + TORRENT_EXTRA_EXPORT boost::uint32_t peer_priority( + tcp::endpoint e1, tcp::endpoint e2); + + void request_a_block(torrent& t, peer_connection& c); + + class TORRENT_EXTRA_EXPORT policy + { + public: + + policy(torrent* t); + + struct peer; + +#if TORRENT_USE_I2P + policy::peer* add_i2p_peer(char const* destination, int source, char flags); +#endif + + // this is called once for every peer we get from + // the tracker, pex, lsd or dht. + policy::peer* add_peer(const tcp::endpoint& remote, const peer_id& pid + , int source, char flags); + + // false means duplicate connection + bool update_peer_port(int port, policy::peer* p, int src); + + // called when an incoming connection is accepted + // false means the connection was refused or failed + bool new_connection(peer_connection& c, int session_time); + + // the given connection was just closed + void connection_closed(const peer_connection& c, int session_time); + + void ban_peer(policy::peer* p); + void set_connection(policy::peer* p, peer_connection* c); + void set_failcount(policy::peer* p, int f); + + // the peer has got at least one interesting piece + void peer_is_interesting(peer_connection& c); + + void ip_filter_updated(); + + void set_seed(policy::peer* p, bool s); + + // this clears all cached peer priorities. It's called when + // our external IP changes + void clear_peer_prio(); + +#if TORRENT_USE_ASSERTS + bool has_connection(const peer_connection* p); +#endif +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + struct TORRENT_EXTRA_EXPORT peer + { + peer(boost::uint16_t port, bool connectable, int src); + + size_type total_download() const; + size_type total_upload() const; + + boost::uint32_t rank(external_ip const& external, int external_port) const; + + libtorrent::address address() const; + char const* dest() const; + + tcp::endpoint ip() const { return tcp::endpoint(address(), port); } + + // this is the accumulated amount of + // uploaded and downloaded data to this + // peer. It only accounts for what was + // shared during the last connection to + // this peer. i.e. These are only updated + // when the connection is closed. For the + // total amount of upload and download + // we'll have to add thes figures with the + // statistics from the peer_connection. + // since these values don't need to be stored + // with byte-precision, they specify the number + // of kiB. i.e. shift left 10 bits to compare to + // byte counters. + boost::uint32_t prev_amount_upload; + boost::uint32_t prev_amount_download; + + // if the peer is connected now, this + // will refer to a valid peer_connection + peer_connection* connection; + +#ifndef TORRENT_DISABLE_GEO_IP +#ifdef TORRENT_DEBUG + // only used in debug mode to assert that + // the first entry in the AS pair keeps the same + boost::uint16_t inet_as_num; +#endif + // The AS this peer belongs to + std::pair* inet_as; +#endif + + // as computed by hashing our IP with the remote + // IP of this peer + // calculated lazily + mutable boost::uint32_t peer_rank; + + // the time when this peer was optimistically unchoked + // the last time. in seconds since session was created + // 16 bits is enough to last for 18.2 hours + // when the session time reaches 18 hours, it jumps back by + // 9 hours, and all peers' times are updated to be + // relative to that new time offset + boost::uint16_t last_optimistically_unchoked; + + // the time when the peer connected to us + // or disconnected if it isn't connected right now + // in number of seconds since session was created + boost::uint16_t last_connected; + + // the port this peer is or was connected on + boost::uint16_t port; + + // the upload and download rate limits set for this peer + ufloat16 upload_rate_limit; + ufloat16 download_rate_limit; + + // the number of times this peer has been + // part of a piece that failed the hash check + boost::uint8_t hashfails; + + // the number of failed connection attempts + // this peer has + unsigned failcount:5; // [0, 31] + + // incoming peers (that don't advertize their listen port) + // will not be considered connectable. Peers that + // we have a listen port for will be assumed to be. + bool connectable:1; + + // true if this peer currently is unchoked + // because of an optimistic unchoke. + // when the optimistic unchoke is moved to + // another peer, this peer will be choked + // if this is true + bool optimistically_unchoked:1; + + // this is true if the peer is a seed + bool seed:1; + + // the number of times we have allowed a fast + // reconnect for this peer. + unsigned fast_reconnects:4; + + // for every valid piece we receive where this + // peer was one of the participants, we increase + // this value. For every invalid piece we receive + // where this peer was a participant, we decrease + // this value. If it sinks below a threshold, its + // considered a bad peer and will be banned. + signed trust_points:4; // [-7, 8] + + // a bitmap combining the peer_source flags + // from peer_info. + unsigned source:6; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Hints encryption support of peer. Only effective + // for and when the outgoing encryption policy + // allows both encrypted and non encrypted + // connections (pe_settings::out_enc_policy + // == enabled). The initial state of this flag + // determines the initial connection attempt + // type (true = encrypted, false = standard). + // This will be toggled everytime either an + // encrypted or non-encrypted handshake fails. + bool pe_support:1; +#endif + +#if TORRENT_USE_IPV6 + // this is true if the v6 union member in addr is + // the one to use, false if it's the v4 one + bool is_v6_addr:1; +#endif +#if TORRENT_USE_I2P + // set if the i2p_destination is in use in the addr union + bool is_i2p_addr:1; +#endif + + // if this is true, the peer has previously + // participated in a piece that failed the piece + // hash check. This will put the peer on parole + // and only request entire pieces. If a piece pass + // that was partially requested from this peer it + // will leave parole mode and continue download + // pieces as normal peers. + bool on_parole:1; + + // is set to true if this peer has been banned + bool banned:1; + +#ifndef TORRENT_DISABLE_DHT + // this is set to true when this peer as been + // pinged by the DHT + bool added_to_dht:1; +#endif + // we think this peer supports uTP + bool supports_utp:1; + // we have been connected via uTP at least once + bool confirmed_supports_utp:1; + bool supports_holepunch:1; + // this is set to one for web seeds. Web seeds + // are not stored in the policy m_peers list, + // and are excempt from connect candidate bookkeeping + // so, any peer with the web_seed bit set, is + // never considered a connect candidate + bool web_seed:1; +#if TORRENT_USE_ASSERTS + bool in_use:1; +#endif + }; + + struct TORRENT_EXTRA_EXPORT ipv4_peer : peer + { + ipv4_peer(tcp::endpoint const& ip, bool connectable, int src); + + address_v4 addr; + }; + +#if TORRENT_USE_I2P + struct TORRENT_EXTRA_EXPORT i2p_peer : peer + { + i2p_peer(char const* destination, bool connectable, int src); + ~i2p_peer(); + + char* destination; + }; +#endif + +#if TORRENT_USE_IPV6 + struct TORRENT_EXTRA_EXPORT ipv6_peer : peer + { + ipv6_peer(tcp::endpoint const& ip, bool connectable, int src); + + const address_v6::bytes_type addr; + }; +#endif + + int num_peers() const { return int(m_peers.size()); } + + struct peer_address_compare + { + bool operator()( + peer const* lhs, libtorrent::address const& rhs) const + { + return lhs->address() < rhs; + } + + bool operator()( + libtorrent::address const& lhs, peer const* rhs) const + { + return lhs < rhs->address(); + } + +#if TORRENT_USE_I2P + bool operator()( + peer const* lhs, char const* rhs) const + { + return strcmp(lhs->dest(), rhs) < 0; + } + + bool operator()( + char const* lhs, peer const* rhs) const + { + return strcmp(lhs, rhs->dest()) < 0; + } +#endif + + bool operator()( + peer const* lhs, peer const* rhs) const + { +#if TORRENT_USE_I2P + if (rhs->is_i2p_addr == lhs->is_i2p_addr) + return strcmp(lhs->dest(), rhs->dest()) < 0; +#endif + return lhs->address() < rhs->address(); + } + }; + + typedef std::deque peers_t; + + typedef peers_t::iterator iterator; + typedef peers_t::const_iterator const_iterator; + iterator begin_peer() { return m_peers.begin(); } + iterator end_peer() { return m_peers.end(); } + const_iterator begin_peer() const { return m_peers.begin(); } + const_iterator end_peer() const { return m_peers.end(); } + + std::pair find_peers(address const& a) + { + return std::equal_range( + m_peers.begin(), m_peers.end(), a, peer_address_compare()); + } + + std::pair find_peers(address const& a) const + { + return std::equal_range( + m_peers.begin(), m_peers.end(), a, peer_address_compare()); + } + + bool connect_one_peer(int session_time); + + bool has_peer(policy::peer const* p) const; + + int num_seeds() const { return m_num_seeds; } + int num_connect_candidates() const { return m_num_connect_candidates; } + void recalculate_connect_candidates(); + + void erase_peer(policy::peer* p); + void erase_peer(iterator i); + + private: + + void update_peer(policy::peer* p, int src, int flags + , tcp::endpoint const& remote, char const* destination); + bool insert_peer(policy::peer* p, iterator iter, int flags); + + bool compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const; + bool compare_peer(policy::peer const& lhs, policy::peer const& rhs + , external_ip const& external, int source_port) const; + + iterator find_connect_candidate(int session_time); + + bool is_connect_candidate(peer const& p, bool finished) const; + bool is_erase_candidate(peer const& p, bool finished) const; + bool is_force_erase_candidate(peer const& pe) const; + bool should_erase_immediately(peer const& p) const; + + enum flags_t { force_erase = 1 }; + void erase_peers(int flags = 0); + + peers_t m_peers; + + torrent* m_torrent; + + // this shouldbe NULL for the most part. It's set + // to point to a valid torrent_peer object if that + // object needs to be kept alive. If we ever feel + // like removing a torrent_peer from m_peers, we + // first check if the peer matches this one, and + // if so, don't delete it. + peer* m_locked_peer; + + // since the peer list can grow too large + // to scan all of it, start at this iterator + int m_round_robin; + + // The number of peers in our peer list + // that are connect candidates. i.e. they're + // not already connected and they have not + // yet reached their max try count and they + // have the connectable state (we have a listen + // port for them). + int m_num_connect_candidates; + + // the number of seeds in the peer list + int m_num_seeds; + + // this was the state of the torrent the + // last time we recalculated the number of + // connect candidates. Since seeds (or upload + // only) peers are not connect candidates + // when we're finished, the set depends on + // this state. Every time m_torrent->is_finished() + // is different from this state, we need to + // recalculate the connect candidates. + bool m_finished:1; + }; + + inline policy::ipv4_peer::ipv4_peer( + tcp::endpoint const& ep, bool c, int src + ) + : peer(ep.port(), c, src) + , addr(ep.address().to_v4()) + { +#if TORRENT_USE_IPV6 + is_v6_addr = false; +#endif +#if TORRENT_USE_I2P + is_i2p_addr = false; +#endif + } + +#if TORRENT_USE_I2P + inline policy::i2p_peer::i2p_peer(char const* dest, bool connectable, int src) + : peer(0, connectable, src), destination(allocate_string_copy(dest)) + { +#if TORRENT_USE_IPV6 + is_v6_addr = false; +#endif + is_i2p_addr = true; + } + + inline policy::i2p_peer::~i2p_peer() + { free(destination); } +#endif // TORRENT_USE_I2P + +#if TORRENT_USE_IPV6 + inline policy::ipv6_peer::ipv6_peer( + tcp::endpoint const& ep, bool c, int src + ) + : peer(ep.port(), c, src) + , addr(ep.address().to_v6().to_bytes()) + { + is_v6_addr = true; +#if TORRENT_USE_I2P + is_i2p_addr = false; +#endif + } + +#endif // TORRENT_USE_IPV6 + +#if TORRENT_USE_I2P + inline char const* policy::peer::dest() const + { + if (is_i2p_addr) + return static_cast(this)->destination; + return ""; + } +#endif + + inline libtorrent::address policy::peer::address() const + { +#if TORRENT_USE_IPV6 + if (is_v6_addr) + return libtorrent::address_v6( + static_cast(this)->addr); + else +#endif +#if TORRENT_USE_I2P + if (is_i2p_addr) return libtorrent::address(); + else +#endif + return static_cast(this)->addr; + } + +} + +#endif // TORRENT_POLICY_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp new file mode 100644 index 0000000000..a9903cc229 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/proxy_base.hpp @@ -0,0 +1,256 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PROXY_BASE_HPP_INCLUDED +#define TORRENT_PROXY_BASE_HPP_INCLUDED + +#include "libtorrent/io.hpp" +#include "libtorrent/io_service_fwd.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" + +namespace libtorrent { + +class proxy_base : boost::noncopyable +{ +public: + + typedef stream_socket next_layer_type; + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit proxy_base(io_service& io_service) + : m_sock(io_service) + , m_port(0) + , m_resolver(io_service) + {} + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + return m_sock.write_some(buffers, ec); + } + + std::size_t available(error_code& ec) const + { return m_sock.available(ec); } + +#ifndef BOOST_NO_EXCEPTIONS + std::size_t available() const + { return m_sock.available(); } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + std::size_t write_some(Const_Buffers const& buffers) + { + return m_sock.write_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.io_control(ioc); + } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { + m_sock.io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { + m_sock.set_option(opt); + } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { + return m_sock.set_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { + m_sock.get_option(opt); + } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { + return m_sock.get_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& endpoint) + { +// m_sock.bind(endpoint); + } +#endif + + void bind(endpoint_type const& endpoint, error_code& ec) + { + // the reason why we ignore binds here is because we don't + // (necessarily) yet know what address family the proxy + // will resolve to, and binding to the wrong one would + // break our connection attempt later. The caller here + // doesn't necessarily know that we're proxying, so this + // bind address is based on the final endpoint, not the + // proxy. + // TODO: it would be nice to remember the bind port and bind once we know where the proxy is +// m_sock.bind(endpoint, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p) + { +// m_sock.open(p); + } +#endif + + void open(protocol_type const& p, error_code& ec) + { + // we need to ignore this for the same reason as stated + // for ignoring bind() +// m_sock.open(p, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + m_resolver.cancel(); + } +#endif + + void close(error_code& ec) + { + m_remote_endpoint = endpoint_type(); + m_sock.close(ec); + m_resolver.cancel(); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type remote_endpoint() const + { + return m_remote_endpoint; + } +#endif + + endpoint_type remote_endpoint(error_code& ec) const + { + if (!m_sock.is_open()) ec = asio::error::not_connected; + return m_remote_endpoint; + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type local_endpoint() const + { + return m_sock.local_endpoint(); + } +#endif + + endpoint_type local_endpoint(error_code& ec) const + { + return m_sock.local_endpoint(ec); + } + + io_service& get_io_service() + { + return m_sock.get_io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + next_layer_type& next_layer() + { + return m_sock; + } + + bool is_open() const { return m_sock.is_open(); } + +protected: + + stream_socket m_sock; + std::string m_hostname; + int m_port; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp new file mode 100644 index 0000000000..444bbb6789 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ptime.hpp @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PTIME_HPP_INCLUDED +#define TORRENT_PTIME_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include + +#if defined TORRENT_USE_BOOST_DATE_TIME + +#include +#include +#include + +namespace libtorrent +{ + typedef boost::posix_time::ptime ptime; + typedef boost::posix_time::time_duration time_duration; +} + +#else // TORRENT_USE_BOOST_DATE_TIME + +#include + +namespace libtorrent +{ + // libtorrent time_duration type + struct TORRENT_EXPORT time_duration + { + // hidden + time_duration() {} + + // all operators have the same semantics as a 64 bit signed integer + time_duration operator/(int rhs) const { return time_duration(diff / rhs); } + explicit time_duration(boost::int64_t d) : diff(d) {} + time_duration& operator-=(time_duration const& c) + { diff -= c.diff; return *this; } + time_duration& operator+=(time_duration const& c) + { diff += c.diff; return *this; } + time_duration& operator*=(int v) { diff *= v; return *this; } + time_duration operator+(time_duration const& c) + { return time_duration(diff + c.diff); } + time_duration operator-(time_duration const& c) + { return time_duration(diff - c.diff); } + + // internal + boost::int64_t diff; + }; + + // This type represents a point in time. + struct TORRENT_EXPORT ptime + { + // hidden + ptime() {} + explicit ptime(boost::uint64_t t): time(t) {} + + // these operators have the same semantics as signed 64 bit integers + ptime& operator+=(time_duration rhs) { time += rhs.diff; return *this; } + ptime& operator-=(time_duration rhs) { time -= rhs.diff; return *this; } + + // internal + boost::uint64_t time; + }; + + // returns true of the time duration is less than 0 + inline bool is_negative(time_duration dt) { return dt.diff < 0; } + + // all operators have the same semantics as signed 64 bit integers + inline bool operator>(ptime lhs, ptime rhs) + { return lhs.time > rhs.time; } + inline bool operator>=(ptime lhs, ptime rhs) + { return lhs.time >= rhs.time; } + inline bool operator<=(ptime lhs, ptime rhs) + { return lhs.time <= rhs.time; } + inline bool operator<(ptime lhs, ptime rhs) + { return lhs.time < rhs.time; } + inline bool operator!=(ptime lhs, ptime rhs) + { return lhs.time != rhs.time;} + inline bool operator==(ptime lhs, ptime rhs) + { return lhs.time == rhs.time;} + inline bool operator==(time_duration lhs, time_duration rhs) + { return lhs.diff == rhs.diff; } + inline bool operator<(time_duration lhs, time_duration rhs) + { return lhs.diff < rhs.diff; } + inline bool operator<=(time_duration lhs, time_duration rhs) + { return lhs.diff <= rhs.diff; } + inline bool operator>(time_duration lhs, time_duration rhs) + { return lhs.diff > rhs.diff; } + inline bool operator>=(time_duration lhs, time_duration rhs) + { return lhs.diff >= rhs.diff; } + inline time_duration operator*(time_duration lhs, int rhs) + { return time_duration(boost::int64_t(lhs.diff * rhs)); } + inline time_duration operator*(int lhs, time_duration rhs) + { return time_duration(boost::int64_t(lhs * rhs.diff)); } + inline time_duration operator-(ptime lhs, ptime rhs) + { return time_duration(lhs.time - rhs.time); } + inline ptime operator+(ptime lhs, time_duration rhs) + { return ptime(lhs.time + rhs.diff); } + inline ptime operator+(time_duration lhs, ptime rhs) + { return ptime(rhs.time + lhs.diff); } + inline ptime operator-(ptime lhs, time_duration rhs) + { return ptime(lhs.time - rhs.diff); } + +} + +#endif // TORRENT_USE_BOOST_DATE_TIME + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp new file mode 100644 index 0000000000..5af5abb0c5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/puff.hpp @@ -0,0 +1,33 @@ +/* puff.h + Copyright (C) 2002, 2003 Mark Adler, all rights reserved + version 1.7, 3 Mar 2002 + + This software is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Mark Adler madler@alumni.caltech.edu + */ + + +/* + * See puff.c for purpose and usage. + */ +#include + +int puff(unsigned char *dest, /* pointer to destination pointer */ + boost::uint32_t *destlen, /* amount of output space */ + unsigned char *source, /* pointer to source data pointer */ + boost::uint32_t *sourcelen); /* amount of input available */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp new file mode 100644 index 0000000000..07589a8043 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/random.hpp @@ -0,0 +1,40 @@ +/* + +Copyright (c) 2011-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include + +namespace libtorrent +{ + void TORRENT_EXTRA_EXPORT random_seed(boost::uint32_t v); + boost::uint32_t TORRENT_EXTRA_EXPORT random(); +} diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp new file mode 100644 index 0000000000..5a32b352b1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/rss.hpp @@ -0,0 +1,277 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_RSS_HPP_INCLUDED +#define TORRENT_RSS_HPP_INCLUDED + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/size_type.hpp" + +#include +#include + +namespace libtorrent +{ + namespace aux + { struct session_impl; } + + // represents one item from an RSS feed. Specifically + // a feed of torrents. + // + struct TORRENT_EXPORT feed_item + { + feed_item(); + ~feed_item(); + + // these are self explanatory and may be empty if the feed does not specify + // those fields. + std::string url; + std::string uuid; + std::string title; + std::string description; + std::string comment; + std::string category; + + // the total size of the content the torrent refers to, or -1 + // if no size was specified by the feed. + size_type size; + + // the handle to the torrent, if the session is already downloading + // this torrent. + torrent_handle handle; + + // the info-hash of the torrent, or cleared (i.e. all zeroes) if + // the feed does not specify the info-hash. + sha1_hash info_hash; + }; + + // given a feed_item ``f``, add the torrent it refers to to session ``s``. +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& p); +#endif + torrent_handle TORRENT_EXPORT add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& p, error_code& ec); + + // the feed_settings object is all the information + // and configuration for a specific feed. All of + // these settings can be changed by the user + // after adding the feed + struct TORRENT_EXPORT feed_settings + { + feed_settings() + : auto_download(true) + , auto_map_handles(true) + , default_ttl(30) + {} + + std::string url; + + // By default ``auto_download`` is true, which means all torrents in + // the feed will be downloaded. Set this to false in order to manually + // add torrents to the session. You may react to the rss_alert when + // a feed has been updated to poll it for the new items in the feed + // when adding torrents manually. When torrents are added automatically, + // an add_torrent_alert is posted which includes the torrent handle + // as well as the error code if it failed to be added. You may also call + // ``session::get_torrents()`` to get the handles to the new torrents. + bool auto_download; + + // ``auto_map_handles`` defaults to true and determines whether or + // not to set the ``handle`` field in the feed_item, returned + // as the feed status. If auto-download is enabled, this setting + // is ignored. If auto-download is not set, setting this to false + // will save one pass through all the feed items trying to find + // corresponding torrents in the session. + bool auto_map_handles; + + // The ``default_ttl`` is the default interval for refreshing a feed. + // This may be overridden by the feed itself (by specifying the ```` + // tag) and defaults to 30 minutes. The field specifies the number of + // minutes between refreshes. + int default_ttl; + + // If torrents are added automatically, you may want to set the + // ``add_args`` to appropriate values for download directory etc. + // This object is used as a template for adding torrents from feeds, + // but some torrent specific fields will be overridden by the + // individual torrent being added. For more information on the + // add_torrent_params, see async_add_torrent() and add_torrent(). + add_torrent_params add_args; + }; + + // holds information about the status of an RSS feed. Retrieved by + // calling get_feed_status() on feed_handle. + struct TORRENT_EXPORT feed_status + { + feed_status(): last_update(0), next_update(0) + , updating(false), ttl(0) {} + + // the URL of the feed. + std::string url; + + // the name of the feed (as specified by the feed itself). This + // may be empty if we have not recevied a response from the RSS server yet, + // or if the feed does not specify a title. + std::string title; + + // the feed description (as specified by the feed itself). + // This may be empty if we have not received a response from the RSS server + // yet, or if the feed does not specify a description. + std::string description; + + // the posix time of the last successful response from the feed. + time_t last_update; + + // the number of seconds, from now, when the feed will be + // updated again. + int next_update; + + // true if the feed is currently being updated (i.e. waiting for + // DNS resolution, connecting to the server or waiting for the response to the + // HTTP request, or receiving the response). + bool updating; + + // a vector of all items that we have received from the feed. See + // feed_item for more information. + std::vector items; + + // set to the appropriate error code if the feed encountered an + // error. See error_code for more info. + error_code error; + + // the current refresh time (in minutes). It's either the configured + // default ttl, or the ttl specified by the feed. + int ttl; + }; + + struct feed; + + // The ``feed_handle`` refers to a specific RSS feed that is watched by the session. + struct TORRENT_EXPORT feed_handle + { + feed_handle() {} + + // Forces an update/refresh of the feed. Regular updates of the feed is managed + // by libtorrent, be careful to not call this too frequently since it may + // overload the RSS server. + void update_feed(); + + // Queries the RSS feed for information, including all the items in the feed. + // see feed_status. + feed_status get_feed_status() const; + + // Sets and gets settings for this feed. For more information on the + // available settings, see add_feed(). + void set_settings(feed_settings const& s); + feed_settings settings() const; + private: + friend struct aux::session_impl; + friend struct feed; + feed_handle(boost::weak_ptr const& p); + boost::weak_ptr m_feed_ptr; + }; + + struct feed_state; + class http_parser; + + boost::shared_ptr TORRENT_EXPORT new_feed(aux::session_impl& ses, feed_settings const& sett); + + // this is the internal object holding all state about an + // RSS feed. All user interaction with this object + // goes through the feed_handle, which makes sure all calls + // are posted to the network thread + struct TORRENT_EXTRA_EXPORT feed : boost::enable_shared_from_this + { + friend void parse_feed(feed_state& f, int token, char const* name, char const* val); + + feed(aux::session_impl& ses, feed_settings const& feed); + + void on_feed(error_code const& ec, http_parser const& parser + , char const* data, int size); + + int update_feed(); + + aux::session_impl& session() const { return m_ses; } + + void set_settings(feed_settings const& s); + void get_settings(feed_settings* s) const; + void get_feed_status(feed_status* ret) const; + + int next_update(time_t now) const; + + void load_state(lazy_entry const& rd); + void save_state(entry& rd) const; + +// private: + + void add_item(feed_item const& item); + + feed_handle my_handle(); + + error_code m_error; + std::vector m_items; + + // these are all the URLs we've seen in the items list. + // it's used to avoid adding duplicate entries to the actual + // item vector + std::set m_urls; + + // these are URLs that have been added to the session + // once. If we see them again, and they're not in the + // session, don't add them again, since it means they + // were removed from the session. It maps URLs to the + // posix time when they were added. The timestamp is + // used to prune this list by removing the oldest ones + // when the size gets too big + std::map m_added; + + std::string m_title; + std::string m_description; + time_t m_last_attempt; + time_t m_last_update; + // refresh rate of this feed in minutes + int m_ttl; + // the number of update failures in a row + int m_failures; + // true while waiting for the server to respond + bool m_updating; + feed_settings m_settings; + + aux::session_impl& m_ses; + }; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp new file mode 100644 index 0000000000..40846044aa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session.hpp @@ -0,0 +1,1064 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_HPP_INCLUDED +#define TORRENT_SESSION_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" // alert::error_notification +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/rss.hpp" +#include "libtorrent/build_config.hpp" + +#include "libtorrent/storage.hpp" + +#ifdef _MSC_VER +# include +#endif + +#ifdef TORRENT_USE_OPENSSL +// this is a nasty openssl macro +#ifdef set_key +#undef set_key +#endif +#endif + +namespace libtorrent +{ + struct plugin; + struct torrent_plugin; + class torrent; + struct ip_filter; + class port_filter; + class connection_queue; + class alert; + + // The default values of the session settings are set for a regular + // bittorrent client running on a desktop system. There are functions that + // can set the session settings to pre set settings for other environments. + // These can be used for the basis, and should be tweaked to fit your needs + // better. + // + // ``min_memory_usage`` returns settings that will use the minimal amount of + // RAM, at the potential expense of upload and download performance. It + // adjusts the socket buffer sizes, disables the disk cache, lowers the send + // buffer watermarks so that each connection only has at most one block in + // use at any one time. It lowers the outstanding blocks send to the disk + // I/O thread so that connections only have one block waiting to be flushed + // to disk at any given time. It lowers the max number of peers in the peer + // list for torrents. It performs multiple smaller reads when it hashes + // pieces, instead of reading it all into memory before hashing. + // + // This configuration is inteded to be the starting point for embedded + // devices. It will significantly reduce memory usage. + // + // ``high_performance_seed`` returns settings optimized for a seed box, + // serving many peers and that doesn't do any downloading. It has a 128 MB + // disk cache and has a limit of 400 files in its file pool. It support fast + // upload rates by allowing large send buffers. + TORRENT_EXPORT session_settings min_memory_usage(); + TORRENT_EXPORT session_settings high_performance_seed(); + +#ifndef TORRENT_CFG +#error TORRENT_CFG is not defined! +#endif + + void TORRENT_EXPORT TORRENT_CFG(); + + namespace aux + { + struct session_impl; + } + + // this is a holder for the internal session implementation object. Once the + // session destruction is explicitly initiated, this holder is used to + // synchronize the completion of the shutdown. The lifetime of this object + // may outlive session, causing the session destructor to not block. The + // session_proxy destructor will block however, until the underlying session + // is done shutting down. + class TORRENT_EXPORT session_proxy + { + friend class session; + public: + // default constructor, does not refer to any session + // implementation object. + session_proxy() {} + private: + session_proxy(boost::shared_ptr impl) + : m_impl(impl) {} + boost::shared_ptr m_impl; + }; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +#define TORRENT_LOGPATH_ARG_DEFAULT , std::string logpath = "." +#else +#define TORRENT_LOGPATH_ARG_DEFAULT +#endif + + // The session holds all state that spans multiple torrents. Among other + // things it runs the network loop and manages all torrents. Once it's + // created, the session object will spawn the main thread that will do all + // the work. The main thread will be idle as long it doesn't have any + // torrents to participate in. + class TORRENT_EXPORT session: public boost::noncopyable + { + public: + + // If the fingerprint in the first overload is omited, the client will + // get a default fingerprint stating the version of libtorrent. The + // fingerprint is a short string that will be used in the peer-id to + // identify the client and the client's version. For more details see the + // fingerprint class. The constructor that only takes a fingerprint will + // not open a listen port for the session, to get it running you'll have + // to call ``session::listen_on()``. The other constructor, that takes a + // port range and an interface as well as the fingerprint will + // automatically try to listen on a port on the given interface. For more + // information about the parameters, see ``listen_on()`` function. + // + // The flags paramater can be used to start default features (upnp & + // nat-pmp) and default plugins (ut_metadata, ut_pex and smart_ban). The + // default is to start those things. If you do not want them to start, + // pass 0 as the flags parameter. + // + // The ``alert_mask`` is the same mask that you would send to + // set_alert_mask(). + session(fingerprint const& print = fingerprint("LT" + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0) + , int flags = start_default_features | add_default_plugins + , boost::uint32_t alert_mask = alert::error_notification + TORRENT_LOGPATH_ARG_DEFAULT) + { + TORRENT_CFG(); + init(std::make_pair(0, 0), "0.0.0.0", print, alert_mask); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + set_log_path(logpath); +#endif + start(flags); + } + session(fingerprint const& print + , std::pair listen_port_range + , char const* listen_interface = "0.0.0.0" + , int flags = start_default_features | add_default_plugins + , int alert_mask = alert::error_notification + TORRENT_LOGPATH_ARG_DEFAULT) + { + TORRENT_CFG(); + TORRENT_ASSERT(listen_port_range.first > 0); + TORRENT_ASSERT(listen_port_range.first < listen_port_range.second); + init(listen_port_range, listen_interface, print, alert_mask); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + set_log_path(logpath); +#endif + start(flags); + } + + // The destructor of session will notify all trackers that our torrents + // have been shut down. If some trackers are down, they will time out. + // All this before the destructor of session returns. So, it's advised + // that any kind of interface (such as windows) are closed before + // destructing the session object. Because it can take a few second for + // it to finish. The timeout can be set with ``set_settings()``. + ~session(); + + // TODO: 2 the ip filter should probably be saved here too + + // flags that determines which aspects of the session should be + // saved when calling save_state(). + enum save_state_flags_t + { + // saves settings (i.e. the session_settings) + save_settings = 0x001, + + // saves dht_settings + save_dht_settings = 0x002, + + // saves dht state such as nodes and node-id, possibly accelerating + // joining the DHT if provided at next session startup. + save_dht_state = 0x004, + + // save proxy_settings + save_proxy = 0x008, + + // save i2p_proxy settings + save_i2p_proxy = 0x010, + + // save pe_settings + save_encryption_settings = 0x020, + + // internal + save_as_map = 0x040, + + // saves RSS feeds + save_feeds = 0x080 + +#ifndef TORRENT_NO_DEPRECATE + , + save_dht_proxy = save_proxy, + save_peer_proxy = save_proxy, + save_web_proxy = save_proxy, + save_tracker_proxy = save_proxy +#endif + }; + + // loads and saves all session settings, including dht_settings, + // encryption settings and proxy settings. ``save_state`` writes all keys + // to the ``entry`` that's passed in, which needs to either not be + // initialized, or initialized as a dictionary. + // + // ``load_state`` expects a lazy_entry which can be built from a bencoded + // buffer with lazy_bdecode(). + // + // The ``flags`` arguments passed in to ``save_state`` can be used to + // filter which parts of the session state to save. By default, all state + // is saved (except for the individual torrents). see save_state_flags_t + void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; + void load_state(lazy_entry const& e); + + // .. note:: + // these calls are potentially expensive and won't scale well with + // lots of torrents. If you're concerned about performance, consider + // using ``post_torrent_updates()`` instead. + // + // ``get_torrent_status`` returns a vector of the torrent_status for + // every torrent which satisfies ``pred``, which is a predicate function + // which determines if a torrent should be included in the returned set + // or not. Returning true means it should be included and false means + // excluded. The ``flags`` argument is the same as to + // ``torrent_handle::status()``. Since ``pred`` is guaranteed to be + // called for every torrent, it may be used to count the number of + // torrents of different categories as well. + // + // ``refresh_torrent_status`` takes a vector of torrent_status structs + // (for instance the same vector that was returned by + // get_torrent_status() ) and refreshes the status based on the + // ``handle`` member. It is possible to use this function by first + // setting up a vector of default constructed ``torrent_status`` objects, + // only initializing the ``handle`` member, in order to request the + // torrent status for multiple torrents in a single call. This can save a + // significant amount of time if you have a lot of torrents. + // + // Any torrent_status object whose ``handle`` member is not referring to + // a valid torrent are ignored. + void get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags = 0) const; + void refresh_torrent_status(std::vector* ret + , boost::uint32_t flags = 0) const; + + // This functions instructs the session to post the state_update_alert, + // containing the status of all torrents whose state changed since the + // last time this function was called. + // + // Only torrents who has the state subscription flag set will be + // included. This flag is on by default. See add_torrent_params. + void post_torrent_updates(); + + // internal + io_service& get_io_service(); + + // ``find_torrent()`` looks for a torrent with the given info-hash. In + // case there is such a torrent in the session, a torrent_handle to that + // torrent is returned. In case the torrent cannot be found, an invalid + // torrent_handle is returned. + // + // See ``torrent_handle::is_valid()`` to know if the torrent was found or + // not. + // + // ``get_torrents()`` returns a vector of torrent_handles to all the + // torrents currently in the session. + torrent_handle find_torrent(sha1_hash const& info_hash) const; + std::vector get_torrents() const; + + // You add torrents through the add_torrent() function where you give an + // object with all the parameters. The add_torrent() overloads will block + // until the torrent has been added (or failed to be added) and returns + // an error code and a torrent_handle. In order to add torrents more + // efficiently, consider using async_add_torrent() which returns + // immediately, without waiting for the torrent to add. Notification of + // the torrent being added is sent as add_torrent_alert. + // + // The overload that does not take an error_code throws an exception on + // error and is not available when building without exception support. + // The torrent_handle returned by add_torrent() can be used to retrieve + // information about the torrent's progress, its peers etc. It is also + // used to abort a torrent. + // + // If the torrent you are trying to add already exists in the session (is + // either queued for checking, being checked or downloading) + // ``add_torrent()`` will throw libtorrent_exception which derives from + // ``std::exception`` unless duplicate_is_error is set to false. In that + // case, add_torrent() will return the handle to the existing torrent. + // + // all torrent_handles must be destructed before the session is destructed! +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle add_torrent(add_torrent_params const& params); +#endif + torrent_handle add_torrent(add_torrent_params const& params, error_code& ec); + void async_add_torrent(add_torrent_params const& params); + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + torrent_info const& ti + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor) TORRENT_DEPRECATED; + + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + boost::intrusive_ptr ti + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; + + // deprecated in 0.14 + TORRENT_DEPRECATED_PREFIX + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , std::string const& save_path + , entry const& resume_data = entry() + , storage_mode_t storage_mode = storage_mode_sparse + , bool paused = false + , storage_constructor_type sc = default_storage_constructor + , void* userdata = 0) TORRENT_DEPRECATED; +#endif +#endif + + // In case you want to destruct the session asynchrounously, you can + // request a session destruction proxy. If you don't do this, the + // destructor of the session object will block while the trackers are + // contacted. If you keep one ``session_proxy`` to the session when + // destructing it, the destructor will not block, but start to close down + // the session, the destructor of the proxy will then synchronize the + // threads. So, the destruction of the session is performed from the + // ``session`` destructor call until the ``session_proxy`` destructor + // call. The ``session_proxy`` does not have any operations on it (since + // the session is being closed down, no operations are allowed on it). + // The only valid operation is calling the destructor:: + // + // class session_proxy + // { + // public: + // session_proxy(); + // ~session_proxy() + // }; + session_proxy abort() { return session_proxy(m_impl); } + + // Pausing the session has the same effect as pausing every torrent in + // it, except that torrents will not be resumed by the auto-manage + // mechanism. Resuming will restore the torrents to their previous paused + // state. i.e. the session pause state is separate from the torrent pause + // state. A torrent is inactive if it is paused or if the session is + // paused. + void pause(); + void resume(); + bool is_paused() const; + + // returns session wide-statistics and status. For more information, see + // the ``session_status`` struct. + session_status status() const; + + // Returns status of the disk cache for this session. For more + // information, see the cache_status type. + cache_status get_cache_status() const; + + // fills out the supplied vector with information for + // each piece that is currently in the disk cache for the torrent with the + // specified info-hash (``ih``). + void get_cache_info(sha1_hash const& ih + , std::vector& ret) const; + + // This adds an RSS feed to the session. The feed will be refreshed + // regularly and optionally add all torrents from the feed, as they + // appear. + // + // Before adding the feed, you must set the ``url`` field to the feed's + // url. It may point to an RSS or an atom feed. The returned feed_handle + // is a handle which is used to interact with the feed, things like + // forcing a refresh or querying for information about the items in the + // feed. For more information, see feed_handle. + feed_handle add_feed(feed_settings const& feed); + + // Removes a feed from being watched by the session. When this + // call returns, the feed handle is invalid and won't refer + // to any feed. + void remove_feed(feed_handle h); + + // Returns a list of all RSS feeds that are being watched by the session. + void get_feeds(std::vector& f) const; + + // starts/stops UPnP, NATPMP or LSD port mappers they are stopped by + // default These functions are not available in case + // ``TORRENT_DISABLE_DHT`` is defined. ``start_dht`` starts the dht node + // and makes the trackerless service available to torrents. The startup + // state is optional and can contain nodes and the node id from the + // previous session. The dht node state is a bencoded dictionary with the + // following entries: + // + // nodes + // A list of strings, where each string is a node endpoint encoded in + // binary. If the string is 6 bytes long, it is an IPv4 address of 4 + // bytes, encoded in network byte order (big endian), followed by a 2 + // byte port number (also network byte order). If the string is 18 + // bytes long, it is 16 bytes of IPv6 address followed by a 2 bytes + // port number (also network byte order). + // + // node-id + // The node id written as a readable string as a hexadecimal number. + // + // ``dht_state`` will return the current state of the dht node, this can + // be used to start up the node again, passing this entry to + // ``start_dht``. It is a good idea to save this to disk when the session + // is closed, and read it up again when starting. + // + // If the port the DHT is supposed to listen on is already in use, and + // exception is thrown, ``asio::error``. + // + // ``stop_dht`` stops the dht node. + // + // ``add_dht_node`` adds a node to the routing table. This can be used if + // your client has its own source of bootstrapping nodes. + // + // ``set_dht_settings`` sets some parameters availavle to the dht node. + // See dht_settings for more information. + // + // ``is_dht_running()`` returns true if the DHT support has been started + // and false + // otherwise. + void start_dht(); + void stop_dht(); + void set_dht_settings(dht_settings const& settings); + bool is_dht_running() const; + + // ``add_dht_node`` takes a host name and port pair. That endpoint will be + // pinged, and if a valid DHT reply is received, the node will be added to + // the routing table. + // + // ``add_dht_router`` adds the given endpoint to a list of DHT router nodes. + // If a search is ever made while the routing table is empty, those nodes will + // be used as backups. Nodes in the router node list will also never be added + // to the regular routing table, which effectively means they are only used + // for bootstrapping, to keep the load off them. + // + // An example routing node that you could typically add is + // ``router.bittorrent.com``. + void add_dht_node(std::pair const& node); + void add_dht_router(std::pair const& node); + + // query the DHT for an immutable item at the ``target`` hash. + // the result is posted as a dht_immutable_item_alert. + void dht_get_item(sha1_hash const& target); + + // query the DHT for a mutable item under the public key ``key``. + // this is an ed25519 key. ``salt`` is optional and may be left + // as an empty string if no salt is to be used. + // if the item is found in the DHT, a dht_mutable_item_alert is + // posted. + void dht_get_item(boost::array key + , std::string salt = std::string()); + + // store the given bencoded data as an immutable item in the DHT. + // the returned hash is the key that is to be used to look the item + // up agan. It's just the sha-1 hash of the bencoded form of the + // structure. + sha1_hash dht_put_item(entry data); + + // store a mutable item. The ``key`` is the public key the blob is + // to be stored under. The optional ``salt`` argument is a string that + // is to be mixed in with the key when determining where in the DHT + // the value is to be stored. The callback function is called from within + // the libtorrent network thread once we've found where to store the blob, + // possibly with the current value stored under the key. + // The values passed to the callback functions are: + // + // entry& value + // the current value stored under the key (may be empty). Also expected + // to be set to the value to be stored by the function. + // + // boost::array& signature + // the signature authenticating the current value. This may be zeroes + // if there is currently no value stored. The functon is expected to + // fill in this buffer with the signature of the new value to store. + // To generate the signature, you may want to use the + // ``sign_mutable_item`` function. + // + // boost::uint64_t& seq + // current sequence number. May be zero if there is no current value. + // The function is expected to set this to the new sequence number of + // the value that is to be stored. Sequence numbers must be monotonically + // increasing. Attempting to overwrite a value with a lower or equal + // sequence number will fail, even if the signature is correct. + // + // std::string const& salt + // this is the salt that was used for this put call. + // + // Since the callback function ``cb`` is called from within libtorrent, + // it is critical to not perform any blocking operations. Ideally not + // even locking a mutex. Pass any data required for this function along + // with the function object's context and make the function entirely + // self-contained. The only reason data blobs' values are computed + // via a function instead of just passing in the new value is to avoid + // race conditions. If you want to *update* the value in the DHT, you + // must first retrieve it, then modify it, then write it back. The way + // the DHT works, it is natural to always do a lookup before storing and + // calling the callback in between is convenient. + void dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt = std::string()); + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.15 + // use save_state and load_state instead + TORRENT_DEPRECATED_PREFIX + entry dht_state() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void start_dht(entry const& startup_state) TORRENT_DEPRECATED; +#endif + + // This function adds an extension to this session. The argument is a + // function object that is called with a ``torrent*`` and which should + // return a ``boost::shared_ptr``. To write custom + // plugins, see `libtorrent plugins`_. For the typical bittorrent client + // all of these extensions should be added. The main plugins implemented + // in libtorrent are: + // + // metadata extension + // Allows peers to download the metadata (.torren files) from the swarm + // directly. Makes it possible to join a swarm with just a tracker and + // info-hash. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_metadata_plugin); + // + // uTorrent metadata + // Same as ``metadata extension`` but compatible with uTorrent. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_ut_metadata_plugin); + // + // uTorrent peer exchange + // Exchanges peers between clients. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_ut_pex_plugin); + // + // smart ban plugin + // A plugin that, with a small overhead, can ban peers + // that sends bad data with very high accuracy. Should + // eliminate most problems on poisoned torrents. + // + // :: + // + // #include + // ses.add_extension(&libtorrent::create_smart_ban_plugin); + // + // + // .. _`libtorrent plugins`: libtorrent_plugins.html + void add_extension(boost::function( + torrent*, void*)> ext); + void add_extension(boost::shared_ptr ext); + + // These functions are not available if ``TORRENT_DISABLE_GEO_IP`` is + // defined. They expects a path to the `MaxMind ASN database`_ and + // `MaxMind GeoIP database`_ respectively. This will be used to look up + // which AS and country peers belong to. + // + // ``as_for_ip`` returns the AS number for the IP address specified. If + // the IP is not in the database or the ASN database is not loaded, 0 is + // returned. + // + // .. _`MaxMind ASN database`: http://www.maxmind.com/app/asnum + // .. _`MaxMind GeoIP database`: http://www.maxmind.com/app/geolitecountry + void load_asnum_db(char const* file); + void load_country_db(char const* file); + int as_for_ip(address const& addr); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void load_country_db(wchar_t const* file) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void load_asnum_db(wchar_t const* file) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.15 + // use load_state and save_state instead + TORRENT_DEPRECATED_PREFIX + void load_state(entry const& ses_state) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + entry state() const TORRENT_DEPRECATED; +#endif + + // Sets a filter that will be used to reject and accept incoming as well + // as outgoing connections based on their originating ip address. The + // default filter will allow connections to any ip address. To build a + // set of rules for which addresses are accepted and not, see ip_filter. + // + // Each time a peer is blocked because of the IP filter, a + // peer_blocked_alert is generated. ``get_ip_filter()`` Returns the + // ip_filter currently in the session. See ip_filter. + void set_ip_filter(ip_filter const& f); + ip_filter get_ip_filter() const; + + // apply port_filter ``f`` to incoming and outgoing peers. a port filter + // will reject making outgoing peer connections to certain remote ports. + // The main intention is to be able to avoid triggering certain + // anti-virus software by connecting to SMTP, FTP ports. + void set_port_filter(port_filter const& f); + + // sets and gets the raw peer ID used by libtorrent. When anonymous + // mode is set the peer ID is randomized per peer anyway. + void set_peer_id(peer_id const& pid); + peer_id id() const; + + // sets the key sent to trackers. If it's not set, it is initialized + // by libtorrent. The key may be used by the tracker to identify the + // peer potentially across you changing your IP. + void set_key(int key); + + + // ``is_listening()`` will tell you whether or not the session has + // successfully opened a listening port. If it hasn't, this function will + // return false, and then you can use ``listen_on()`` to make another + // attempt. + // + // ``listen_port()`` returns the port we ended up listening on. Since you + // just pass a port-range to the constructor and to ``listen_on()``, to + // know which port it ended up using, you have to ask the session using + // this function. + // + // ``listen_on()`` will change the listen port and/or the listen + // interface. If the session is already listening on a port, this socket + // will be closed and a new socket will be opened with these new + // settings. The port range is the ports it will try to listen on, if the + // first port fails, it will continue trying the next port within the + // range and so on. The interface parameter can be left as 0, in that + // case the os will decide which interface to listen on, otherwise it + // should be the ip-address of the interface you want the listener socket + // bound to. ``listen_on()`` returns the error code of the operation in + // ``ec``. If this indicates success, the session is listening on a port + // within the specified range. If it fails, it will also generate an + // appropriate alert (listen_failed_alert). + // + // If all ports in the specified range fails to be opened for listening, + // libtorrent will try to use port 0 (which tells the operating system to + // pick a port that's free). If that still fails you may see a + // listen_failed_alert with port 0 even if you didn't ask to listen on + // it. + // + // It is possible to prevent libtorrent from binding to port 0 by passing + // in the flag ``session::no_system_port`` in the ``flags`` argument. + // + // The interface parameter can also be a hostname that will resolve to + // the device you want to listen on. If you don't specify an interface, + // libtorrent may attempt to listen on multiple interfaces (typically + // 0.0.0.0 and ::). This means that if your IPv6 interface doesn't work, + // you may still see a listen_failed_alert, even though the IPv4 port + // succeeded. + // + // The ``flags`` parameter can either be 0 or + // ``session::listen_reuse_address``, which will set the reuse address + // socket option on the listen socket(s). By default, the listen socket + // does not use reuse address. If you're running a service that needs to + // run on a specific port no matter if it's in use, set this flag. + // + // If you're also starting the DHT, it is a good idea to do that after + // you've called ``listen_on()``, since the default listen port for the + // DHT is the same as the tcp listen socket. If you start the DHT first, + // it will assume the tcp port is free and open the udp socket on that + // port, then later, when ``listen_on()`` is called, it may turn out that + // the tcp port is in use. That results in the DHT and the bittorrent + // socket listening on different ports. If the DHT is active when + // ``listen_on`` is called, the udp port will be rebound to the new port, + // if it was configured to use the same port as the tcp socket, and if + // the listen_on call failed to bind to the same port that the udp uses. + // + // If you want the OS to pick a port for you, pass in 0 as both first and + // second. + // + // The reason why it's a good idea to run the DHT and the bittorrent + // socket on the same port is because that is an assumption that may be + // used to increase performance. One way to accelerate the connecting of + // peers on windows may be to first ping all peers with a DHT ping + // packet, and connect to those that responds first. On windows one can + // only connect to a few peers at a time because of a built in limitation + // (in XP Service pack 2). + void listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface = 0 + , int flags = 0); + unsigned short listen_port() const; + unsigned short ssl_listen_port() const; + bool is_listening() const; + + // if the listen port failed in some way you can retry to listen on + // another port- range with this function. If the listener succeeded and + // is currently listening, a call to this function will shut down the + // listen port and reopen it using these new properties (the given + // interface and port range). As usual, if the interface is left as 0 + // this function will return false on failure. If it fails, it will also + // generate alerts describing the error. It will return true on success. + enum listen_on_flags_t + { +#ifndef TORRENT_NO_DEPRECATE + // this is always on starting with 0.16.2 + listen_reuse_address = 0x01, +#endif + listen_no_system_port = 0x02 + }; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0 + , int flags = 0) TORRENT_DEPRECATED; +#endif + + // flags to be passed in to remove_torrent(). + enum options_t + { + // delete the files belonging to the torrent from disk. + delete_files = 1 + }; + + // flags to be passed in to the session constructor + enum session_flags_t + { + // this will add common extensions like ut_pex, ut_metadata, lt_tex + // smart_ban and possibly others. + add_default_plugins = 1, + + // this will start features like DHT, local service discovery, UPnP + // and NAT-PMP. + start_default_features = 2 + }; + + // ``remove_torrent()`` will close all peer connections associated with + // the torrent and tell the tracker that we've stopped participating in + // the swarm. This operation cannot fail. When it completes, you will + // receive a torrent_removed_alert. + // + // The optional second argument ``options`` can be used to delete all the + // files downloaded by this torrent. To do so, pass in the value + // ``session::delete_files``. The removal of the torrent is asyncronous, + // there is no guarantee that adding the same torrent immediately after + // it was removed will not throw a libtorrent_exception exception. Once + // the torrent is deleted, a torrent_deleted_alert is posted. + void remove_torrent(const torrent_handle& h, int options = 0); + + // Sets the session settings and the packet encryption settings + // respectively. See session_settings and pe_settings for more + // information on available options. + void set_settings(session_settings const& s); + session_settings settings() const; + void set_pe_settings(pe_settings const& settings); + pe_settings get_pe_settings() const; + + // These functions sets and queries the proxy settings to be used for the + // session. + // + // For more information on what settings are available for proxies, see + // proxy_settings. If the session is not in anonymous mode, proxies that + // aren't working or fail, will automatically be disabled and packets + // will flow without using any proxy. If you want to enforce using a + // proxy, even when the proxy doesn't work, enable anonymous_mode in + // session_settings. + void set_proxy(proxy_settings const& s); + proxy_settings proxy() const; + +#ifdef TORRENT_STATS + // internal + void enable_stats_logging(bool s); +#endif + + // ``set_i2p_proxy`` sets the i2p_ proxy, and tries to open a persistant + // connection to it. The only used fields in the proxy settings structs + // are ``hostname`` and ``port``. + // + // ``i2p_proxy`` returns the current i2p proxy in use. + // + // .. _i2p: http://www.i2p2.de + void set_i2p_proxy(proxy_settings const& s); + proxy_settings i2p_proxy() const; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + // Get the number of uploads. + TORRENT_DEPRECATED_PREFIX + int num_uploads() const TORRENT_DEPRECATED; + + // Get the number of connections. This number also contains the + // number of half open connections. + TORRENT_DEPRECATED_PREFIX + int num_connections() const TORRENT_DEPRECATED; + + // deprecated in 0.15. + TORRENT_DEPRECATED_PREFIX + void set_peer_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_web_seed_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_tracker_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + proxy_settings peer_proxy() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings web_seed_proxy() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings tracker_proxy() const TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + void set_dht_proxy(proxy_settings const& s) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + proxy_settings dht_proxy() const TORRENT_DEPRECATED; + + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX + int upload_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int download_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int local_upload_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int local_download_rate_limit() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int max_half_open_connections() const TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + void set_local_upload_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_local_download_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_upload_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_download_rate_limit(int bytes_per_second) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_uploads(int limit) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_connections(int limit) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_max_half_open_connections(int limit) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + int max_connections() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int max_uploads() const TORRENT_DEPRECATED; +#endif + + // ``pop_alert()`` is used to ask the session if any errors or events has + // occurred. With set_alert_mask() you can filter which alerts to receive + // through ``pop_alert()``. For information about the alert categories, + // see alerts_. + // + // ``pop_alerts()`` pops all pending alerts in a single call. In high + // performance environments with a very high alert churn rate, this can + // save significant amount of time compared to popping alerts one at a + // time. Each call requires one round-trip to the network thread. If + // alerts are produced in a higher rate than they can be popped (when + // popped one at a time) it's easy to get stuck in an infinite loop, + // trying to drain the alert queue. Popping the entire queue at once + // avoids this problem. + // + // However, the ``pop_alerts`` function comes with significantly more + // responsibility. You pass in an *empty* ``std::dequeue`` to it. + // If it's not empty, all elements in it will be deleted and then + // cleared. All currently pending alerts are returned by being swapped + // into the passed in container. The responsibility of deleting the + // alerts is transferred to the caller. This means you need to call + // delete for each item in the returned dequeue. It's probably a good + // idea to delete the alerts as you handle them, to save one extra pass + // over the dequeue. + // + // Alternatively, you can pass in the same container the next time you + // call ``pop_alerts``. + // + // ``wait_for_alert`` blocks until an alert is available, or for no more + // than ``max_wait`` time. If ``wait_for_alert`` returns because of the + // time-out, and no alerts are available, it returns 0. If at least one + // alert was generated, a pointer to that alert is returned. The alert is + // not popped, any subsequent calls to ``wait_for_alert`` will return the + // same pointer until the alert is popped by calling ``pop_alert``. This + // is useful for leaving any alert dispatching mechanism independent of + // this blocking call, the dispatcher can be called and it can pop the + // alert independently. + // + // .. note:: + // Although these functions are all thread-safe, popping alerts from + // multiple separate threads may introduce race conditions in that + // the thread issuing an asynchronous operation may not be the one + // receiving the alert with the result. + // + // In the python binding, ``wait_for_alert`` takes the number of + // milliseconds to wait as an integer. + // + // To control the max number of alerts that's queued by the session, see + // ``session_settings::alert_queue_size``. + // + // save_resume_data_alert and save_resume_data_failed_alert are always + // posted, regardelss of the alert mask. + std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); + alert const* wait_for_alert(time_duration max_wait); + +#ifndef TORRENT_NO_DEPRECATE + TORRENT_DEPRECATED_PREFIX + void set_severity_level(alert::severity_t s) TORRENT_DEPRECATED; + + TORRENT_DEPRECATED_PREFIX + size_t set_alert_queue_size_limit(size_t queue_size_limit_) TORRENT_DEPRECATED; +#endif + + // Changes the mask of which alerts to receive. By default only errors + // are reported. ``m`` is a bitmask where each bit represents a category + // of alerts. + // + // See category_t enum for options. + void set_alert_mask(boost::uint32_t m); + + // This sets a function to be called (from within libtorrent's netowrk + // thread) every time an alert is posted. Since the function (``fun``) is + // run in libtorrent's internal thread, it may not call any of + // libtorrent's external API functions. Doing so results in a dead lock. + // + // The main intention with this function is to support integration with + // platform-dependent message queues or signalling systems. For instance, + // on windows, one could post a message to an HNWD or on linux, write to + // a pipe or an eventfd. + void set_alert_dispatch(boost::function)> const& fun); + + // internal + connection_queue& get_connection_queue(); + + // Starts and stops Local Service Discovery. This service will broadcast + // the infohashes of all the non-private torrents on the local network to + // look for peers on the same swarm within multicast reach. + // + // It is turned off by default. + void start_lsd(); + void stop_lsd(); + + // Starts and stops the UPnP service. When started, the listen port and + // the DHT port are attempted to be forwarded on local UPnP router + // devices. + // + // The upnp object returned by ``start_upnp()`` can be used to add and + // remove arbitrary port mappings. Mapping status is returned through the + // portmap_alert and the portmap_error_alert. The object will be valid + // until ``stop_upnp()`` is called. See upnp-and-nat-pmp_. + // + // It is off by default. + void start_upnp(); + void stop_upnp(); + + // protocols used by add_port_mapping() + enum protocol_type { udp = 1, tcp = 2 }; + + // add_port_mapping adds a port forwarding on UPnP and/or NAT-PMP, + // whichever is enabled. The return value is a handle referring to the + // port mapping that was just created. Pass it to delete_port_mapping() + // to remove it. + int add_port_mapping(protocol_type t, int external_port, int local_port); + void delete_port_mapping(int handle); + + // Starts and stops the NAT-PMP service. When started, the listen port + // and the DHT port are attempted to be forwarded on the router through + // NAT-PMP. + // + // The natpmp object returned by ``start_natpmp()`` can be used to add + // and remove arbitrary port mappings. Mapping status is returned through + // the portmap_alert and the portmap_error_alert. The object will be + // valid until ``stop_natpmp()`` is called. See upnp-and-nat-pmp_. + // + // It is off by default. + void start_natpmp(); + void stop_natpmp(); + + private: + + void init(std::pair listen_range, char const* listen_interface + , fingerprint const& id, boost::uint32_t alert_mask); + void set_log_path(std::string const& p); + void start(int flags); + + // data shared between the main thread + // and the working thread + boost::shared_ptr m_impl; + }; + +} + +#endif // TORRENT_SESSION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp new file mode 100644 index 0000000000..e63c2b1e6c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session_settings.hpp @@ -0,0 +1,1569 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_SETTINGS_HPP_INCLUDED +#define TORRENT_SESSION_SETTINGS_HPP_INCLUDED + +#include "libtorrent/version.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/version.hpp" + +#include +#include + +namespace libtorrent +{ + + // The ``proxy_settings`` structs contains the information needed to + // direct certain traffic to a proxy. + struct TORRENT_EXPORT proxy_settings + { + // defaults constructs proxy settings, initializing it to the default + // settings. + proxy_settings() : type(none) + , port(0), proxy_hostnames(true) + , proxy_peer_connections(true) + {} + + // the name or IP of the proxy server. ``port`` is the port number the + // proxy listens to. If required, ``username`` and ``password`` can be + // set to authenticate with the proxy. + std::string hostname; + + // when using a proy type that requires authentication, the username + // and password fields must be set to the credentials for the proxy. + std::string username; + std::string password; + + // the type of proxy to use. Assign one of these to the + // proxy_settings::type field. + enum proxy_type + { + // This is the default, no proxy server is used, all other fields are + // ignored. + none, + + // The server is assumed to be a `SOCKS4 server`_ that requires a + // username. + // + // .. _`SOCKS4 server`: http://www.ufasoft.com/doc/socks4_protocol.htm + socks4, + + // The server is assumed to be a SOCKS5 server (`RFC 1928`_) that does + // not require any authentication. The username and password are + // ignored. + // + // .. _`RFC 1928`: http://www.faqs.org/rfcs/rfc1928.html + socks5, + + // The server is assumed to be a SOCKS5 server that supports plain + // text username and password authentication (`RFC 1929`_). The + // username and password specified may be sent to the proxy if it + // requires. + // + // .. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html + socks5_pw, + + // The server is assumed to be an HTTP proxy. If the transport used + // for the connection is non-HTTP, the server is assumed to support + // the CONNECT_ method. i.e. for web seeds and HTTP trackers, a plain + // proxy will suffice. The proxy is assumed to not require + // authorization. The username and password will not be used. + // + // .. _CONNECT: http://tools.ietf.org/html/draft-luotonen-web-proxy-tunneling-01 + http, + + // The server is assumed to be an HTTP proxy that requires user + // authorization. The username and password will be sent to the proxy. + http_pw, + + // route through a i2p SAM proxy + i2p_proxy + }; + + // tells libtorrent what kind of proxy server it is. See proxy_type + // enum for options + boost::uint8_t type; + + // the port the proxy server is running on + boost::uint16_t port; + + // defaults to true. It means that hostnames should be attempted to be + // resolved through the proxy instead of using the local DNS service. + // This is only supported by SOCKS5 and HTTP. + bool proxy_hostnames; + + // determines whether or not to excempt peer and web seed connections + // from using the proxy. This defaults to true, i.e. peer connections are + // proxied by default. + bool proxy_peer_connections; + }; + + // This holds most of the session-wide settings in libtorrent. Pass this + // to session::set_settings() to change the settings, initialize it from + // session::get_settings() to get the current settings. + struct TORRENT_EXPORT session_settings + { + // initializes the session_settings to the default settings. + session_settings(std::string const& user_agent = "libtorrent/" + LIBTORRENT_VERSION); + ~session_settings(); + + // automatically set to the libtorrent version you're using in order to + // be forward binary compatible. This field should not be changed. + int version; + + // the client identification to the tracker. The recommended format of + // this string is: "ClientName/ClientVersion + // libtorrent/libtorrentVersion". This name will not only be used when + // making HTTP requests, but also when sending extended headers to peers + // that support that extension. + std::string user_agent; + + // the number of seconds the tracker connection will wait from when it + // sent the request until it considers the tracker to have timed-out. + // Default value is 60 seconds. + int tracker_completion_timeout; + + // the number of seconds to wait to receive any data from the tracker. If + // no data is received for this number of seconds, the tracker will be + // considered as having timed out. If a tracker is down, this is the kind + // of timeout that will occur. The default value is 20 seconds. + int tracker_receive_timeout; + + // the time to wait when sending a stopped message before considering a + // tracker to have timed out. this is usually shorter, to make the client + // quit faster + // + // This is given in seconds. Default is 10 seconds. + int stop_tracker_timeout; + + // the maximum number of bytes in a tracker response. If a response size + // passes this number it will be rejected and the connection will be + // closed. On gzipped responses this size is measured on the uncompressed + // data. So, if you get 20 bytes of gzip response that'll expand to 2 + // megs, it will be interrupted before the entire response has been + // uncompressed (given your limit is lower than 2 megs). Default limit is + // 1 megabyte. + int tracker_maximum_response_length; + + // controls the number of seconds from a request is sent until it times + // out if no piece response is returned. + int piece_timeout; + + // the number of seconds one block (16kB) is expected to be received + // within. If it's not, the block is requested from a different peer + int request_timeout; + + // the length of the request queue given in the number of seconds it + // should take for the other end to send all the pieces. i.e. the actual + // number of requests depends on the download rate and this number. + int request_queue_time; + + // the number of outstanding block requests a peer is allowed to queue up + // in the client. If a peer sends more requests than this (before the + // first one has been sent) the last request will be dropped. the higher + // this is, the faster upload speeds the client can get to a single peer. + int max_allowed_in_request_queue; + + // the maximum number of outstanding requests to send to a peer. This + // limit takes precedence over request_queue_time. i.e. no matter the + // download speed, the number of outstanding requests will never exceed + // this limit. + int max_out_request_queue; + + // if a whole piece can be downloaded in this number of seconds, or less, + // the peer_connection will prefer to request whole pieces at a time from + // this peer. The benefit of this is to better utilize disk caches by + // doing localized accesses and also to make it easier to identify bad + // peers if a piece fails the hash check. + int whole_pieces_threshold; + + // the number of seconds to wait for any activity on the peer wire before + // closing the connectiong due to time out. This defaults to 120 seconds, + // since that's what's specified in the protocol specification. After + // half the time out, a keep alive message is sent. + int peer_timeout; + + // same as peer_timeout, but only applies to url-seeds. this is usually + // set lower, because web servers are expected to be more reliable. This + // value defaults to 20 seconds. + int urlseed_timeout; + + // controls the pipelining with the web server. When using persistent + // connections to HTTP 1.1 servers, the client is allowed to send more + // requests before the first response is received. This number controls + // the number of outstanding requests to use with url-seeds. Default is + // 5. + int urlseed_pipeline_size; + + // time to wait until a new retry takes place + int urlseed_wait_retry; + + // sets the upper limit on the total number of files this session will + // keep open. The reason why files are left open at all is that some anti + // virus software hooks on every file close, and scans the file for + // viruses. deferring the closing of the files will be the difference + // between a usable system and a completely hogged down system. Most + // operating systems also has a limit on the total number of file + // descriptors a process may have open. It is usually a good idea to find + // this limit and set the number of connections and the number of files + // limits so their sum is slightly below it. + int file_pool_size; + + // determines if connections from the same IP address as existing + // connections should be rejected or not. Multiple connections from the + // same IP address is not allowed by default, to prevent abusive behavior + // by peers. It may be useful to allow such connections in cases where + // simulations are run on the same machie, and all peers in a swarm has + // the same IP address. + bool allow_multiple_connections_per_ip; + + // the maximum times we try to connect to a peer before stop connecting + // again. If a peer succeeds, its failcounter is reset. If a peer is + // retrieved from a peer source (other than DHT) the failcount is + // decremented by one, allowing another try. + int max_failcount; + + // the number of seconds to wait to reconnect to a peer. this time is + // multiplied with the failcount. + int min_reconnect_time; + + // the number of seconds to wait after a connection attempt is initiated + // to a peer until it is considered as having timed out. The default is + // 10 seconds. This setting is especially important in case the number of + // half-open connections are limited, since stale half-open connection + // may delay the connection of other peers considerably. + int peer_connect_timeout; + + // if set to true, upload, download and unchoke limits are ignored for + // peers on the local network. + bool ignore_limits_on_local_network; + + // the number of connection attempts that are made per second. If a + // number < 0 is specified, it will default to 200 connections per + // second. If 0 is specified, it means don't make outgoing connections at + // all. + int connection_speed; + + // if this is set to true, have messages will be sent to peers that + // already have the piece. This is typically not necessary, but it might + // be necessary for collecting statistics in some cases. Default is + // false. + bool send_redundant_have; + + // prevents outgoing bitfields from being full. If the client is seed, a + // few bits will be set to 0, and later filled in with have-messages. + // This is an old attempt to prevent certain ISPs from stopping people + // from seeding. + bool lazy_bitfields; + + // if a peer is uninteresting and uninterested for longer than this + // number of seconds, it will be disconnected. default is 10 minutes + int inactivity_timeout; + + // the number of seconds between chokes/unchokes. On this interval, peers + // are re-evaluated for being choked/unchoked. This is defined as 30 + // seconds in the protocol, and it should be significantly longer than + // what it takes for TCP to ramp up to it's max rate. + int unchoke_interval; + + // the number of seconds between each *optimistic* unchoke. On this + // timer, the currently optimistically unchoked peer will change. + int optimistic_unchoke_interval; + + // the ip address passed along to trackers as the ``&ip=`` parameter. If + // left as the default (an empty string), that parameter is omitted. Most + // trackers ignore this argument. This is here for completeness for + // edge-cases where it may be useful. + std::string announce_ip; + + // the number of peers we want from each tracker request. It defines what + // is sent as the ``&num_want=`` parameter to the tracker. Stopped + // messages always send num_want=0. This setting control what to say in + // the case where we actually want peers. + int num_want; + + // specifies the number of pieces we need before we switch to rarest + // first picking. This defaults to 4, which means the 4 first pieces in + // any torrent are picked at random, the following pieces are picked in + // rarest first order. + int initial_picker_threshold; + + // the number of allowed pieces to send to choked peers that supports the + // fast extensions + int allowed_fast_set_size; + + // options for session_settings::suggest_mode. + enum suggest_mode_t + { + // the default. will not send out suggest messages. + no_piece_suggestions = 0, + + // send out suggest messages for the most recent pieces that are in + // the read cache. + suggest_read_cache = 1 + }; + + // this determines which pieces will be suggested to peers suggest read + // cache will make libtorrent suggest pieces that are fresh in the disk + // read cache, to potentially lower disk access and increase the cache + // hit ratio + // + // for options, see suggest_mode_t. + int suggest_mode; + + // the maximum number of bytes a connection may have pending in the disk + // write queue before its download rate is being throttled. This prevents + // fast downloads to slow medias to allocate more memory indefinitely. + // This should be set to at least 16 kB to not completely disrupt normal + // downloads. If it's set to 0, you will be starving the disk thread and + // nothing will be written to disk. this is a per session setting. + // + // When this limit is reached, the peer connections will stop reading + // data from their sockets, until the disk thread catches up. Setting + // this too low will severly limit your download rate. + int max_queued_disk_bytes; + + // this is the low watermark for the disk buffer queue. whenever the + // number of queued bytes exceed the max_queued_disk_bytes, libtorrent + // will wait for it to drop below this value before issuing more reads + // from the sockets. If set to 0, the low watermark will be half of the + // max queued disk bytes + int max_queued_disk_bytes_low_watermark; + + // the number of seconds to wait for a handshake response from a peer. If + // no response is received within this time, the peer is disconnected. + int handshake_timeout; + + // determines how the DHT is used. If this is true, the DHT will only be + // used for torrents where all trackers in its tracker list has failed. + // Either by an explicit error message or a time out. This is false by + // default, which means the DHT is used by default regardless of if the + // trackers fail or not. + bool use_dht_as_fallback; + + // determines whether or not the torrent's piece hashes are kept in + // memory after the torrent becomes a seed or not. If it is set to + // ``true`` the hashes are freed once the torrent is a seed (they're not + // needed anymore since the torrent won't download anything more). If + // it's set to false they are not freed. If they are freed, the + // torrent_info returned by get_torrent_info() will return an object that + // may be incomplete, that cannot be passed back to async_add_torrent() + // and add_torrent() for instance. + bool free_torrent_hashes; + + // indicates whether or not the UPnP implementation should ignore any + // broadcast response from a device whose address is not the configured + // router for this machine. i.e. it's a way to not talk to other people's + // routers by mistake. + bool upnp_ignore_nonrouters; + + // This is the minimum send buffer target size (send buffer includes + // bytes pending being read from disk). For good and snappy seeding + // performance, set this fairly high, to at least fit a few blocks. This + // is essentially the initial window size which will determine how fast + // we can ramp up the send rate + int send_buffer_low_watermark; + + // the upper limit of the send buffer low-watermark. + // + // if the send buffer has fewer bytes than this, we'll read another 16kB + // block onto it. If set too small, upload rate capacity will suffer. If + // set too high, memory will be wasted. The actual watermark may be lower + // than this in case the upload rate is low, this is the upper limit. + int send_buffer_watermark; + + // the current upload rate to a peer is multiplied by this factor to get + // the send buffer watermark. The factor is specified as a percentage. + // i.e. 50 indicates a factor of 0.5. + // + // This product is clamped to the send_buffer_watermark setting to not + // exceed the max. For high speed upload, this should be set to a greater + // value than 100. The default is 50. + // + // For high capacity connections, setting this higher can improve upload + // performance and disk throughput. Setting it too high may waste RAM and + // create a bias towards read jobs over write jobs. + int send_buffer_watermark_factor; + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 defaults to true. When true, if there is a global + // upload limit set and the current upload rate is less than 90% of that, + // another upload slot is opened. If the upload rate has been saturated + // for an extended period of time, on upload slot is closed. The number + // of upload slots will never be less than what has been set by + // ``session::set_max_uploads()``. To query the current number of upload + // slots, see ``session_status::allowed_upload_slots``. + bool auto_upload_slots; + + // When set, and ``auto_upload_slots`` is set, the max upload slots + // setting is used as a minimum number of unchoked slots. This algorithm + // is designed to prevent the peer from spreading its upload capacity too + // thin, but still open more slots in order to utilize the full capacity. + bool auto_upload_slots_rate_based; +#endif + + // the different choking algorithms available. Set + // session_settings::choking_algorithm to one of these + enum choking_algorithm_t + { + // the traditional choker with a fixed number of unchoke slots, as + // specified by session::set_max_uploads().. + fixed_slots_choker, + + // opens at least the number of slots as specified by + // session::set_max_uploads() but opens up more slots if the upload + // capacity is not saturated. This unchoker will work just like the + // ``fixed_slot_choker`` if there's no global upload rate limit set. + auto_expand_choker, + + // opens up unchoke slots based on the upload rate achieved to peers. + // The more slots that are opened, the marginal upload rate required + // to open up another slot increases. + rate_based_choker, + // attempts to optimize download rate by finding the reciprocation + // rate of each peer individually and prefers peers that gives the + // highest *return on investment*. It still allocates all upload + // capacity, but shuffles it around to the best peers first. For this + // choker to be efficient, you need to set a global upload rate limit + // session_settings::upload_rate_limit. For more information about + // this choker, see the paper_. + // + // .. _paper: http://bittyrant.cs.washington.edu/#papers + bittyrant_choker + }; + + // specifies which algorithm to use to determine which peers to unchoke. + // This setting replaces the deprecated settings ``auto_upload_slots`` + // and ``auto_upload_slots_rate_based``. For options, see + // choking_algorithm_t. + int choking_algorithm; + + // the different choking algorithms available when seeding. Set + // session_settings::seed_choking_algorithm to one of these + enum seed_choking_algorithm_t + { + // round-robins the peers that are unchoked when seeding. This + // distributes the upload bandwidht uniformly and fairly. It minimizes + // the ability for a peer to download everything without + // redistributing it. + round_robin, + + // unchokes the peers we can send to the fastest. This might be a bit + // more reliable in utilizing all available capacity. + fastest_upload, + + // prioritizes peers who have just started or are just about to finish + // the download. The intention is to force peers in the middle of the + // download to trade with each other. + anti_leech + }; + + // controls the seeding unchoke behavior. For options, see + // seed_choking_algorithm_t. + int seed_choking_algorithm; + + // specifies if parole mode should be used. Parole mode means that peers + // that participate in pieces that fail the hash check are put in a mode + // where they are only allowed to download whole pieces. If the whole + // piece a peer in parole mode fails the hash check, it is banned. If a + // peer participates in a piece that passes the hash check, it is taken + // out of parole mode. + bool use_parole_mode; + + // the disk write and read cache. It is specified in units of 16 KiB + // blocks. Buffers that are part of a peer's send or receive buffer also + // count against this limit. Send and receive buffers will never be + // denied to be allocated, but they will cause the actual cached blocks + // to be flushed or evicted. If this is set to -1, the cache size is + // automatically set to the amount of physical RAM available in the + // machine divided by 8. If the amount of physical RAM cannot be + // determined, it's set to 1024 (= 16 MiB). + // + // Disk buffers are allocated using a pool allocator, the number of + // blocks that are allocated at a time when the pool needs to grow can be + // specified in ``cache_buffer_chunk_size``. This defaults to 16 blocks. + // Lower numbers saves memory at the expense of more heap allocations. It + // must be at least 1. + int cache_size; + + // this is the number of disk buffer blocks (16 kiB) that should be + // allocated at a time. It must be at least 1. Lower number saves memory + // at the expense of more heap allocations + int cache_buffer_chunk_size; + + // the number of seconds a write cache entry sits idle in the cache + // before it's forcefully flushed to disk. + int cache_expiry; + + // when set to true (default), the disk cache is also used to cache + // pieces read from disk. Blocks for writing pieces takes presedence. + bool use_read_cache; + + // defaults to 0. If set to something greater than 0, the disk read cache + // will not be evicted by cache misses and will explicitly be controlled + // based on the rarity of pieces. Rare pieces are more likely to be + // cached. This would typically be used together with ``suggest_mode`` + // set to ``suggest_read_cache``. The value is the number of pieces to + // keep in the read cache. If the actual read cache can't fit as many, it + // will essentially be clamped. + bool explicit_read_cache; + + // the number of seconds in between each refresh of a part of the + // explicit read cache. Torrents take turns in refreshing and this is the + // time in between each torrent refresh. Refreshing a torrent's explicit + // read cache means scanning all pieces and picking a random set of the + // rarest ones. There is an affinity to pick pieces that are already in + // the cache, so that subsequent refreshes only swaps in pieces that are + // rarer than whatever is in the cache at the time. + int explicit_cache_interval; + + // the buffer modes to use for reading and writing. Set + // session_settings::disk_io_read_mode and disk_io_write_mode to one of + // these. + enum io_buffer_mode_t + { + // This is the default and files are opened normally, with the OS + // caching reads and writes. + enable_os_cache = 0, + // This will open files in unbuffered mode for files where every read + // and write would be sector aligned. Using aligned disk offsets is a + // requirement on some operating systems. + disable_os_cache_for_aligned_files = 1, + // This opens all files in unbuffered mode (if allowed by the + // operating system). Linux and Windows, for instance, require disk + // offsets to be sector aligned, and in those cases, this option is + // the same as ``disable_os_caches_for_aligned_files``. + disable_os_cache = 2 + }; + + // determines how files are opened when they're in read only mode versus + // read and write mode. For options, see io_buffer_mode_t. + // + // One reason to disable caching is that it may help the operating system + // from growing its file cache indefinitely. Since some OSes only allow + // aligned files to be opened in unbuffered mode, It is recommended to + // make the largest file in a torrent the first file (with offset 0) or + // use pad files to align all files to piece boundries. + int disk_io_write_mode; + int disk_io_read_mode; + + // when set to true, instead of issuing multiple adjacent reads or writes + // to the disk, allocate a larger buffer, copy all writes into it and + // issue a single write. For reads, read into a larger buffer and copy + // the buffer into the smaller individual read buffers afterwards. This + // may save system calls, but will cost in additional memory allocation + // and copying. + bool coalesce_reads; + bool coalesce_writes; + + // if set to something other than (0, 0) is a range of ports used to bind + // outgoing sockets to. This may be useful for users whose router allows + // them to assign QoS classes to traffic based on its local port. It is a + // range instead of a single port because of the problems with failing to + // reconnect to peers if a previous socket to that peer and port is in + // ``TIME_WAIT`` state. + // + //.. warning:: + // setting outgoing ports will limit the ability to keep multiple + // connections to the same client, even for different torrents. It is not + // recommended to change this setting. Its main purpose is to use as an + // escape hatch for cheap routers with QoS capability but can only + // classify flows based on port numbers. + std::pair outgoing_ports; + + // determines the TOS byte set in the IP header of every packet sent to + // peers (including web seeds). The default value for this is ``0x0`` (no + // marking). One potentially useful TOS mark is ``0x20``, this represents + // the *QBone scavenger service*. For more details, see QBSS_. + // + // .. _`QBSS`: http://qbone.internet2.edu/qbss/ + char peer_tos; + + // for auto managed torrents, these are the limits they are subject to. + // If there are too many torrents some of the auto managed ones will be + // paused until some slots free up. + // + // ``active_dht_limit`` and ``active_tracker_limit`` limits the number of + // torrents that will be active on the DHT and their tracker. If the + // active limit is set higher than these numbers, some torrents will be + // "active" in the sense that they will accept incoming connections, but + // not announce on the DHT or their trackers. + // + // ``active_lsd_limit`` is the max number of torrents to announce to the + // local network over the local service discovery protocol. By default + // this is 80, which is no more than one announce every 5 seconds + // (assuming the default announce interval of 5 minutes). + // + // ``active_limit`` is a hard limit on the number of active torrents. + // This applies even to slow torrents. + // + // You can have more torrents *active*, even though they are not + // announced to the DHT, lsd or their tracker. If some peer knows about + // you for any reason and tries to connect, it will still be accepted, + // unless the torrent is paused, which means it won't accept any + // connections. + // + // ``active_downloads`` and ``active_seeds`` controls how many active + // seeding and downloading torrents the queuing mechanism allows. The + // target number of active torrents is ``min(active_downloads + + // active_seeds, active_limit)``. ``active_downloads`` and + // ``active_seeds`` are upper limits on the number of downloading + // torrents and seeding torrents respectively. Setting the value to -1 + // means unlimited. + // + // For example if there are 10 seeding torrents and 10 downloading + // torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4, + // there will be 4 seeds active and 4 downloading torrents. If the + // settings are ``active_downloads`` = 2 and ``active_seeds`` = 4, then + // there will be 2 downloading torrents and 4 seeding torrents active. + // Torrents that are not auto managed are also counted against these + // limits. If there are non-auto managed torrents that use up all the + // slots, no auto managed torrent will be activated. + int active_downloads; + int active_seeds; + int active_dht_limit; + int active_tracker_limit; + int active_lsd_limit; + int active_limit; + + // prefer seeding torrents when determining which torrents to give active + // slots to, the default is false which gives preference to downloading + // torrents + bool auto_manage_prefer_seeds; + + // if true, torrents without any payload transfers are not subject to the + // ``active_seeds`` and ``active_downloads`` limits. This is intended to + // make it more likely to utilize all available bandwidth, and avoid + // having torrents that don't transfer anything block the active slots. + bool dont_count_slow_torrents; + + // the number of seconds in between recalculating which torrents to + // activate and which ones to queue + int auto_manage_interval; + + // when a seeding torrent reaches either the share ratio (bytes up / + // bytes down) or the seed time ratio (seconds as seed / seconds as + // downloader) or the seed time limit (seconds as seed) it is considered + // done, and it will leave room for other torrents the default value for + // share ratio is 2 the default seed time ratio is 7, because that's a + // common asymmetry ratio on connections + // + //.. note:: + // This is an out-dated option that doesn't make much sense. It will be + // removed in future versions of libtorrent + float share_ratio_limit; + + // the seeding time / downloading time ratio limit for considering a + // seeding torrent to have met the seed limit criteria. See queuing_. + float seed_time_ratio_limit; + + // the limit on the time a torrent has been an active seed (specified in + // seconds) before it is considered having met the seed limit criteria. + // See queuing_. + int seed_time_limit; + + // controls a feature where libtorrent periodically can disconnect the + // least useful peers in the hope of connecting to better ones. + // ``peer_turnover_interval`` controls the interval of this optimistic + // disconnect. It defaults to every 5 minutes, and is specified in + // seconds. + // + // ``peer_turnover`` Is the fraction of the peers that are disconnected. + // This is a float where 1.f represents all peers an 0 represents no + // peers. It defaults to 4% (i.e. 0.04f) + // + // ``peer_turnover_cutoff`` is the cut off trigger for optimistic + // unchokes. If a torrent has more than this fraction of its connection + // limit, the optimistic unchoke is triggered. This defaults to 90% (i.e. + // 0.9f). + int peer_turnover_interval; + float peer_turnover; + float peer_turnover_cutoff; + + // specifies whether libtorrent should close connections where both ends + // have no utility in keeping the connection open. For instance if both + // ends have completed their downloads, there's no point in keeping it + // open. This defaults to ``true``. + bool close_redundant_connections; + + // the number of seconds between scrapes of queued torrents (auto managed + // and paused torrents). Auto managed torrents that are paused, are + // scraped regularly in order to keep track of their downloader/seed + // ratio. This ratio is used to determine which torrents to seed and + // which to pause. + int auto_scrape_interval; + + // the minimum number of seconds between any automatic scrape (regardless + // of torrent). In case there are a large number of paused auto managed + // torrents, this puts a limit on how often a scrape request is sent. + int auto_scrape_min_interval; + + // the maximum number of peers in the list of known peers. These peers + // are not necessarily connected, so this number should be much greater + // than the maximum number of connected peers. Peers are evicted from the + // cache when the list grows passed 90% of this limit, and once the size + // hits the limit, peers are no longer added to the list. If this limit + // is set to 0, there is no limit on how many peers we'll keep in the + // peer list. + int max_peerlist_size; + + // the max peer list size used for torrents that are paused. This default + // to the same as ``max_peerlist_size``, but can be used to save memory + // for paused torrents, since it's not as important for them to keep a + // large peer list. + int max_paused_peerlist_size; + + // the minimum allowed announce interval for a tracker. This is specified + // in seconds, defaults to 5 minutes and is used as a sanity check on + // what is returned from a tracker. It mitigates hammering misconfigured + // trackers. + int min_announce_interval; + + // If true, partial pieces are picked before pieces that are more rare. + // If false, rare pieces are always prioritized, unless the number of + // partial pieces is growing out of proportion. + bool prioritize_partial_pieces; + + // the number of seconds a torrent is considered active after it was + // started, regardless of upload and download speed. This is so that + // newly started torrents are not considered inactive until they have a + // fair chance to start downloading. + int auto_manage_startup; + + // if set to true, the estimated TCP/IP overhead is drained from the rate + // limiters, to avoid exceeding the limits with the total traffic + bool rate_limit_ip_overhead; + + // controls how multi tracker torrents are treated. If this is set to + // true, all trackers in the same tier are announced to in parallel. If + // all trackers in tier 0 fails, all trackers in tier 1 are announced as + // well. If it's set to false, the behavior is as defined by the multi + // tracker specification. It defaults to false, which is the same + // behavior previous versions of libtorrent has had as well. + bool announce_to_all_trackers; + + // controls how multi tracker torrents are treated. When this is set to + // true, one tracker from each tier is announced to. This is the uTorrent + // behavior. This is false by default in order to comply with the + // multi-tracker specification. + bool announce_to_all_tiers; + + // true by default. It means that trackers may be rearranged in a way + // that udp trackers are always tried before http trackers for the same + // hostname. Setting this to fails means that the trackers' tier is + // respected and there's no preference of one protocol over another. + bool prefer_udp_trackers; + + // when this is set to true, a piece has to have been forwarded to a + // third peer before another one is handed out. This is the traditional + // definition of super seeding. + bool strict_super_seeding; + + // the number of pieces to send to a peer, when seeding, before rotating + // in another peer to the unchoke set. It defaults to 3 pieces, which + // means that when seeding, any peer we've sent more than this number of + // pieces to will be unchoked in favour of a choked peer. + int seeding_piece_quota; + + // is a limit of the number of *sparse regions* in a torrent. A sparse + // region is defined as a hole of pieces we have not yet downloaded, in + // between pieces that have been downloaded. This is used as a hack for + // windows vista which has a bug where you cannot write files with more + // than a certain number of sparse regions. This limit is not hard, it + // will be exceeded. Once it's exceeded, pieces that will maintain or + // decrease the number of sparse regions are prioritized. To disable this + // functionality, set this to 0. It defaults to 0 on all platforms except + // windows. + int max_sparse_regions; + + // if lock disk cache is set to true the disk cache that's in use, will + // be locked in physical memory, preventing it from being swapped out. + bool lock_disk_cache; + + // the number of piece requests we will reject in a row while a peer is + // choked before the peer is considered abusive and is disconnected. + int max_rejects; + + // specifies the buffer sizes set on peer sockets. 0 (which is the + // default) means the OS default (i.e. don't change the buffer sizes). + // The socket buffer sizes are changed using setsockopt() with + // SOL_SOCKET/SO_RCVBUF and SO_SNDBUFFER. + int recv_socket_buffer_size; + int send_socket_buffer_size; + + // chooses between two ways of reading back piece data from disk when its + // complete and needs to be verified against the piece hash. This happens + // if some blocks were flushed to the disk out of order. Everything that + // is flushed in order is hashed as it goes along. Optimizing for speed + // will allocate space to fit all the the remaingin, unhashed, part of + // the piece, reads the data into it in a single call and hashes it. This + // is the default. If ``optimizing_hashing_for_speed`` is false, a single + // block will be allocated (16 kB), and the unhashed parts of the piece + // are read, one at a time, and hashed in this single block. This is + // appropriate on systems that are memory constrained. + bool optimize_hashing_for_speed; + + // the number of milliseconds to sleep + // in between disk read operations when checking torrents. This defaults + // to 0, but can be set to higher numbers to slow down the rate at which + // data is read from the disk while checking. This may be useful for + // background tasks that doesn't matter if they take a bit longer, as long + // as they leave disk I/O time for other processes. + int file_checks_delay_per_block; + + // the disk cache algorithms available. Set + // session_settings::disk_cache_algorithm to one of these. + enum disk_cache_algo_t + { + // This flushes the entire piece, in the write cache, that was least + // recently written to. + lru, + + // will flush the largest sequences of contiguous blocks from the + // write cache, regarless of the piece's last use time. + largest_contiguous, + + // will prioritize flushing blocks that will avoid having to read them + // back in to verify the hash of the piece once it's done. This is + // especially useful for high throughput setups, where reading from + // the disk is especially expensive. + avoid_readback + }; + + // tells the disk I/O thread which cache flush algorithm to use. + // This is specified by the disk_cache_algo_t enum. + disk_cache_algo_t disk_cache_algorithm; + + // the number of blocks to read into the read cache when a read cache + // miss occurs. Setting this to 0 is essentially the same thing as + // disabling read cache. The number of blocks read into the read cache is + // always capped by the piece boundry. + // + // When a piece in the write cache has ``write_cache_line_size`` + // contiguous blocks in it, they will be flushed. Setting this to 1 + // effectively disables the write cache. + int read_cache_line_size; + + // whenever a contiguous range of this many blocks is found in the write + // cache, it is flushed immediately + int write_cache_line_size; + + // the number of seconds from a disk write errors occur on a torrent + // until libtorrent will take it out of the upload mode, to test if the + // error condition has been fixed. + // + // libtorrent will only do this automatically for auto managed torrents. + // + // You can explicitly take a torrent out of upload only mode using + // set_upload_mode(). + int optimistic_disk_retry; + + // controls if downloaded pieces are verified against the piece hashes in + // the torrent file or not. The default is false, i.e. to verify all + // downloaded data. It may be useful to turn this off for performance + // profiling and simulation scenarios. Do not disable the hash check for + // regular bittorrent clients. + bool disable_hash_checks; + + // if this is true, disk read operations may be re-ordered based on their + // physical disk read offset. This greatly improves throughput when + // uploading to many peers. This assumes a traditional hard drive with a + // read head and spinning platters. If your storage medium is a solid + // state drive, this optimization doesn't give you an benefits + bool allow_reordered_disk_operations; + + // if this is true, i2p torrents are allowed to also get peers from other + // sources than the tracker, and connect to regular IPs, not providing + // any anonymization. This may be useful if the user is not interested in + // the anonymization of i2p, but still wants to be able to connect to i2p + // peers. + bool allow_i2p_mixed; + + // the max number of suggested piece indices received from a peer that's + // remembered. If a peer floods suggest messages, this limit prevents + // libtorrent from using too much RAM. It defaults to 10. + int max_suggest_pieces; + + // If set to true (it defaults to false), piece requests that have been + // skipped enough times when piece messages are received, will be + // considered lost. Requests are considered skipped when the returned + // piece messages are re-ordered compared to the order of the requests. + // This was an attempt to get out of dead-locks caused by BitComet peers + // silently ignoring some requests. It may cause problems at high rates, + // and high level of reordering in the uploading peer, that's why it's + // disabled by default. + bool drop_skipped_requests; + + // determines if the disk I/O should use a normal + // or low priority policy. This defaults to true, which means that + // it's low priority by default. Other processes doing disk I/O will + // normally take priority in this mode. This is meant to improve the + // overall responsiveness of the system while downloading in the + // background. For high-performance server setups, this might not + // be desirable. + bool low_prio_disk; + + // the time between local + // network announces for a torrent. By default, when local service + // discovery is enabled a torrent announces itself every 5 minutes. + // This interval is specified in seconds. + int local_service_announce_interval; + + // the number of seconds between announcing + // torrents to the distributed hash table (DHT). This is specified to + // be 15 minutes which is its default. + int dht_announce_interval; + + // the number of seconds libtorrent + // will keep UDP tracker connection tokens around for. This is specified + // to be 60 seconds, and defaults to that. The higher this value is, the + // fewer packets have to be sent to the UDP tracker. In order for higher + // values to work, the tracker needs to be configured to match the + // expiration time for tokens. + int udp_tracker_token_expiry; + + // if this is set to true, read cache blocks + // that are hit by peer read requests are removed from the disk cache + // to free up more space. This is useful if you don't expect the disk + // cache to create any cache hits from other peers than the one who + // triggered the cache line to be read into the cache in the first place. + bool volatile_read_cache; + + // enables the disk cache to adjust the size + // of a cache line generated by peers to depend on the upload rate + // you are sending to that peer. The intention is to optimize the RAM + // usage of the cache, to read ahead further for peers that you're + // sending faster to. + bool guided_read_cache; + + // the minimum number of seconds any read cache line is kept in the + // cache. This defaults to one second but may be greater if + // ``guided_read_cache`` is enabled. Having a lower bound on the time a + // cache line stays in the cache is an attempt to avoid swapping the same + // pieces in and out of the cache in case there is a shortage of spare + // cache space. + int default_cache_min_age; + + // the number of optimistic unchoke slots to use. It defaults to 0, which + // means automatic. Having a higher number of optimistic unchoke slots + // mean you will find the good peers faster but with the trade-off to use + // up more bandwidth. When this is set to 0, libtorrent opens up 20% of + // your allowed upload slots as optimistic unchoke slots. + int num_optimistic_unchoke_slots; + + // this is a linux-only option and passes in the ``O_NOATIME`` to + // ``open()`` when opening files. This may lead to some disk performance + // improvements. + bool no_atime_storage; + + // the assumed reciprocation rate from peers when using the BitTyrant + // choker. This defaults to 14 kiB/s. If set too high, you will + // over-estimate your peers and be more altruistic while finding the true + // reciprocation rate, if it's set too low, you'll be too stingy and + // waste finding the true reciprocation rate. + int default_est_reciprocation_rate; + + // specifies how many percent the extimated reciprocation rate should be + // increased by each unchoke interval a peer is still choking us back. + // This defaults to 20%. This only applies to the BitTyrant choker. + int increase_est_reciprocation_rate; + + // specifies how many percent the estimated reciprocation rate should be + // decreased by each unchoke interval a peer unchokes us. This default to + // 3%. This only applies to the BitTyrant choker. + int decrease_est_reciprocation_rate; + + // defaults to false. If a torrent has been paused by the auto managed + // feature in libtorrent, i.e. the torrent is paused and auto managed, + // this feature affects whether or not it is automatically started on an + // incoming connection. The main reason to queue torrents, is not to make + // them unavailable, but to save on the overhead of announcing to the + // trackers, the DHT and to avoid spreading one's unchoke slots too thin. + // If a peer managed to find us, even though we're no in the torrent + // anymore, this setting can make us start the torrent and serve it. + bool incoming_starts_queued_torrents; + + // when set to true, the downloaded counter sent to trackers will include + // the actual number of payload bytes donwnloaded including redundant + // bytes. If set to false, it will not include any redundany bytes + bool report_true_downloaded; + + // defaults to true, and controls when a block may be requested twice. If + // this is ``true``, a block may only be requested twice when there's ay + // least one request to every piece that's left to download in the + // torrent. This may slow down progress on some pieces sometimes, but it + // may also avoid downloading a lot of redundant bytes. If this is + // ``false``, libtorrent attempts to use each peer connection to its max, + // by always requesting something, even if it means requesting something + // that has been requested from another peer already. + bool strict_end_game_mode; + + // if set to true, the local peer discovery (or Local Service Discovery) + // will not only use IP multicast, but also broadcast its messages. This + // can be useful when running on networks that don't support multicast. + // Since broadcast messages might be expensive and disruptive on + // networks, only every 8th announce uses broadcast. + bool broadcast_lsd; + + // these all determines if libtorrent should attempt to make outgoing + // connections of the specific type, or allow incoming connection. By + // default all of them are enabled. + bool enable_outgoing_utp; + bool enable_incoming_utp; + bool enable_outgoing_tcp; + bool enable_incoming_tcp; + + // the max number of peers we accept from pex messages from a single peer. + // this limits the number of concurrent peers any of our peers claims to + // be connected to. If they clain to be connected to more than this, we'll + // ignore any peer that exceeds this limit + int max_pex_peers; + + // determines if the storage, when loading resume data files, should + // verify that the file modification time with the timestamps in the + // resume data. This defaults to false, which means timestamps are taken + // into account, and resume data is less likely to accepted (torrents are + // more likely to be fully checked when loaded). It might be useful to + // set this to true if your network is faster than your disk, and it + // would be faster to redownload potentially missed pieces than to go + // through the whole storage to look for them. + bool ignore_resume_timestamps; + + // determines if the storage should check the whole files when resume + // data is incomplete or missing or whether it should simply assume we + // don't have any of the data. By default, this is determined by the + // existance of any of the files. By setting this setting to true, the + // files won't be checked, but will go straight to download mode. + bool no_recheck_incomplete_resume; + + // defaults to false. When set to true, the client tries to hide its + // identity to a certain degree. The peer-ID will no longer include the + // client's fingerprint. The user-agent will be reset to an empty string. + // It will also try to not leak other identifying information, such as + // your local listen port, your IP etc. + // + // If you're using I2P, a VPN or a proxy, it might make sense to enable + // anonymous mode. + bool anonymous_mode; + + // disables any communication that's not going over a proxy. Enabling + // this requires a proxy to be configured as well, see + // ``set_proxy_settings``. The listen sockets are closed, and incoming + // connections will only be accepted through a SOCKS5 or I2P proxy (if a + // peer proxy is set up and is run on the same machine as the tracker + // proxy). This setting also disabled peer country lookups, since those + // are done via DNS lookups that aren't supported by proxies. + bool force_proxy; + + // specifies the number of milliseconds between internal ticks. This is + // the frequency with which bandwidth quota is distributed to peers. It + // should not be more than one second (i.e. 1000 ms). Setting this to a + // low value (around 100) means higher resolution bandwidth quota + // distribution, setting it to a higher value saves CPU cycles. + int tick_interval; + + // specifies whether downloads from web seeds is reported to the + // tracker or not. Defaults to on + bool report_web_seed_downloads; + + // specifies the target share ratio for share mode torrents. This + // defaults to 3, meaning we'll try to upload 3 times as much as we + // download. Setting this very high, will make it very conservative and + // you might end up not downloading anything ever (and not affecting your + // share ratio). It does not make any sense to set this any lower than 2. + // For instance, if only 3 peers need to download the rarest piece, it's + // impossible to download a single piece and upload it more than 3 times. + // If the share_mode_target is set to more than 3, nothing is downloaded. + int share_mode_target; + + // sets the session-global limits of upload and download rate limits, in + // bytes per second. The local rates refer to peers on the local network. + // By default peers on the local network are not rate limited. + // + // These rate limits are only used for local peers (peers within the same + // subnet as the client itself) and it is only used when + // ``session_settings::ignore_limits_on_local_network`` is set to true + // (which it is by default). These rate limits default to unthrottled, + // but can be useful in case you want to treat local peers + // preferentially, but not quite unthrottled. + // + // A value of 0 means unlimited. + int upload_rate_limit; + int download_rate_limit; + int local_upload_rate_limit; + int local_download_rate_limit; + + // sets the rate limit on the DHT. This is specified in bytes per second + // and defaults to 4000. For busy boxes with lots of torrents that + // requires more DHT traffic, this should be raised. + int dht_upload_rate_limit; + + // the max number of unchoked peers in the session. The number of unchoke + // slots may be ignored depending on what ``choking_algorithm`` is set + // to. A value of -1 means infinite. + int unchoke_slots_limit; + + // sets the maximum number of half-open connections libtorrent will have + // when connecting to peers. A half-open connection is one where + // connect() has been called, but the connection still hasn't been + // established (nor failed). Windows XP Service Pack 2 sets a default, + // system wide, limit of the number of half-open connections to 10. So, + // this limit can be used to work nicer together with other network + // applications on that system. The default is to have no limit, and + // passing -1 as the limit, means to have no limit. When limiting the + // number of simultaneous connection attempts, peers will be put in a + // queue waiting for their turn to get connected. + int half_open_limit; + + // sets a global limit on the number of connections opened. The number of + // connections is set to a hard minimum of at least two per torrent, so + // if you set a too low connections limit, and open too many torrents, + // the limit will not be met. + int connections_limit; + + // the number of extra incoming connections allowed temporarily, in order + // to support replacing peers + int connections_slack; + + // the target delay for uTP sockets in milliseconds. A high value will + // make uTP connections more aggressive and cause longer queues in the + // upload bottleneck. It cannot be too low, since the noise in the + // measurements would cause it to send too slow. The default is 50 + // milliseconds. + int utp_target_delay; + + // the number of bytes the uTP congestion window can increase at the most + // in one RTT. This defaults to 300 bytes. If this is set too high, the + // congestion controller reacts too hard to noise and will not be stable, + // if it's set too low, it will react slow to congestion and not back off + // as fast. + int utp_gain_factor; + + // the shortest allowed uTP socket timeout, specified in milliseconds. + // This defaults to 500 milliseconds. The timeout depends on the RTT of + // the connection, but is never smaller than this value. A connection + // times out when every packet in a window is lost, or when a packet is + // lost twice in a row (i.e. the resent packet is lost as well). + // + // The shorter the timeout is, the faster the connection will recover + // from this situation, assuming the RTT is low enough. + int utp_min_timeout; + + // the number of SYN packets that are sent (and timed out) before + // giving up and closing the socket. + int utp_syn_resends; + + // the number of resent packets sent on a closed socket before giving up + int utp_fin_resends; + + // the number of times a packet is sent (and lossed or timed out) + // before giving up and closing the connection. + int utp_num_resends; + + // the number of milliseconds of timeout for the initial SYN packet for + // uTP connections. For each timed out packet (in a row), the timeout is + // doubled. + int utp_connect_timeout; + +#ifndef TORRENT_NO_DEPRECATE + // number of milliseconds of delaying ACKing packets the most + int utp_delayed_ack; +#endif + + // controls if the uTP socket manager is allowed to increase the socket + // buffer if a network interface with a large MTU is used (such as + // loopback or ethernet jumbo frames). This defaults to true and might + // improve uTP throughput. For RAM constrained systems, disabling this + // typically saves around 30kB in user space and probably around 400kB in + // kernel socket buffers (it adjusts the send and receive buffer size on + // the kernel socket, both for IPv4 and IPv6). + bool utp_dynamic_sock_buf; + + // controls how the congestion window is changed when a packet loss is + // experienced. It's specified as a percentage multiplier for ``cwnd``. + // By default it's set to 50 (i.e. cut in half). Do not change this value + // unless you know what you're doing. Never set it higher than 100. + int utp_loss_multiplier; + + // the options for session_settings::mixed_mode_algorithm. + enum bandwidth_mixed_algo_t + { + // disables the mixed mode bandwidth balancing + prefer_tcp = 0, + + // does not throttle uTP, throttles TCP to the same proportion + // of throughput as there are TCP connections + peer_proportional = 1 + }; + + // determines how to treat TCP connections when there are uTP + // connections. Since uTP is designed to yield to TCP, there's an + // inherent problem when using swarms that have both TCP and uTP + // connections. If nothing is done, uTP connections would often be + // starved out for bandwidth by the TCP connections. This mode is + // ``prefer_tcp``. The ``peer_proportional`` mode simply looks at the + // current throughput and rate limits all TCP connections to their + // proportional share based on how many of the connections are TCP. This + // works best if uTP connections are not rate limited by the global rate + // limiter, see rate_limit_utp. + // + // see bandwidth_mixed_algo_t for options. + int mixed_mode_algorithm; + + // determines if uTP connections should be throttled by the global rate + // limiter or not. By default they are. + bool rate_limit_utp; + + // the value passed in to listen() for the listen socket. It is the + // number of outstanding incoming connections to queue up while we're not + // actively waiting for a connection to be accepted. The default is 5 + // which should be sufficient for any normal client. If this is a high + // performance server which expects to receive a lot of connections, or + // used in a simulator or test, it might make sense to raise this number. + // It will not take affect until listen_on() is called again (or for the + // first time). + int listen_queue_size; + + // if true, the ``&ip=`` argument in tracker requests (unless otherwise + // specified) will be set to the intermediate IP address, if the user is + // double NATed. If ther user is not double NATed, this option has no + // affect. + bool announce_double_nat; + + // the number of peers to try to connect to immediately when the first + // tracker response is received for a torrent. This is a boost to given + // to new torrents to accelerate them starting up. The normal connect + // scheduler is run once every second, this allows peers to be connected + // immediately instead of waiting for the session tick to trigger + // connections. + int torrent_connect_boost; + + // determines if seeding (and finished) torrents should attempt to make + // outgoing connections or not. By default this is true. It may be set to + // false in very specific applications where the cost of making outgoing + // connections is high, and there are no or small benefits of doing so. + // For instance, if no nodes are behind a firewall or a NAT, seeds don't + // need to make outgoing connections. + bool seeding_outgoing_connections; + + // if true (which is the default), libtorrent will not connect to any + // peers on priviliged ports (<= 1023). This can mitigate using + // bittorrent swarms for certain DDoS attacks. + bool no_connect_privileged_ports; + + // the maximum number of alerts queued up internally. If alerts are not + // popped, the queue will eventually fill up to this level. This defaults + // to 1000. + int alert_queue_size; + + // the maximum allowed size (in bytes) to be received + // by the metadata extension, i.e. magnet links. It defaults to 1 MiB. + int max_metadata_size; + + // true by default, which means the number of connection attempts per + // second may be limited to below the ``connection_speed``, in case we're + // close to bump up against the limit of number of connections. The + // intention of this setting is to more evenly distribute our connection + // attempts over time, instead of attempting to connectin in batches, and + // timing them out in batches. + bool smooth_connects; + + // defaults to false. When set to true, web connections will include a + // user-agent with every request, as opposed to just the first request in + // a connection. + bool always_send_user_agent; + + // defaults to true. It determines whether the IP filter applies to + // trackers as well as peers. If this is set to false, trackers are + // exempt from the IP filter (if there is one). If no IP filter is set, + // this setting is irrelevant. + bool apply_ip_filter_to_trackers; + + // used to avoid starvation of read jobs in the disk I/O thread. By + // default, read jobs are deferred, sorted by physical disk location and + // serviced once all write jobs have been issued. In scenarios where the + // download rate is enough to saturate the disk, there's a risk the read + // jobs will never be serviced. With this setting, every *x* write job, + // issued in a row, will instead pick one read job off of the sorted + // queue, where *x* is ``read_job_every``. + int read_job_every; + + // defaults to true and will attempt to optimize disk reads by giving the + // operating system heads up of disk read requests as they are queued in + // the disk job queue. This gives a significant performance boost for + // seeding. + bool use_disk_read_ahead; + + // determines whether or not to lock files which libtorrent is + // downloading to or seeding from. This is implemented using + // ``fcntl(F_SETLK)`` on unix systems and by not passing in + // ``SHARE_READ`` and ``SHARE_WRITE`` on windows. This might prevent 3rd + // party processes from corrupting the files under libtorrent's feet. + bool lock_files; + + // sets the listen port for SSL connections. If this is set to 0, no SSL + // listen port is opened. Otherwise a socket is opened on this port. This + // setting is only taken into account when opening the regular listen + // port, and won't re-open the listen socket simply by changing this + // setting. + // + // if this is 0, outgoing SSL connections are disabled + // + // It defaults to port 4433. + int ssl_listen; + + // ``tracker_backoff`` determines how aggressively to back off from + // retrying failing trackers. This value determines *x* in the following + // formula, determining the number of seconds to wait until the next + // retry: + // + // delay = 5 + 5 * x / 100 * fails^2 + // + // It defaults to 250. + // + // This setting may be useful to make libtorrent more or less aggressive + // in hitting trackers. + // + int tracker_backoff; + + // enables banning web seeds. By default, web seeds that send corrupt + // data are banned. + bool ban_web_seeds; + + // specifies the max number of bytes to receive into RAM buffers when + // downloading stuff over HTTP. Specifically when specifying a URL to a + // .torrent file when adding a torrent or when announcing to an HTTP + // tracker. The default is 2 MiB. + int max_http_recv_buffer_size; + + // enables or disables the share mode extension. This is enabled by + // default. + bool support_share_mode; + + // enables or disables the merkle tree torrent support. This is enabled + // by default. + bool support_merkle_torrents; + + // enables or disables reporting redundant bytes to the tracker. This is + // enabled by default. + bool report_redundant_bytes; + + // the version string to advertise for this client in the peer protocol + // handshake. If this is empty the user_agent is used + std::string handshake_client_version; + + // if this is true, the disk cache uses a pool allocator for disk cache + // blocks. Enabling this improves performance of the disk cache with the + // side effect that the disk cache is less likely and slower at returning + // memory to the kernel when cache pressure is low. + bool use_disk_cache_pool; + + // the download and upload rate limits for a torrent to be considered + // active by the queuing mechanism. A torrent whose download rate is less + // than ``inactive_down_rate`` and whose upload rate is less than + // ``inactive_up_rate`` for ``auto_manage_startup`` seconds, is + // considered inactive, and another queued torrent may be startert. + // This logic is disabled if ``dont_count_slow_torrents`` is false. + int inactive_down_rate; + int inactive_up_rate; + }; + + // structure used to hold configuration options for the DHT + // + // The ``dht_settings`` struct used to contain a ``service_port`` member to + // control which port the DHT would listen on and send messages from. This + // field is deprecated and ignored. libtorrent always tries to open the UDP + // socket on the same port as the TCP socket. + struct TORRENT_EXPORT dht_settings + { + // initialized dht_settings to the default values + dht_settings() + : max_peers_reply(100) + , search_branching(5) +#ifndef TORRENT_NO_DEPRECATE + , service_port(0) +#endif + , max_fail_count(20) + , max_torrents(2000) + , max_dht_items(700) + , max_torrent_search_reply(20) + , restrict_routing_ips(true) + , restrict_search_ips(true) + , extended_routing_table(true) + , aggressive_lookups(true) + , privacy_lookups(false) + , enforce_node_id(false) + , ignore_dark_internet(true) + {} + + // the maximum number of peers to send in a reply to ``get_peers`` + int max_peers_reply; + + // the number of concurrent search request the node will send when + // announcing and refreshing the routing table. This parameter is called + // alpha in the kademlia paper + int search_branching; + +#ifndef TORRENT_NO_DEPRECATE + // the listen port for the dht. This is a UDP port. zero means use the + // same as the tcp interface + int service_port; +#endif + + // the maximum number of failed tries to contact a node before it is + // removed from the routing table. If there are known working nodes that + // are ready to replace a failing node, it will be replaced immediately, + // this limit is only used to clear out nodes that don't have any node + // that can replace them. + int max_fail_count; + + // the total number of torrents to track from the DHT. This is simply an + // upper limit to make sure malicious DHT nodes cannot make us allocate + // an unbounded amount of memory. + int max_torrents; + + // max number of items the DHT will store + int max_dht_items; + + // the max number of torrents to return in a torrent search query to the + // DHT + int max_torrent_search_reply; + + // determines if the routing table entries should restrict entries to one + // per IP. This defaults to true, which helps mitigate some attacks on + // the DHT. It prevents adding multiple nodes with IPs with a very close + // CIDR distance. + // + // when set, nodes whose IP address that's in the same /24 (or /64 for + // IPv6) range in the same routing table bucket. This is an attempt to + // mitigate node ID spoofing attacks also restrict any IP to only have a + // single entry in the whole routing table + bool restrict_routing_ips; + + // determines if DHT searches should prevent adding nodes with IPs with + // very close CIDR distance. This also defaults to true and helps + // mitigate certain attacks on the DHT. + bool restrict_search_ips; + + // makes the first buckets in the DHT routing table fit 128, 64, 32 and + // 16 nodes respectively, as opposed to the standard size of 8. All other + // buckets have size 8 still. + bool extended_routing_table; + + // slightly changes the lookup behavior in terms of how many outstanding + // requests we keep. Instead of having branch factor be a hard limit, we + // always keep *branch factor* outstanding requests to the closest nodes. + // i.e. every time we get results back with closer nodes, we query them + // right away. It lowers the lookup times at the cost of more outstanding + // queries. + bool aggressive_lookups; + + // when set, perform lookups in a way that is slightly more expensive, + // but which minimizes the amount of information leaked about you. + bool privacy_lookups; + + // when set, node's whose IDs that are not correctly generated based on + // its external IP are ignored. When a query arrives from such node, an + // error message is returned with a message saying "invalid node ID". + bool enforce_node_id; + + // ignore DHT messages from parts of the internet we wouldn't expect to + // see any traffic from + bool ignore_dark_internet; + }; + + + // The ``pe_settings`` structure is used to control the settings related + // to peer protocol encryption. + struct TORRENT_EXPORT pe_settings + { + // initializes the encryption settings with the default vaues + pe_settings() + : out_enc_policy(enabled) + , in_enc_policy(enabled) + , allowed_enc_level(both) + , prefer_rc4(false) + {} + + // the encoding policy options for use with pe_settings::out_enc_policy + // and pe_settings::in_enc_policy. + enum enc_policy + { + // Only encrypted connections are allowed. Incoming connections that + // are not encrypted are closed and if the encrypted outgoing + // connection fails, a non-encrypted retry will not be made. + forced, + + // encrypted connections are enabled, but non-encrypted connections + // are allowed. An incoming non-encrypted connection will be accepted, + // and if an outgoing encrypted connection fails, a non- encrypted + // connection will be tried. + enabled, + + // only non-encrypted connections are allowed. + disabled + }; + + // the encryption levels, to be used with pe_settings::allowed_enc_level. + enum enc_level + { + // use only plaintext encryption + plaintext = 1, + // use only rc4 encryption + rc4 = 2, + // allow both + both = 3 + }; + + // control the settings for incoming + // and outgoing connections respectively. + // see enc_policy enum for the available options. + boost::uint8_t out_enc_policy; + boost::uint8_t in_enc_policy; + + // determines the encryption level of the + // connections. This setting will adjust which encryption scheme is + // offered to the other peer, as well as which encryption scheme is + // selected by the client. See enc_level enum for options. + boost::uint8_t allowed_enc_level; + + // if the allowed encryption level is both, setting this to + // true will prefer rc4 if both methods are offered, plaintext + // otherwise + bool prefer_rc4; + }; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp new file mode 100644 index 0000000000..71d964e5a3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/session_status.hpp @@ -0,0 +1,265 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_STATUS_HPP_INCLUDED +#define TORRENT_SESSION_STATUS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/size_type.hpp" +#include + +namespace libtorrent +{ + + // holds statistics about a current dht_lookup operation. + // a DHT lookup is the travesal of nodes, looking up a + // set of target nodes in the DHT for retrieving and possibly + // storing information in the DHT + struct TORRENT_EXPORT dht_lookup + { + // string literal indicating which kind of lookup this is + char const* type; + + // the number of outstanding request to individual nodes + // this lookup has right now + int outstanding_requests; + + // the total number of requests that have timed out so far + // for this lookup + int timeouts; + + // the total number of responses we have received for this + // lookup so far for this lookup + int responses; + + // the branch factor for this lookup. This is the number of + // nodes we keep outstanding requests to in parallel by default. + // when nodes time out we may increase this. + int branch_factor; + + // the number of nodes left that could be queries for this + // lookup. Many of these are likely to be part of the trail + // while performing the lookup and would never end up actually + // being queried. + int nodes_left; + + // the number of seconds ago the + // last message was sent that's still + // outstanding + int last_sent; + + // the number of outstanding requests + // that have exceeded the short timeout + // and are considered timed out in the + // sense that they increased the branch + // factor + int first_timeout; + }; + + // holds dht routing table stats + struct TORRENT_EXPORT dht_routing_bucket + { + // the total number of nodes and replacement nodes + // in the routing table + int num_nodes; + int num_replacements; + +#ifndef TORRENT_NO_DEPRECATE + // number of seconds since last activity + int last_active; +#endif + }; + + // holds counters and gauges for the uTP sockets + struct TORRENT_EXPORT utp_status + { + // gauges. These are snapshots of the number of + // uTP sockets in each respective state + int num_idle; + int num_syn_sent; + int num_connected; + int num_fin_sent; + int num_close_wait; + + // counters. These are monotonically increasing + // and cumulative counters for their respective event. + boost::uint64_t packet_loss; + boost::uint64_t timeout; + boost::uint64_t packets_in; + boost::uint64_t packets_out; + boost::uint64_t fast_retransmit; + boost::uint64_t packet_resend; + boost::uint64_t samples_above_target; + boost::uint64_t samples_below_target; + boost::uint64_t payload_pkts_in; + boost::uint64_t payload_pkts_out; + boost::uint64_t invalid_pkts_in; + boost::uint64_t redundant_pkts_in; + }; + + // contains session wide state and counters + struct TORRENT_EXPORT session_status + { + // false as long as no incoming connections have been + // established on the listening socket. Every time you change the listen port, this will + // be reset to false. + bool has_incoming_connections; + + // the total download and upload rates accumulated + // from all torrents. This includes bittorrent protocol, DHT and an estimated TCP/IP + // protocol overhead. + int upload_rate; + int download_rate; + + // the total number of bytes downloaded and + // uploaded to and from all torrents. This also includes all the protocol overhead. + size_type total_download; + size_type total_upload; + + // the rate of the payload + // down- and upload only. + int payload_upload_rate; + int payload_download_rate; + + // the total transfers of payload + // only. The payload does not include the bittorrent protocol overhead, but only parts of the + // actual files to be downloaded. + size_type total_payload_download; + size_type total_payload_upload; + + // the estimated TCP/IP overhead in each direction. + int ip_overhead_upload_rate; + int ip_overhead_download_rate; + size_type total_ip_overhead_download; + size_type total_ip_overhead_upload; + + // the upload and download rate used by DHT traffic. Also the total number + // of bytes sent and received to and from the DHT. + int dht_upload_rate; + int dht_download_rate; + size_type total_dht_download; + size_type total_dht_upload; + + // the upload and download rate used by tracker traffic. Also the total number + // of bytes sent and received to and from trackers. + int tracker_upload_rate; + int tracker_download_rate; + size_type total_tracker_download; + size_type total_tracker_upload; + + // the number of bytes that has been received more than once. + // This can happen if a request from a peer times out and is requested from a different + // peer, and then received again from the first one. To make this lower, increase the + // ``request_timeout`` and the ``piece_timeout`` in the session settings. + size_type total_redundant_bytes; + + // the number of bytes that was downloaded which later failed + // the hash-check. + size_type total_failed_bytes; + + // the total number of peer connections this session has. This includes + // incoming connections that still hasn't sent their handshake or outgoing connections + // that still hasn't completed the TCP connection. This number may be slightly higher + // than the sum of all peers of all torrents because the incoming connections may not + // be assigned a torrent yet. + int num_peers; + + // the current number of unchoked peers. + int num_unchoked; + + // the current allowed number of unchoked peers. + int allowed_upload_slots; + + // the number of peers that are + // waiting for more bandwidth quota from the torrent rate limiter. + int up_bandwidth_queue; + int down_bandwidth_queue; + + // count the number of + // bytes the connections are waiting for to be able to send and receive. + int up_bandwidth_bytes_queue; + int down_bandwidth_bytes_queue; + + // tells the number of + // seconds until the next optimistic unchoke change and the start of the next + // unchoke interval. These numbers may be reset prematurely if a peer that is + // unchoked disconnects or becomes notinterested. + int optimistic_unchoke_counter; + int unchoke_counter; + + // the number of peers currently + // waiting on a disk write or disk read to complete before it receives or sends + // any more data on the socket. It'a a metric of how disk bound you are. + int disk_write_queue; + int disk_read_queue; + + // only available when + // built with DHT support. They are all set to 0 if the DHT isn't running. When + // the DHT is running, ``dht_nodes`` is set to the number of nodes in the routing + // table. This number only includes *active* nodes, not cache nodes. The + // ``dht_node_cache`` is set to the number of nodes in the node cache. These nodes + // are used to replace the regular nodes in the routing table in case any of them + // becomes unresponsive. + int dht_nodes; + int dht_node_cache; + + // the number of torrents tracked by the DHT at the moment. + int dht_torrents; + + // an estimation of the total number of nodes in the DHT + // network. + size_type dht_global_nodes; + + // a vector of the currently running DHT lookups. + std::vector active_requests; + + // contains information about every bucket in the DHT routing + // table. + std::vector dht_routing_table; + + // the number of nodes allocated dynamically for a + // particular DHT lookup. This represents roughly the amount of memory used + // by the DHT. + int dht_total_allocations; + + // statistics on the uTP sockets. + utp_status utp_stats; + + // the number of known peers across all torrents. These are not necessarily + // connected peers, just peers we know of. + int peerlist_size; + }; + +} + +#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp new file mode 100644 index 0000000000..3d58e4ba09 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/settings.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef SETTINGS_HPP_INCLUDED +#define SETTINGS_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" + +namespace libtorrent +{ + struct lazy_entry; + class entry; + + // internal + enum struct_field_type_t + { + std_string, character, integer, floating_point, + boolean, size_integer, time_integer, integer16 + }; + + // this is used to map struct entries + // to names in a bencoded dictionary to + // save and load the struct + struct bencode_map_entry + { + char const* name; + int offset; // struct offset + int type; + }; + + void load_struct(lazy_entry const& e, void* s, bencode_map_entry const* m, int num); + void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num, void const* def = 0); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp new file mode 100644 index 0000000000..5be33db42a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/sha1_hash.hpp @@ -0,0 +1,307 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SHA1_HASH_HPP_INCLUDED +#define TORRENT_SHA1_HASH_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#if TORRENT_USE_IOSTREAM +#include "libtorrent/escape_string.hpp" // to_hex, from_hex +#include +#include +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +namespace libtorrent +{ + + // This type holds a SHA-1 digest or any other kind of 20 byte + // sequence. It implements a number of convenience functions, such + // as bit operations, comparison operators etc. + // + // In libtorrent it is primarily used to hold info-hashes, piece-hashes, + // peer IDs, node IDs etc. + class TORRENT_EXPORT sha1_hash + { + enum { number_size = 20 }; + public: + // the number of bytes of the number + static const int size = number_size; + + // constructs an all-sero sha1-hash + sha1_hash() { clear(); } + + // returns an all-F sha1-hash. i.e. the maximum value + // representable by a 160 bit number (20 bytes). This is + // a static member function. + static sha1_hash max() + { + sha1_hash ret; + memset(ret.m_number, 0xff, size); + return ret; + } + + // returns an all-zero sha1-hash. i.e. the minimum value + // representable by a 160 bit number (20 bytes). This is + // a static member function. + static sha1_hash min() + { + sha1_hash ret; + memset(ret.m_number, 0, size); + return ret; + } + + // copies 20 bytes from the pointer provided, into the sha1-hash. + // The passed in string MUST be at least 20 bytes. NULL terminators + // are ignored, ``s`` is treated like a raw memory buffer. + explicit sha1_hash(char const* s) + { + if (s == 0) clear(); + else std::memcpy(m_number, s, size); + } + explicit sha1_hash(std::string const& s) + { + TORRENT_ASSERT(s.size() >= 20); + int sl = int(s.size()) < size ? int(s.size()) : size; + std::memcpy(m_number, s.c_str(), sl); + } + void assign(std::string const& s) + { + TORRENT_ASSERT(s.size() >= 20); + int sl = int(s.size()) < size ? int(s.size()) : size; + std::memcpy(m_number, s.c_str(), sl); + } + void assign(char const* str) { std::memcpy(m_number, str, size); } + + // set the sha1-hash to all zeroes. + void clear() { std::memset(m_number, 0, number_size); } + + // return true if the sha1-hash is all zero. + bool is_all_zeros() const + { + for (const unsigned char* i = m_number; i < m_number+number_size; ++i) + if (*i != 0) return false; + return true; + } + + // shift left ``n`` bits. + sha1_hash& operator<<=(int n) + { + TORRENT_ASSERT(n >= 0); + int num_bytes = n / 8; + if (num_bytes >= number_size) + { + std::memset(m_number, 0, number_size); + return *this; + } + + if (num_bytes > 0) + { + std::memmove(m_number, m_number + num_bytes, number_size - num_bytes); + std::memset(m_number + number_size - num_bytes, 0, num_bytes); + n -= num_bytes * 8; + } + if (n > 0) + { + for (int i = 0; i < number_size - 1; ++i) + { + m_number[i] <<= n; + m_number[i] |= m_number[i+1] >> (8 - n); + } + m_number[number_size-1] <<= n; + } + return *this; + } + + // shift r ``n`` bits. + sha1_hash& operator>>=(int n) + { + TORRENT_ASSERT(n >= 0); + int num_bytes = n / 8; + if (num_bytes >= number_size) + { + std::memset(m_number, 0, number_size); + return *this; + } + if (num_bytes > 0) + { + std::memmove(m_number + num_bytes, m_number, number_size - num_bytes); + std::memset(m_number, 0, num_bytes); + n -= num_bytes * 8; + } + if (n > 0) + { + for (int i = number_size - 1; i > 0; --i) + { + m_number[i] >>= n; + m_number[i] |= (m_number[i-1] << (8 - n)) & 0xff; + } + m_number[0] >>= n; + } + return *this; + } + + // standard comparison operators + bool operator==(sha1_hash const& n) const + { + return std::equal(n.m_number, n.m_number+number_size, m_number); + } + bool operator!=(sha1_hash const& n) const + { + return !std::equal(n.m_number, n.m_number+number_size, m_number); + } + bool operator<(sha1_hash const& n) const + { + for (int i = 0; i < number_size; ++i) + { + if (m_number[i] < n.m_number[i]) return true; + if (m_number[i] > n.m_number[i]) return false; + } + return false; + } + + // returns a bit-wise negated copy of the sha1-hash + sha1_hash operator~() + { + sha1_hash ret; + for (int i = 0; i< number_size; ++i) + ret.m_number[i] = ~m_number[i]; + return ret; + } + + // returns the bit-wise XOR of the two sha1-hashes. + sha1_hash operator^(sha1_hash const& n) const + { + sha1_hash ret = *this; + ret ^= n; + return ret; + } + + // in-place bit-wise XOR with the passed in sha1_hash. + sha1_hash& operator^=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] ^= n.m_number[i]; + return *this; + } + + // returns the bit-wise AND of the two sha1-hashes. + sha1_hash operator&(sha1_hash const& n) const + { + sha1_hash ret = *this; + ret &= n; + return ret; + } + + // in-place bit-wise AND of the passed in sha1_hash + sha1_hash& operator&=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] &= n.m_number[i]; + return *this; + } + + // in-place bit-wise OR of the two sha1-hash. + sha1_hash& operator|=(sha1_hash const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] |= n.m_number[i]; + return *this; + } + + // accessors for specific bytes + unsigned char& operator[](int i) + { TORRENT_ASSERT(i >= 0 && i < number_size); return m_number[i]; } + unsigned char const& operator[](int i) const + { TORRENT_ASSERT(i >= 0 && i < number_size); return m_number[i]; } + + typedef const unsigned char* const_iterator; + typedef unsigned char* iterator; + + // start and end iterators for the hash. The value type + // of these iterators is ``unsigned char``. + const_iterator begin() const { return m_number; } + const_iterator end() const { return m_number+number_size; } + iterator begin() { return m_number; } + iterator end() { return m_number+number_size; } + + // return a copy of the 20 bytes representing the sha1-hash as a std::string. + // It's still a binary string with 20 binary characters. + std::string to_string() const + { return std::string((char const*)&m_number[0], number_size); } + + private: + + unsigned char m_number[number_size]; + + }; + + typedef sha1_hash peer_id; + +#if TORRENT_USE_IOSTREAM + + // print a sha1_hash object to an ostream as 40 hexadecimal digits + inline std::ostream& operator<<(std::ostream& os, sha1_hash const& peer) + { + char out[41]; + to_hex((char const*)&peer[0], sha1_hash::size, out); + return os << out; + } + + // read 40 hexadecimal digits from an istream into a sha1_hash + inline std::istream& operator>>(std::istream& is, sha1_hash& peer) + { + char hex[40]; + is.read(hex, 40); + if (!from_hex(hex, 40, (char*)&peer[0])) + is.setstate(std::ios_base::failbit); + return is; + } +#endif // TORRENT_USE_IOSTREAM +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp new file mode 100644 index 0000000000..f4238c7d54 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/size_type.hpp @@ -0,0 +1,53 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SIZE_TYPE_HPP_INCLUDED +#define TORRENT_SIZE_TYPE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + typedef boost::int64_t size_type; + typedef boost::uint64_t unsigned_size_type; +} + + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp new file mode 100644 index 0000000000..7db7f33457 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/sliding_average.hpp @@ -0,0 +1,114 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SLIDING_AVERAGE_HPP_INCLUDED +#define TORRENT_SLIDING_AVERAGE_HPP_INCLUDED + +#include +#include // for std::abs + +namespace libtorrent +{ + +// an exponential moving average accumulator. Add samples to it and it keeps +// track of a moving mean value and an average deviation +template +struct sliding_average +{ + sliding_average(): m_mean(0), m_average_deviation(0), m_num_samples(0) {} + + void add_sample(int s) + { + // fixed point + s *= 64; + int deviation; + + if (m_num_samples > 0) + deviation = std::abs(m_mean - s); + + if (m_num_samples < inverted_gain) + ++m_num_samples; + + m_mean += (s - m_mean) / m_num_samples; + + if (m_num_samples > 1) { + // the the exact same thing for deviation off the mean except -1 on + // the samples, because the number of deviation samples always lags + // behind by 1 (you need to actual samples to have a single deviation + // sample). + m_average_deviation += (deviation - m_average_deviation) / (m_num_samples - 1); + } + } + + int mean() const { return m_num_samples > 0 ? (m_mean + 32) / 64 : 0; } + int avg_deviation() const { return m_num_samples > 1 ? (m_average_deviation + 32) / 64 : 0; } + +private: + // both of these are fixed point values (* 64) + int m_mean; + int m_average_deviation; + // the number of samples we have received, but no more than inverted_gain + // this is the effective inverted_gain + int m_num_samples; +}; + +struct average_accumulator +{ + average_accumulator() + : m_num_samples(0) + , m_sample_sum(0) + {} + + void add_sample(int s) + { + ++m_num_samples; + m_sample_sum += s; + } + + int mean() + { + int ret; + if (m_num_samples == 0) ret = 0; + else ret = int(m_sample_sum / m_num_samples); + m_num_samples = 0; + m_sample_sum = 0; + return ret; + } + + int m_num_samples; + boost::uint64_t m_sample_sum; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp new file mode 100644 index 0000000000..f063d3b83c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket.hpp @@ -0,0 +1,209 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_HPP_INCLUDED +#define TORRENT_SOCKET_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +// if building as Objective C++, asio's template +// parameters Protocol has to be renamed to avoid +// colliding with keywords + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#include + +#if BOOST_VERSION < 103500 +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#ifdef __OBJC__ +#undef Protocol +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + +#if BOOST_VERSION < 103500 + using ::asio::ip::tcp; + using ::asio::ip::udp; + using ::asio::async_write; + using ::asio::async_read; + + typedef ::asio::ip::tcp::socket stream_socket; + typedef ::asio::ip::udp::socket datagram_socket; + typedef ::asio::ip::tcp::acceptor socket_acceptor; +#else + using boost::asio::ip::tcp; + using boost::asio::ip::udp; + using boost::asio::async_write; + using boost::asio::async_read; + + typedef boost::asio::ip::tcp::socket stream_socket; + typedef boost::asio::ip::udp::socket datagram_socket; + typedef boost::asio::ip::tcp::acceptor socket_acceptor; + + namespace asio = boost::asio; +#endif + +#if TORRENT_USE_IPV6 +#ifdef IPV6_V6ONLY + struct v6only + { + v6only(bool enable): m_value(enable) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_V6ONLY; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif +#endif + +#ifdef TORRENT_WINDOWS + +#ifndef IPV6_PROTECTION_LEVEL +#define IPV6_PROTECTION_LEVEL 30 +#endif + struct v6_protection_level + { + v6_protection_level(int level): m_value(level) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_PROTECTION_LEVEL; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif + +#ifdef IPV6_TCLASS + struct traffic_class + { + traffic_class(char val): m_value(val) {} + template + int level(Protocol const&) const { return IPPROTO_IPV6; } + template + int name(Protocol const&) const { return IPV6_TCLASS; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif + + struct type_of_service + { +#ifdef WIN32 + typedef DWORD tos_t; +#else + typedef int tos_t; +#endif + type_of_service(char val): m_value(val) {} + template + int level(Protocol const&) const { return IPPROTO_IP; } + template + int name(Protocol const&) const { return IP_TOS; } + template + tos_t const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + tos_t m_value; + }; + +#if defined IP_DONTFRAG || defined IP_MTU_DISCOVER || defined IP_DONTFRAGMENT +#define TORRENT_HAS_DONT_FRAGMENT +#endif + +#ifdef TORRENT_HAS_DONT_FRAGMENT + struct dont_fragment + { + dont_fragment(bool val) +#ifdef IP_PMTUDISCOVER_DO + : m_value(val ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT) {} +#else + : m_value(val) {} +#endif + template + int level(Protocol const&) const { return IPPROTO_IP; } + template + int name(Protocol const&) const +#if defined IP_DONTFRAG + { return IP_DONTFRAG; } +#elif defined IP_MTU_DISCOVER + { return IP_MTU_DISCOVER; } +#elif defined IP_DONTFRAGMENT + { return IP_DONTFRAGMENT; } +#else + {} +#endif + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif // TORRENT_HAS_DONT_FRAGMENT +} + +#endif // TORRENT_SOCKET_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp new file mode 100644 index 0000000000..6b11d4b7e7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_io.hpp @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_IO_HPP_INCLUDED +#define TORRENT_SOCKET_IO_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/peer_id.hpp" // for sha1_hash +#include + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT std::string print_address(address const& addr); + TORRENT_EXTRA_EXPORT std::string print_endpoint(tcp::endpoint const& ep); + TORRENT_EXTRA_EXPORT std::string print_endpoint(udp::endpoint const& ep); + TORRENT_EXTRA_EXPORT std::string address_to_bytes(address const& a); + // internal + TORRENT_EXPORT std::string endpoint_to_bytes(udp::endpoint const& ep); + TORRENT_EXTRA_EXPORT void hash_address(address const& ip, sha1_hash& h); + + namespace detail + { + template + void write_address(address const& a, OutIt& out) + { +#if TORRENT_USE_IPV6 + if (a.is_v4()) + { +#endif + write_uint32(a.to_v4().to_ulong(), out); +#if TORRENT_USE_IPV6 + } + else if (a.is_v6()) + { + typedef address_v6::bytes_type bytes_t; + bytes_t bytes = a.to_v6().to_bytes(); + for (bytes_t::iterator i = bytes.begin() + , end(bytes.end()); i != end; ++i) + write_uint8(*i, out); + } +#endif + } + + template + address read_v4_address(InIt& in) + { + unsigned long ip = read_uint32(in); + return address_v4(ip); + } + +#if TORRENT_USE_IPV6 + template + address read_v6_address(InIt& in) + { + typedef address_v6::bytes_type bytes_t; + bytes_t bytes; + for (bytes_t::iterator i = bytes.begin() + , end(bytes.end()); i != end; ++i) + *i = read_uint8(in); + return address_v6(bytes); + } +#endif + + template + void write_endpoint(Endpoint const& e, OutIt& out) + { + write_address(e.address(), out); + write_uint16(e.port(), out); + } + + template + Endpoint read_v4_endpoint(InIt& in) + { + address addr = read_v4_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } + +#if TORRENT_USE_IPV6 + template + Endpoint read_v6_endpoint(InIt& in) + { + address addr = read_v6_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } +#endif + + template + void read_endpoint_list(libtorrent::lazy_entry const* n, std::vector& epl) + { + using namespace libtorrent; + if (n->type() != lazy_entry::list_t) return; + for (int i = 0; i < n->list_size(); ++i) + { + lazy_entry const* e = n->list_at(i); + if (e->type() != lazy_entry::string_t) return; + if (e->string_length() < 6) continue; + char const* in = e->string_ptr(); + if (e->string_length() == 6) + epl.push_back(read_v4_endpoint(in)); +#if TORRENT_USE_IPV6 + else if (e->string_length() == 18) + epl.push_back(read_v6_endpoint(in)); +#endif + } + } + } + + template + void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) + { + using namespace libtorrent; + if (n->type() != entry::list_t) return; + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + if (i->type() != entry::string_t) return; + std::string const& p = i->string(); + if (p.size() < 6) continue; + std::string::const_iterator in = p.begin(); + if (p.size() == 6) + epl.push_back(detail::read_v4_endpoint(in)); +#if TORRENT_USE_IPV6 + else if (p.size() == 18) + epl.push_back(detail::read_v6_endpoint(in)); +#endif + } + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp new file mode 100644 index 0000000000..7dc5d435b8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type.hpp @@ -0,0 +1,337 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_TYPE +#define TORRENT_SOCKET_TYPE + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/http_stream.hpp" +#include "libtorrent/i2p_stream.hpp" +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/max.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include "libtorrent/ssl_stream.hpp" +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#if defined TORRENT_OS2 && defined ioc +#undef ioc +#endif + +#if TORRENT_USE_I2P + +#define TORRENT_SOCKTYPE_I2P_FORWARD(x) \ + case socket_type_int_impl::value: \ + get()->x; break; + +#define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ + case socket_type_int_impl::value: \ + return get()->x; + +#else // TORRENT_USE_I2P + +#define TORRENT_SOCKTYPE_I2P_FORWARD(x) +#define TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) + +#endif + +#ifdef TORRENT_USE_OPENSSL + +#define TORRENT_SOCKTYPE_SSL_FORWARD(x) \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; \ + case socket_type_int_impl >::value: \ + get >()->x; break; + +#define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; \ + case socket_type_int_impl >::value: \ + return get >()->x; + +#else + +#define TORRENT_SOCKTYPE_SSL_FORWARD(x) +#define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) + +#endif + +#define TORRENT_SOCKTYPE_FORWARD(x) \ + switch (m_type) { \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + case socket_type_int_impl::value: \ + get()->x; break; \ + TORRENT_SOCKTYPE_I2P_FORWARD(x) \ + TORRENT_SOCKTYPE_SSL_FORWARD(x) \ + default: TORRENT_ASSERT(false); \ + } + +#define TORRENT_SOCKTYPE_FORWARD_RET(x, def) \ + switch (m_type) { \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + case socket_type_int_impl::value: \ + return get()->x; \ + TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \ + TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ + default: TORRENT_ASSERT(false); return def; \ + } + +namespace libtorrent +{ + + template + struct socket_type_int_impl + { enum { value = 0 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 1 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 2 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 3 }; }; + + template <> + struct socket_type_int_impl + { enum { value = 4 }; }; + +#if TORRENT_USE_I2P + template <> + struct socket_type_int_impl + { enum { value = 5 }; }; +#endif + +#ifdef TORRENT_USE_OPENSSL + template <> + struct socket_type_int_impl > + { enum { value = 6 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 7 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 8 }; }; + + template <> + struct socket_type_int_impl > + { enum { value = 9 }; }; +#endif + + struct TORRENT_EXTRA_EXPORT socket_type + { + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit socket_type(io_service& ios): m_io_service(ios), m_type(0) {} + ~socket_type(); + + io_service& get_io_service() const; + bool is_open() const; + + char const* type_name() const; + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p); + void close(); + endpoint_type local_endpoint() const; + endpoint_type remote_endpoint() const; + void bind(endpoint_type const& endpoint); + std::size_t available() const; +#endif + + void open(protocol_type const& p, error_code& ec); + void close(error_code& ec); + endpoint_type local_endpoint(error_code& ec) const; + endpoint_type remote_endpoint(error_code& ec) const; + void bind(endpoint_type const& endpoint, error_code& ec); + std::size_t available(error_code& ec) const; + int type() const; + + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers, ec), 0) } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_read_some(buffers, handler)) } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(write_some(buffers, ec), 0) } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_write_some(buffers, handler)) } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { TORRENT_SOCKTYPE_FORWARD(async_connect(endpoint, handler)) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void io_control(IO_Control_Command& ioc) + { TORRENT_SOCKTYPE_FORWARD(io_control(ioc)) } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { TORRENT_SOCKTYPE_FORWARD_RET(read_some(buffers), 0) } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(io_control(ioc, ec)) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { TORRENT_SOCKTYPE_FORWARD(set_option(opt)) } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(set_option(opt, ec), ec) } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { TORRENT_SOCKTYPE_FORWARD(get_option(opt)) } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD_RET(get_option(opt, ec), ec) } + + + template + void instantiate(io_service& ios, void* userdata = 0) + { + TORRENT_ASSERT(&ios == &m_io_service); + construct(socket_type_int_impl::value, userdata); + } + + template S* get() + { + if (m_type != socket_type_int_impl::value) return 0; + return (S*)m_data; + } + + template S const* get() const + { + if (m_type != socket_type_int_impl::value) return 0; + return (S const*)m_data; + } + + private: + + void destruct(); + void construct(int type, void* userdata); + + io_service& m_io_service; + int m_type; + enum { storage_size = max9< + sizeof(stream_socket) + , sizeof(socks5_stream) + , sizeof(http_stream) + , sizeof(utp_stream) +#if TORRENT_USE_I2P + , sizeof(i2p_stream) +#else + , 0 +#endif +#ifdef TORRENT_USE_OPENSSL + , sizeof(ssl_stream) + , sizeof(ssl_stream) + , sizeof(ssl_stream) + , sizeof(ssl_stream) +#else + , 0, 0, 0, 0 +#endif + >::value + }; + + size_type m_data[(storage_size + sizeof(size_type) - 1) / sizeof(size_type)]; + }; + + // returns true if this socket is an SSL socket + bool is_ssl(socket_type const& s); + + // returns true if this is a uTP socket + bool is_utp(socket_type const& s); + +#if TORRENT_USE_I2P + // returns true if this is an i2p socket + bool is_i2p(socket_type const& s); +#endif + + // assuming the socket_type s is an ssl socket, make sure it + // verifies the hostname in its SSL handshake + void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec); + + // properly shuts down SSL sockets. holder keeps s alive + void async_shutdown(socket_type& s, boost::shared_ptr holder); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp new file mode 100644 index 0000000000..20276710ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socket_type_fwd.hpp @@ -0,0 +1,42 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_TYPE_FWD_HPP +#define TORRENT_SOCKET_TYPE_FWD_HPP + +namespace libtorrent +{ + struct socket_type; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp new file mode 100644 index 0000000000..4ca6c68d6b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/socks5_stream.hpp @@ -0,0 +1,192 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKS5_STREAM_HPP_INCLUDED +#define TORRENT_SOCKS5_STREAM_HPP_INCLUDED + +#include +#include +#include +#include "libtorrent/proxy_base.hpp" +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent { +namespace socks_error { + + // SOCKS5 error values. If an error_code has the + // socks error category (get_socks_category()), these + // are the error values. + enum socks_error_code + { + no_error = 0, + unsupported_version, + unsupported_authentication_method, + unsupported_authentication_version, + authentication_error, + username_required, + general_failure, + command_not_supported, + no_identd, + identd_error, + + num_errors + }; + + // internal + TORRENT_EXPORT boost::system::error_code make_error_code(socks_error_code e); + +} // namespace socks_error + +// returns the error_category for SOCKS5 errors +TORRENT_EXPORT boost::system::error_category& get_socks_category(); + +class socks5_stream : public proxy_base +{ +public: + + explicit socks5_stream(io_service& io_service) + : proxy_base(io_service) + , m_version(5) + , m_command(1) + , m_listen(0) + {} + + void set_version(int v) { m_version = v; } + + void set_command(int c) { m_command = c; } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + void set_dst_name(std::string const& host) + { + m_dst_name = host; + if (m_dst_name.size() > 255) + m_dst_name.resize(255); + } + + void close(error_code& ec) + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_hostname.clear(); + m_dst_name.clear(); + proxy_base::close(); + } +#endif + + typedef boost::function handler_type; + +//#error fix error messages to use custom error_code category +//#error add async_connect() that takes a hostname and port as well + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. if version == 5: + // 3.1 send SOCKS5 authentication method message + // 3.2 read SOCKS5 authentication response + // 3.3 send username+password + // 4. send SOCKS command message + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::name_lookup"); +#endif + tcp::resolver::query q(m_hostname, to_string(m_port).elems); + m_resolver.async_resolve(q, boost::bind( + &socks5_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(error_code const& e, boost::shared_ptr h); + void handshake1(error_code const& e, boost::shared_ptr h); + void handshake2(error_code const& e, boost::shared_ptr h); + void handshake3(error_code const& e, boost::shared_ptr h); + void handshake4(error_code const& e, boost::shared_ptr h); + void socks_connect(boost::shared_ptr h); + void connect1(error_code const& e, boost::shared_ptr h); + void connect2(error_code const& e, boost::shared_ptr h); + void connect3(error_code const& e, boost::shared_ptr h); + + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + std::string m_dst_name; + int m_version; + int m_command; + // set to one when we're waiting for the + // second message to accept an incoming connection + int m_listen; +}; + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp new file mode 100644 index 0000000000..9644b8b172 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/ssl_stream.hpp @@ -0,0 +1,308 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SSL_STREAM_HPP_INCLUDED +#define TORRENT_SSL_STREAM_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif +// openssl seems to believe it owns +// this name in every single scope +#undef set_key + +namespace libtorrent { + +template +class ssl_stream +{ +public: + + explicit ssl_stream(io_service& io_service, asio::ssl::context& ctx) + : m_sock(io_service, ctx) + { + } + + typedef typename asio::ssl::stream sock_type; + typedef typename sock_type::next_layer_type next_layer_type; + typedef typename Stream::lowest_layer_type lowest_layer_type; + typedef typename Stream::endpoint_type endpoint_type; + typedef typename Stream::protocol_type protocol_type; + + void set_host_name(std::string name) + { +#if OPENSSL_VERSION_NUMBER >= 0x90812f + SSL_set_tlsext_host_name(m_sock.native_handle(), name.c_str()); +#endif + } + + template + void set_verify_callback(T const& fun, error_code& ec) + { m_sock.set_verify_callback(fun, ec); } + +#if BOOST_VERSION >= 104700 + SSL* native_handle() { return m_sock.native_handle(); } +#endif + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + // the connect is split up in the following steps: + // 1. connect to peer + // 2. perform SSL client handshake + + // to avoid unnecessary copying of the handler, + // store it in a shared_ptr + boost::shared_ptr h(new handler_type(handler)); + + m_sock.next_layer().async_connect(endpoint + , boost::bind(&ssl_stream::connected, this, _1, h)); + } + + template + void async_accept_handshake(Handler const& handler) + { + // this is used for accepting SSL connections + boost::shared_ptr h(new handler_type(handler)); + m_sock.async_handshake(asio::ssl::stream_base::server + , boost::bind(&ssl_stream::handshake, this, _1, h)); + } + + void accept_handshake(error_code& ec) + { + // this is used for accepting SSL connections + m_sock.handshake(asio::ssl::stream_base::server, ec); + } + + template + void async_shutdown(Handler const& handler) + { + m_sock.async_shutdown(handler); + } + + void shutdown(error_code& ec) + { + m_sock.shutdown(ec); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) + { + m_sock.next_layer().set_option(opt); + } +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) + { + return m_sock.next_layer().set_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) + { + m_sock.next_layer().get_option(opt); + } +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) + { + return m_sock.next_layer().get_option(opt, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.next_layer().io_control(ioc); + } +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) + { + m_sock.next_layer().io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + return m_sock.write_some(buffers, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + std::size_t available() const + { return const_cast(m_sock).next_layer().available(); } +#endif + + std::size_t available(error_code& ec) const + { return const_cast(m_sock).next_layer().available(ec); } + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& endpoint) + { + m_sock.next_layer().bind(endpoint); + } +#endif + + void bind(endpoint_type const& endpoint, error_code& ec) + { + m_sock.next_layer().bind(endpoint, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + void open(protocol_type const& p) + { + m_sock.next_layer().open(p); + } +#endif + + void open(protocol_type const& p, error_code& ec) + { + m_sock.next_layer().open(p, ec); + } + + bool is_open() const + { + return const_cast(m_sock).next_layer().is_open(); + } + +#ifndef BOOST_NO_EXCEPTIONS + void close() + { + m_sock.next_layer().close(); + } +#endif + + void close(error_code& ec) + { + m_sock.next_layer().close(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type remote_endpoint() const + { + return const_cast(m_sock).next_layer().remote_endpoint(); + } +#endif + + endpoint_type remote_endpoint(error_code& ec) const + { + return const_cast(m_sock).next_layer().remote_endpoint(ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + endpoint_type local_endpoint() const + { + return const_cast(m_sock).next_layer().local_endpoint(); + } +#endif + + endpoint_type local_endpoint(error_code& ec) const + { + return const_cast(m_sock).next_layer().local_endpoint(ec); + } + + io_service& get_io_service() + { + return m_sock.get_io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + next_layer_type& next_layer() + { + return m_sock.next_layer(); + } + +private: + + void connected(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + return; + } + + m_sock.async_handshake(asio::ssl::stream_base::client + , boost::bind(&ssl_stream::handshake, this, _1, h)); + } + + void handshake(error_code const& e, boost::shared_ptr h) + { + (*h)(e); + } + + asio::ssl::stream m_sock; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp new file mode 100644 index 0000000000..a64b0c9b3a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/stat.hpp @@ -0,0 +1,384 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STAT_HPP_INCLUDED +#define TORRENT_STAT_HPP_INCLUDED + +#include +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + class TORRENT_EXTRA_EXPORT stat_channel + { + public: + + stat_channel() + : m_counter(0) + , m_5_sec_average(0) + , m_30_sec_average(0) + , m_total_counter(0) + {} + + void operator+=(stat_channel const& s) + { + TORRENT_ASSERT(m_counter >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + TORRENT_ASSERT(s.m_counter >= 0); + m_counter += s.m_counter; + m_total_counter += s.m_counter; + TORRENT_ASSERT(m_counter >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + } + + void add(int count) + { + TORRENT_ASSERT(count >= 0); + + m_counter += count; + TORRENT_ASSERT(m_counter >= 0); + m_total_counter += count; + TORRENT_ASSERT(m_total_counter >= 0); + } + + // should be called once every second + void second_tick(int tick_interval_ms); + int rate() const { return m_5_sec_average; } + int low_pass_rate() const { return m_30_sec_average; } + size_type total() const { return m_total_counter; } + + void offset(size_type c) + { + TORRENT_ASSERT(c >= 0); + TORRENT_ASSERT(m_total_counter >= 0); + m_total_counter += c; + TORRENT_ASSERT(m_total_counter >= 0); + } + + int counter() const { return m_counter; } + + void clear() + { + m_counter = 0; + m_5_sec_average = 0; + m_30_sec_average = 0; + m_total_counter = 0; + } + + private: + + // the accumulator for this second. + int m_counter; + + // sliding average + int m_5_sec_average; + int m_30_sec_average; + + // TODO: this is 4 bytes of padding! + + // total counters + size_type m_total_counter; + }; + + class TORRENT_EXTRA_EXPORT stat + { + friend class invariant_access; + public: + void operator+=(const stat& s) + { + for (int i = 0; i < num_channels; ++i) + m_stat[i] += s.m_stat[i]; + } + + void sent_syn(bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); +#endif + } + + void received_synack(bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + // we received SYN-ACK and also sent ACK back + m_stat[download_ip_protocol].add(ipv6 ? 60 : 40); + m_stat[upload_ip_protocol].add(ipv6 ? 60 : 40); +#endif + } + + void received_dht_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[download_dht_protocol].add(bytes); +#endif + } + + void sent_dht_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[upload_dht_protocol].add(bytes); +#endif + } + + void received_tracker_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[download_tracker_protocol].add(bytes); +#endif + } + + void sent_tracker_bytes(int bytes) + { +#ifndef TORRENT_DISABLE_FULL_STATS + TORRENT_ASSERT(bytes >= 0); + m_stat[upload_tracker_protocol].add(bytes); +#endif + } + + void received_bytes(int bytes_payload, int bytes_protocol) + { + TORRENT_ASSERT(bytes_payload >= 0); + TORRENT_ASSERT(bytes_protocol >= 0); + + m_stat[download_payload].add(bytes_payload); + m_stat[download_protocol].add(bytes_protocol); + } + + void sent_bytes(int bytes_payload, int bytes_protocol) + { + TORRENT_ASSERT(bytes_payload >= 0); + TORRENT_ASSERT(bytes_protocol >= 0); + + m_stat[upload_payload].add(bytes_payload); + m_stat[upload_protocol].add(bytes_protocol); + } + + // and IP packet was received or sent + // account for the overhead caused by it + void trancieve_ip_packet(int bytes_transferred, bool ipv6) + { +#ifndef TORRENT_DISABLE_FULL_STATS + // one TCP/IP packet header for the packet + // sent or received, and one for the ACK + // The IPv4 header is 20 bytes + // and IPv6 header is 40 bytes + const int header = (ipv6 ? 40 : 20) + 20; + const int mtu = 1500; + const int packet_size = mtu - header; + const int overhead = (std::max)(1, (bytes_transferred + packet_size - 1) / packet_size) * header; + m_stat[download_ip_protocol].add(overhead); + m_stat[upload_ip_protocol].add(overhead); +#endif + } + +#ifndef TORRENT_DISABLE_FULL_STATS + int upload_ip_overhead() const { return m_stat[upload_ip_protocol].counter(); } + int download_ip_overhead() const { return m_stat[download_ip_protocol].counter(); } + int upload_dht() const { return m_stat[upload_dht_protocol].counter(); } + int download_dht() const { return m_stat[download_dht_protocol].counter(); } + int download_tracker() const { return m_stat[download_tracker_protocol].counter(); } + int upload_tracker() const { return m_stat[upload_tracker_protocol].counter(); } +#else + int upload_ip_overhead() const { return 0; } + int download_ip_overhead() const { return 0; } + int upload_dht() const { return 0; } + int download_dht() const { return 0; } + int download_tracker() const { return 0; } + int upload_tracker() const { return 0; } +#endif + + // should be called once every second + void second_tick(int tick_interval_ms) + { + for (int i = 0; i < num_channels; ++i) + m_stat[i].second_tick(tick_interval_ms); + } + + int low_pass_upload_rate() const + { + return m_stat[upload_payload].low_pass_rate() + + m_stat[upload_protocol].low_pass_rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].low_pass_rate() + + m_stat[upload_dht_protocol].low_pass_rate() + + m_stat[upload_tracker_protocol].low_pass_rate() +#endif + ; + } + + int low_pass_download_rate() const + { + return m_stat[download_payload].low_pass_rate() + + m_stat[download_protocol].low_pass_rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].low_pass_rate() + + m_stat[download_dht_protocol].low_pass_rate() + + m_stat[download_tracker_protocol].low_pass_rate() +#endif + ; + } + + int upload_rate() const + { + return m_stat[upload_payload].rate() + + m_stat[upload_protocol].rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].rate() + + m_stat[upload_dht_protocol].rate() + + m_stat[upload_tracker_protocol].rate() +#endif + ; + } + + int download_rate() const + { + return m_stat[download_payload].rate() + + m_stat[download_protocol].rate() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].rate() + + m_stat[download_dht_protocol].rate() + + m_stat[download_tracker_protocol].rate() +#endif + ; + } + + size_type total_upload() const + { + return m_stat[upload_payload].total() + + m_stat[upload_protocol].total() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[upload_ip_protocol].total() + + m_stat[upload_dht_protocol].total() + + m_stat[upload_tracker_protocol].total() +#endif + ; + } + + size_type total_download() const + { + return m_stat[download_payload].total() + + m_stat[download_protocol].total() +#ifndef TORRENT_DISABLE_FULL_STATS + + m_stat[download_ip_protocol].total() + + m_stat[download_dht_protocol].total() + + m_stat[download_tracker_protocol].total() +#endif + ; + } + + int upload_payload_rate() const + { return m_stat[upload_payload].rate(); } + int download_payload_rate() const + { return m_stat[download_payload].rate(); } + + size_type total_payload_upload() const + { return m_stat[upload_payload].total(); } + size_type total_payload_download() const + { return m_stat[download_payload].total(); } + + size_type total_protocol_upload() const + { return m_stat[upload_protocol].total(); } + size_type total_protocol_download() const + { return m_stat[download_protocol].total(); } + + size_type total_transfer(int channel) const + { return m_stat[channel].total(); } + int transfer_rate(int channel) const + { return m_stat[channel].rate(); } + + // this is used to offset the statistics when a + // peer_connection is opened and have some previous + // transfers from earlier connections. + void add_stat(size_type downloaded, size_type uploaded) + { + m_stat[download_payload].offset(downloaded); + m_stat[upload_payload].offset(uploaded); + } + + int last_payload_downloaded() const + { return m_stat[download_payload].counter(); } + int last_payload_uploaded() const + { return m_stat[upload_payload].counter(); } + int last_protocol_downloaded() const + { return m_stat[download_protocol].counter(); } + int last_protocol_uploaded() const + { return m_stat[upload_protocol].counter(); } + + // these are the channels we keep stats for + enum + { + upload_payload, + upload_protocol, + download_payload, + download_protocol, +#ifndef TORRENT_DISABLE_FULL_STATS + upload_ip_protocol, + upload_dht_protocol, + upload_tracker_protocol, + download_ip_protocol, + download_dht_protocol, + download_tracker_protocol, +#endif + num_channels + }; + + void clear() + { + for (int i = 0; i < num_channels; ++i) + m_stat[i].clear(); + } + + stat_channel const& operator[](int i) const + { + TORRENT_ASSERT(i >= 0 && i < num_channels); + return m_stat[i]; + } + + private: + + stat_channel m_stat[num_channels]; + }; + +} + +#endif // TORRENT_STAT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp new file mode 100644 index 0000000000..3e6c10a2a8 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/storage.hpp @@ -0,0 +1,844 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STORAGE_HPP_INCLUDE +#define TORRENT_STORAGE_HPP_INCLUDE + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/allocator.hpp" +#include "libtorrent/bitfield.hpp" + +// OVERVIEW +// +// libtorrent provides a customization point for storage of data. By default, +// (``default_storage``) downloaded files are saved to disk according with the +// general conventions of bittorrent clients, mimicing the original file layout +// when the torrent was created. The libtorrent user may define a custom +// storage to store piece data in a different way. +// +// A custom storage implementation must derive from and implement the +// storage_interface. You must also provide a function that constructs the +// custom storage object and provide this function to the add_torrent() call +// via add_torrent_params. Either passed in to the constructor or by setting +// the add_torrent_params::storage field. +// +// This is an example storage implementation that stores all pieces in a +// ``std::map``, i.e. in RAM. It's not necessarily very useful in practice, but +// illustrates the basics of implementing a custom storage. +// +// .. code:: c++ +// +// struct temp_storage : storage_interface +// { +// temp_storage(file_storage const& fs) : m_files(fs) {} +// void set_file_priority(std::vector const& prio) {} +// virtual bool initialize(bool allocate_files) { return false; } +// virtual bool has_any_file() { return false; } +// virtual int read(char* buf, int slot, int offset, int size) +// { +// std::map >::const_iterator i = m_file_data.find(slot); +// if (i == m_file_data.end()) return 0; +// int available = i->second.size() - offset; +// if (available <= 0) return 0; +// if (available > size) available = size; +// memcpy(buf, &i->second[offset], available); +// return available; +// } +// virtual int write(const char* buf, int slot, int offset, int size) +// { +// std::vector& data = m_file_data[slot]; +// if (data.size() < offset + size) data.resize(offset + size); +// std::memcpy(&data[offset], buf, size); +// return size; +// } +// virtual bool rename_file(int file, std::string const& new_name) +// { assert(false); return false; } +// virtual bool move_storage(std::string const& save_path) { return false; } +// virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) { return false; } +// virtual bool write_resume_data(entry& rd) const { return false; } +// virtual bool move_slot(int src_slot, int dst_slot) { assert(false); return false; } +// virtual bool swap_slots(int slot1, int slot2) { assert(false); return false; } +// virtual bool swap_slots3(int slot1, int slot2, int slot3) { assert(false); return false; } +// virtual size_type physical_offset(int slot, int offset) +// { return slot * m_files.piece_length() + offset; }; +// virtual sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size) +// { +// int left = piece_size - ph.offset; +// assert(left >= 0); +// if (left > 0) +// { +// std::vector& data = m_file_data[slot]; +// // if there are padding files, those blocks will be considered +// // completed even though they haven't been written to the storage. +// // in this case, just extend the piece buffer to its full size +// // and fill it with zeroes. +// if (data.size() < piece_size) data.resize(piece_size, 0); +// ph.h.update(&data[ph.offset], left); +// } +// return ph.h.final(); +// } +// virtual bool release_files() { return false; } +// virtual bool delete_files() { return false; } +// +// std::map > m_file_data; +// file_storage m_files; +// }; +// +// storage_interface* temp_storage_constructor( +// file_storage const& fs, file_storage const* mapped +// , std::string const& path, file_pool& fp +// , std::vector const& prio) +// { +// return new temp_storage(fs); +// } + +namespace libtorrent +{ + class session; + struct file_pool; + struct disk_io_job; + struct disk_buffer_pool; + struct session_settings; + + TORRENT_EXTRA_EXPORT std::vector > get_filesizes( + file_storage const& t + , std::string const& p); + + TORRENT_EXTRA_EXPORT bool match_filesizes( + file_storage const& t + , std::string const& p + , std::vector > const& sizes + , bool compact_mode + , std::string* error = 0); + + struct TORRENT_EXTRA_EXPORT partial_hash + { + partial_hash(): offset(0) {} + // the number of bytes in the piece that has been hashed + int offset; + // the sha-1 context + hasher h; + }; + + // The storage interface is a pure virtual class that can be implemented to + // customize how and where data for a torrent is stored. The default storage + // implementation uses regular files in the filesystem, mapping the files in + // the torrent in the way one would assume a torrent is saved to disk. + // Implementing your own storage interface makes it possible to store all + // data in RAM, or in some optimized order on disk (the order the pieces are + // received for instance), or saving multifile torrents in a single file in + // order to be able to take advantage of optimized disk-I/O. + // + // It is also possible to write a thin class that uses the default storage + // but modifies some particular behavior, for instance encrypting the data + // before it's written to disk, and decrypting it when it's read again. + // + // The storage interface is based on slots, each slot is 'piece_size' number + // of bytes. All access is done by writing and reading whole or partial + // slots. One slot is one piece in the torrent, but the data in the slot + // does not necessarily correspond to the piece with the same index (in + // compact allocation mode it won't). + // + // libtorrent comes with two built-in storage implementations; + // ``default_storage`` and ``disabled_storage``. Their constructor functions + // are called default_storage_constructor() and + // ``disabled_storage_constructor`` respectively. The disabled storage does + // just what it sounds like. It throws away data that's written, and it + // reads garbage. It's useful mostly for benchmarking and profiling purpose. + // + struct TORRENT_EXPORT storage_interface + { + // hidden + storage_interface(): m_disk_pool(0), m_settings(0) {} + + + // This function is called when the storage is to be initialized. The + // default storage will create directories and empty files at this point. + // If ``allocate_files`` is true, it will also ``ftruncate`` all files to + // their target size. + // + // Returning ``true`` indicates an error occurred. + virtual bool initialize(bool allocate_files) = 0; + + // This function is called when first checking (or re-checking) the + // storage for a torrent. It should return true if any of the files that + // is used in this storage exists on disk. If so, the storage will be + // checked for existing pieces before starting the download. + virtual bool has_any_file() = 0; + + + // change the priorities of files. + virtual void set_file_priority(std::vector const& prio) = 0; + + // These functions should read or write the data in or to the given + // ``slot`` at the given ``offset``. It should read or write ``num_bufs`` + // buffers sequentially, where the size of each buffer is specified in + // the buffer array ``bufs``. The file::iovec_t type has the following + // members:: + // + // struct iovec_t { void* iov_base; size_t iov_len; }; + // + // The return value is the number of bytes actually read or written, or + // -1 on failure. If it returns -1, the error code is expected to be set + // to + // + // Every buffer in ``bufs`` can be assumed to be page aligned and be of a + // page aligned size, except for the last buffer of the torrent. The + // allocated buffer can be assumed to fit a fully page aligned number of + // bytes though. This is useful when reading and writing the last piece + // of a file in unbuffered mode. + // + // The ``offset`` is aligned to 16 kiB boundries *most of the time*, but + // there are rare exceptions when it's not. Specifically if the read + // cache is disabled/or full and a client requests unaligned data, or the + // file itself is not aligned in the torrent. Most clients request + // aligned data. + virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + + // This function is called when a read job is queued. It gives the + // storage wrapper an opportunity to hint the operating system about this + // coming read. For instance, the storage may call + // ``posix_fadvise(POSIX_FADV_WILLNEED)`` or ``fcntl(F_RDADVISE)``. + virtual void hint_read(int, int, int) {} + + // negative return value indicates an error + virtual int read(char* buf, int slot, int offset, int size) = 0; + + // negative return value indicates an error + virtual int write(const char* buf, int slot, int offset, int size) = 0; + + // returns the offset on the physical storage medium for the + // byte at offset ``offset`` in slot ``slot``. + virtual size_type physical_offset(int slot, int offset) = 0; + + // This function is optional. It is supposed to return the first piece, + // starting at ``start`` that is fully contained within a data-region on + // disk (i.e. non-sparse region). The purpose of this is to skip parts of + // files that can be known to contain zeros when checking files. + virtual int sparse_end(int start) const { return start; } + + // This function should move all the files belonging to the storage to + // the new save_path. The default storage moves the single file or the + // directory of the torrent. + // + // Before moving the files, any open file handles may have to be closed, + // like ``release_files()``. + // + // returns one of: + // | no_error = 0 + // | need_full_check = -1 + // | fatal_disk_error = -2 + // | file_exist = -4 + virtual int move_storage(std::string const& save_path, int flags) = 0; + + // This function should verify the resume data ``rd`` with the files + // on disk. If the resume data seems to be up-to-date, return true. If + // not, set ``error`` to a description of what mismatched and return false. + // + // The default storage may compare file sizes and time stamps of the files. + // + // Returning ``false`` indicates an error occurred. + virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0; + + // This function should fill in resume data, the current state of the + // storage, in ``rd``. The default storage adds file timestamps and + // sizes. + // + // Returning ``true`` indicates an error occurred. + virtual bool write_resume_data(entry& rd) const = 0; + + // This function should copy or move the data in slot ``src_slot`` to + // the slot ``dst_slot``. This is only used in compact mode. + // + // If the storage caches slots, this could be implemented more + // efficient than reading and writing the data. + // + // Returning ``true`` indicates an error occurred. + virtual bool move_slot(int src_slot, int dst_slot) = 0; + + // This function should swap the data in ``slot1`` and ``slot2``. The + // default storage uses a scratch buffer to read the data into, then + // moving the other slot and finally writing back the temporary slot's + // data + // + // This is only used in compact mode. + // + // Returning ``true`` indicates an error occurred. + virtual bool swap_slots(int slot1, int slot2) = 0; + + // This function should do a 3-way swap, or shift of the slots. ``slot1`` + // should move to ``slot2``, which should be moved to ``slot3`` which in + // turn should be moved to ``slot1``. + // + // This is only used in compact mode. + // + // Returning ``true`` indicates an error occurred. + virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0; + + // This function should release all the file handles that it keeps open to files + // belonging to this storage. The default implementation just calls + // ``file_pool::release_files(this)``. + // + // Returning ``true`` indicates an error occurred. + virtual bool release_files() = 0; + + // Rename file with index ``file`` to the thame ``new_name``. If there is an error, + // ``true`` should be returned. + virtual bool rename_file(int index, std::string const& new_filename) = 0; + + // This function should delete all files and directories belonging to + // this storage. + // + // Returning ``true`` indicates an error occurred. + // + // The ``disk_buffer_pool`` is used to allocate and free disk buffers. It + // has the following members:: + // + // struct disk_buffer_pool : boost::noncopyable + // { + // char* allocate_buffer(char const* category); + // void free_buffer(char* buf); + // + // char* allocate_buffers(int blocks, char const* category); + // void free_buffers(char* buf, int blocks); + // + // int block_size() const { return m_block_size; } + // + // void release_memory(); + // }; + virtual bool delete_files() = 0; + +#ifndef TORRENT_NO_DEPRECATE + // This function is called each time a file is completely downloaded. The + // storage implementation can perform last operations on a file. The file + // will not be opened for writing after this. + // + // ``index`` is the index of the file that completed. + // + // On windows the default storage implementation clears the sparse file + // flag on the specified file. + virtual void finalize_file(int) {} +#endif + + // access global disk_buffer_pool, for allocating and freeing disk buffers + disk_buffer_pool* disk_pool() { return m_disk_pool; } + + // access global session_settings + session_settings const& settings() const { return *m_settings; } + + // called by the storage implementation to set it into an + // error state. Typically whenever a critical file operation + // fails. + void set_error(std::string const& file, error_code const& ec) const; + + // returns the currently set error code and file path associated with it, + // if set. + error_code const& error() const { return m_error; } + std::string const& error_file() const { return m_error_file; } + + // reset the error state to allow continuing reading and writing + // to the storage + virtual void clear_error() { m_error = error_code(); m_error_file.resize(0); } + + // hidden + mutable error_code m_error; + mutable std::string m_error_file; + + // hidden + virtual ~storage_interface() {} + + // hidden + disk_buffer_pool* m_disk_pool; + session_settings* m_settings; + }; + + // The default implementation of storage_interface. Behaves as a normal + // bittorrent client. It is possible to derive from this class in order to + // override some of its behavior, when implementing a custom storage. + class TORRENT_EXPORT default_storage : public storage_interface, boost::noncopyable + { + public: + // constructs the default_storage based on the give file_storage (fs). + // ``mapped`` is an optional argument (it may be NULL). If non-NULL it + // represents the file mappsing that have been made to the torrent before + // adding it. That's where files are supposed to be saved and looked for + // on disk. ``save_path`` is the root save folder for this torrent. + // ``file_pool`` is the cache of file handles that the storage will use. + // All files it opens will ask the file_pool to open them. ``file_prio`` + // is a vector indicating the priority of files on startup. It may be + // an empty vector. Any file whose index is not represented by the vector + // (because the vector is too short) are assumed to have priority 1. + // this is used to treat files with priority 0 slightly differently. + default_storage(file_storage const& fs, file_storage const* mapped + , std::string const& path, file_pool& fp + , std::vector const& file_prio); + + // hidden + ~default_storage(); + + // hidden + void set_file_priority(std::vector const& prio); +#ifndef TORRENT_NO_DEPRECATE + void finalize_file(int file); +#endif + bool has_any_file(); + bool rename_file(int index, std::string const& new_filename); + bool release_files(); + bool delete_files(); + bool initialize(bool allocate_files); + int move_storage(std::string const& save_path, int flags); + int read(char* buf, int slot, int offset, int size); + int write(char const* buf, int slot, int offset, int size); + int sparse_end(int start) const; + void hint_read(int slot, int offset, int len); + int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + int writev(file::iovec_t const* buf, int slot, int offset, int num_bufs, int flags = file::random_access); + size_type physical_offset(int slot, int offset); + bool move_slot(int src_slot, int dst_slot); + bool swap_slots(int slot1, int slot2); + bool swap_slots3(int slot1, int slot2, int slot3); + bool verify_resume_data(lazy_entry const& rd, error_code& error); + bool write_resume_data(entry& rd) const; + + // if the files in this storage are mapped, returns the mapped + // file_storage, otherwise returns the original file_storage object. + file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; } + + private: + + // this identifies a read or write operation + // so that default_storage::readwritev() knows what to + // do when it's actually touching the file + struct fileop + { + size_type (file::*regular_op)(size_type file_offset + , file::iovec_t const* bufs, int num_bufs, error_code& ec); + size_type (default_storage::*unaligned_op)(boost::intrusive_ptr const& f + , size_type file_offset, file::iovec_t const* bufs, int num_bufs + , error_code& ec); + int cache_setting; + int mode; + }; + + void delete_one_file(std::string const& p); + int readwritev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, fileop const&); + + size_type read_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec); + size_type write_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec); + + boost::scoped_ptr m_mapped_files; + file_storage const& m_files; + + // helper function to open a file in the file pool with the right mode + boost::intrusive_ptr open_file(int file, int mode + , error_code& ec) const; + + std::vector m_file_priority; + std::string m_save_path; + // the file pool is typically stored in + // the session, to make all storage + // instances use the same pool + file_pool& m_pool; + + // this is a bitfield with one bit per file. A bit being set means + // we've written to that file previously. If we do write to a file + // whose bit is 0, we set the file size, to make the file allocated + // on disk (in full allocation mode) and just sparsely allocated in + // case of sparse allocation mode + bitfield m_file_created; + + int m_page_size; + bool m_allocate_files; + }; + + // this storage implementation does not write anything to disk + // and it pretends to read, and just leaves garbage in the buffers + // this is useful when simulating many clients on the same machine + // or when running stress tests and want to take the cost of the + // disk I/O out of the picture. This cannot be used for any kind + // of normal bittorrent operation, since it will just send garbage + // to peers and throw away all the data it downloads. It would end + // up being banned immediately + class disabled_storage : public storage_interface, boost::noncopyable + { + public: + disabled_storage(int piece_size) : m_piece_size(piece_size) {} + void set_file_priority(std::vector const&) {} + bool has_any_file() { return false; } + bool rename_file(int, std::string const&) { return false; } + bool release_files() { return false; } + bool delete_files() { return false; } + bool initialize(bool) { return false; } + int move_storage(std::string const&, int) { return 0; } + int read(char*, int, int, int size) { return size; } + int write(char const*, int, int, int size) { return size; } + size_type physical_offset(int, int) { return 0; } + int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags = file::random_access); + bool move_slot(int, int) { return false; } + bool swap_slots(int, int) { return false; } + bool swap_slots3(int, int, int) { return false; } + bool verify_resume_data(lazy_entry const&, error_code&) { return false; } + bool write_resume_data(entry&) const { return false; } + + int m_piece_size; + }; + + // flags for async_move_storage + enum move_flags_t + { + // replace any files in the destination when copying + // or moving the storage + always_replace_files, + + // if any files that we want to copy exist in the destination + // exist, fail the whole operation and don't perform + // any copy or move. There is an inherent race condition + // in this mode. The files are checked for existence before + // the operation starts. In between the check and performing + // the copy, the destination files may be created, in which + // case they are replaced. + fail_if_exist, + + // if any file exist in the target, take those files instead + // of the ones we may have in the source. + dont_replace + }; + + struct disk_io_thread; + + class TORRENT_EXTRA_EXPORT piece_manager + : public intrusive_ptr_base + , boost::noncopyable + { + friend class invariant_access; + friend struct disk_io_thread; + public: + + piece_manager( + boost::shared_ptr const& torrent + , boost::intrusive_ptr info + , std::string const& path + , file_pool& fp + , disk_io_thread& io + , storage_constructor_type sc + , storage_mode_t sm + , std::vector const& file_prio); + + ~piece_manager(); + + boost::intrusive_ptr info() const { return m_info; } + void write_resume_data(entry& rd) const; + + void async_check_fastresume(lazy_entry const* resume_data + , boost::function const& handler); + + void async_check_files(boost::function const& handler); + + void async_rename_file(int index, std::string const& name + , boost::function const& handler); + + void async_read( + peer_request const& r + , boost::function const& handler + , int cache_line_size = 0 + , int cache_expiry = 0); + + void async_read_and_hash( + peer_request const& r + , boost::function const& handler + , int cache_expiry = 0); + + void async_cache(int piece + , boost::function const& handler + , int cache_expiry = 0); + + // returns the write queue size + int async_write( + peer_request const& r + , disk_buffer_holder& buffer + , boost::function const& f); + + void async_hash(int piece, boost::function const& f); + + void async_release_files( + boost::function const& handler + = boost::function()); + + void abort_disk_io(); + + void async_clear_read_cache( + boost::function const& handler + = boost::function()); + + void async_delete_files( + boost::function const& handler + = boost::function()); + + void async_move_storage(std::string const& p, int flags + , boost::function const& handler); + + void async_set_file_priority( + std::vector const& prios + , boost::function const& handler); + + void async_save_resume_data( + boost::function const& handler); + + enum return_t + { + // return values from check_fastresume and check_files + no_error = 0, + need_full_check = -1, + fatal_disk_error = -2, + disk_check_aborted = -3, + file_exist = -4 + }; + + storage_interface* get_storage_impl() { return m_storage.get(); } + + private: + + std::string save_path() const; + + bool verify_resume_data(lazy_entry const& rd, error_code& e) + { return m_storage->verify_resume_data(rd, e); } + + bool is_allocating() const + { return m_state == state_expand_pieces; } + + void mark_failed(int index); + + error_code const& error() const { return m_storage->error(); } + std::string const& error_file() const { return m_storage->error_file(); } + int last_piece() const { return m_last_piece; } + void clear_error() { m_storage->clear_error(); } + + int slot_for(int piece) const; + int piece_for(int slot) const; + + // helper functions for check_dastresume + int check_no_fastresume(error_code& error); + int check_init_storage(error_code& error); + + // if error is set and return value is 'no_error' or 'need_full_check' + // the error message indicates that the fast resume data was rejected + // if 'fatal_disk_error' is returned, the error message indicates what + // when wrong in the disk access + int check_fastresume(lazy_entry const& rd, error_code& error); + + // this function returns true if the checking is complete + int check_files(int& current_slot, int& have_piece, error_code& error); + +#ifndef TORRENT_NO_DEPRECATE + bool compact_allocation() const + { return m_storage_mode == storage_mode_compact; } +#endif + +#ifdef TORRENT_DEBUG + std::string name() const { return m_info->name(); } +#endif + + bool allocate_slots_impl(int num_slots, mutex::scoped_lock& l, bool abort_on_disk = false); + + // updates the ph.h hasher object with the data at the given slot + // and optionally a 'small hash' as well, the hash for + // the partial slot. Returns the number of bytes read + int hash_for_slot(int slot, partial_hash& h, int piece_size + , int small_piece_size = 0, sha1_hash* small_hash = 0); + + void hint_read_impl(int piece_index, int offset, int size); + + int read_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs); + + int write_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs); + + size_type physical_offset(int piece_index, int offset); + + // returns the number of pieces left in the + // file currently being checked + int skip_file() const; + // -1=error 0=ok >0=skip this many pieces + int check_one_piece(int& have_piece); + int identify_data( + sha1_hash const& large_hash + , sha1_hash const& small_hash + , int current_slot); + + void switch_to_full_mode(); + sha1_hash hash_for_piece_impl(int piece, int* readback = 0); + + int release_files_impl() { return m_storage->release_files(); } + int delete_files_impl() { return m_storage->delete_files(); } + int rename_file_impl(int index, std::string const& new_filename) + { return m_storage->rename_file(index, new_filename); } + void set_file_priority_impl(std::vector const& p) + { m_storage->set_file_priority(p); } + + int move_storage_impl(std::string const& save_path, int flags); + + int allocate_slot_for_piece(int piece_index); +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif +#ifdef TORRENT_STORAGE_DEBUG + void debug_log() const; +#endif + boost::intrusive_ptr m_info; + file_storage const& m_files; + + boost::scoped_ptr m_storage; + + storage_mode_t m_storage_mode; + + // slots that haven't had any file storage allocated + std::vector m_unallocated_slots; + // slots that have file storage, but isn't assigned to a piece + std::vector m_free_slots; + + enum + { + has_no_slot = -3 // the piece has no storage + }; + + // maps piece indices to slots. If a piece doesn't + // have any storage, it is set to 'has_no_slot' + std::vector m_piece_to_slot; + + enum + { + unallocated = -1, // the slot is unallocated + unassigned = -2 // the slot is allocated but not assigned to a piece + }; + + // maps slots to piece indices, if a slot doesn't have a piece + // it can either be 'unassigned' or 'unallocated' + std::vector m_slot_to_piece; + + std::string m_save_path; + + mutable mutex m_mutex; + + enum { + // the default initial state + state_none, + // the file checking is complete + state_finished, + // checking the files + state_full_check, + // move pieces to their final position + state_expand_pieces + } m_state; + int m_current_slot; + // used during check. If any piece is found + // that is not in its final position, this + // is set to true + bool m_out_of_place; + // used to move pieces while expanding + // the storage from compact allocation + // to full allocation + aligned_holder m_scratch_buffer; + aligned_holder m_scratch_buffer2; + // the piece that is in the scratch buffer + int m_scratch_piece; + + // the last piece we wrote to or read from + int m_last_piece; + + // this is saved in case we need to instantiate a new + // storage (osed when remapping files) + storage_constructor_type m_storage_constructor; + + // this maps a piece hash to piece index. It will be + // build the first time it is used (to save time if it + // isn't needed) + std::multimap m_hash_to_piece; + + // this map contains partial hashes for downloading + // pieces. This is only accessed from within the + // disk-io thread. + std::map m_piece_hasher; + + disk_io_thread& m_io_thread; + + // the reason for this to be a void pointer + // is to avoid creating a dependency on the + // torrent. This shared_ptr is here only + // to keep the torrent object alive until + // the piece_manager destructs. This is because + // the torrent_info object is owned by the torrent. + boost::shared_ptr m_torrent; + }; + +} + +#endif // TORRENT_STORAGE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp new file mode 100644 index 0000000000..330b065106 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/storage_defs.hpp @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STORAGE_DEFS_HPP_INCLUDE +#define TORRENT_STORAGE_DEFS_HPP_INCLUDE + +#include "libtorrent/config.hpp" +#include +#include + +namespace libtorrent +{ + struct storage_interface; + class file_storage; + struct file_pool; + + // types of storage allocation used for add_torrent_params::storage_mode. + enum storage_mode_t + { + // All pieces will be written to their final position, all files will be + // allocated in full when the torrent is first started. This is done with + // ``fallocate()`` and similar calls. This mode minimizes fragmentation. + storage_mode_allocate, + + // All pieces will be written to the place where they belong and sparse files + // will be used. This is the recommended, and default mode. + storage_mode_sparse, + + // internal + internal_storage_mode_compact_deprecated, +#ifndef TORRENT_NO_DEPRECATE + storage_mode_compact = internal_storage_mode_compact_deprecated +#endif + }; + + typedef boost::function const&)> storage_constructor_type; + + // the constructor function for the regular file storage. This is the + // default value for add_torrent_params::storage. + TORRENT_EXPORT storage_interface* default_storage_constructor( + file_storage const&, file_storage const* mapped, std::string const&, file_pool& + , std::vector const&); + + // the constructor function for the disabled storage. This can be used for + // testing and benchmarking. It will throw away any data written to + // it and return garbage for anything read from it. + TORRENT_EXPORT storage_interface* disabled_storage_constructor( + file_storage const&, file_storage const* mapped, std::string const&, file_pool& + , std::vector const&); + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp new file mode 100644 index 0000000000..9113410bb0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/string_util.hpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STRING_UTIL_HPP_INCLUDED +#define TORRENT_STRING_UTIL_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT bool is_alpha(char c); + // this is used by bdecode_recursive's header file + TORRENT_EXPORT bool is_digit(char c); + TORRENT_EXTRA_EXPORT bool is_print(char c); + TORRENT_EXTRA_EXPORT bool is_space(char c); + TORRENT_EXTRA_EXPORT char to_lower(char c); + + TORRENT_EXTRA_EXPORT int split_string(char const** tags, int buf_size, char* in); + TORRENT_EXTRA_EXPORT bool string_begins_no_case(char const* s1, char const* s2); + TORRENT_EXTRA_EXPORT bool string_equal_no_case(char const* s1, char const* s2); + + TORRENT_EXTRA_EXPORT void url_random(char* begin, char* end); + + // strdup is not part of the C standard. Some systems + // don't have it and it won't be available when building + // in strict ansi mode + char* allocate_string_copy(char const* str); + + // returns p + x such that the pointer is 8 bytes aligned + // x cannot be greater than 7 + void* align_pointer(void* p); + + // searches for separator in the string 'last'. the pointer last points to + // is set to point to the first character following the separator. + // returns a pointer to a null terminated string starting at last, ending + // at the separator (the string is mutated to replace the separator with + // a '\0' character). If there is no separator, but the end of the string, + // the pointer next points to is set to the last null terminator, which will + // make the following invocation return NULL, to indicate the end of the + // string. + TORRENT_EXTRA_EXPORT char* string_tokenize(char* last, char sep, char** next); +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp new file mode 100644 index 0000000000..70a2b85dd6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/thread.hpp @@ -0,0 +1,89 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_THREAD_HPP_INCLUDED +#define TORRENT_THREAD_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN +// asio assumes that the windows error codes are defined already +#include +#endif + +#if defined TORRENT_BEOS +#include +#endif + +#include // for auto_ptr required by asio + +#include +#include +#include + +namespace libtorrent +{ + typedef boost::asio::detail::thread thread; + typedef boost::asio::detail::mutex mutex; + typedef boost::asio::detail::event event; + + // pauses the calling thread at least for the specified + // number of milliseconds + TORRENT_EXPORT void sleep(int milliseconds); + + struct TORRENT_EXTRA_EXPORT condition_variable + { + condition_variable(); + ~condition_variable(); + void wait(mutex::scoped_lock& l); + void wait_for(mutex::scoped_lock& l, time_duration rel_time); + void notify_all(); + private: +#ifdef BOOST_HAS_PTHREADS + pthread_cond_t m_cond; +#elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + HANDLE m_sem; + mutex m_mutex; + int m_num_waiters; +#elif defined TORRENT_BEOS + sem_id m_sem; + mutex m_mutex; + int m_num_waiters; +#else +#error not implemented +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp new file mode 100644 index 0000000000..1b3382cb92 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/time.hpp @@ -0,0 +1,129 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TIME_HPP_INCLUDED +#define TORRENT_TIME_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/ptime.hpp" +#include +#include + +// OVERVIEW +// +// This section contains fundamental time types used internally by +// libtorrent and exposed through various places in the API. The two +// basic types are ``ptime`` and ``time_duration``. The first represents +// a point in time and the second the difference between two points +// in time. +// +// The internal representation of these types is implementation defined +// and they can only be constructed via one of the construction functions +// that take a well defined time unit (seconds, minutes, etc.). They can +// only be turned into well defined time units by the accessor functions +// (total_microseconds(), etc.). +// +// .. note:: +// In a future version of libtorrent, these types will be replaced +// by the standard timer types from ``std::chrono``. +// + +namespace libtorrent +{ + TORRENT_EXTRA_EXPORT char const* time_now_string(); + std::string log_time(); + + // returns the current time, as represented by ptime. The + // resolution of this timer is about 100 ms. + TORRENT_EXPORT ptime const& time_now(); + + // returns the current time as represented by ptime. This is + // more expensive than time_now(), but provides as high resolution + // as the operating system can provide. + TORRENT_EXPORT ptime time_now_hires(); + + // the earliest and latest possible time points + // representable by ptime. + TORRENT_EXPORT ptime min_time(); + TORRENT_EXPORT ptime max_time(); + +#if defined TORRENT_USE_BOOST_DATE_TIME || defined TORRENT_USE_QUERY_PERFORMANCE_TIMER + + // returns a time_duration representing the specified number of seconds, milliseconds + // microseconds, minutes and hours. + TORRENT_EXPORT time_duration seconds(boost::int64_t s); + TORRENT_EXPORT time_duration milliseconds(boost::int64_t s); + TORRENT_EXPORT time_duration microsec(boost::int64_t s); + TORRENT_EXPORT time_duration minutes(boost::int64_t s); + TORRENT_EXPORT time_duration hours(boost::int64_t s); + + // returns the number of seconds, milliseconds and microseconds + // a time_duration represents. + TORRENT_EXPORT boost::int64_t total_seconds(time_duration td); + TORRENT_EXPORT boost::int64_t total_milliseconds(time_duration td); + TORRENT_EXPORT boost::int64_t total_microseconds(time_duration td); + +#elif TORRENT_USE_CLOCK_GETTIME || TORRENT_USE_SYSTEM_TIME || TORRENT_USE_ABSOLUTE_TIME + + // hidden + inline int total_seconds(time_duration td) + { return td.diff / 1000000; } + // hidden + inline int total_milliseconds(time_duration td) + { return td.diff / 1000; } + // hidden + inline boost::int64_t total_microseconds(time_duration td) + { return td.diff; } + + // hidden + inline time_duration microsec(boost::int64_t s) + { return time_duration(s); } + // hidden + inline time_duration milliseconds(boost::int64_t s) + { return time_duration(s * 1000); } + // hidden + inline time_duration seconds(boost::int64_t s) + { return time_duration(s * 1000000); } + // hidden + inline time_duration minutes(boost::int64_t s) + { return time_duration(s * 1000000 * 60); } + // hidden + inline time_duration hours(boost::int64_t s) + { return time_duration(s * 1000000 * 60 * 60); } + +#endif // TORRENT_USE_CLOCK_GETTIME + +} + +#endif // TORRENT_TIME_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp new file mode 100644 index 0000000000..f40583925a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/timestamp_history.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TIMESTAMP_HISTORY_HPP +#define TIMESTAMP_HISTORY_HPP + +#include "boost/cstdint.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent { + +// timestamp history keeps a history of the lowest timestamps we've +// seen in the last 20 minutes +struct TORRENT_EXTRA_EXPORT timestamp_history +{ + enum { history_size = 20 }; + + timestamp_history() : m_index(0), m_initialized(false), m_base(0), m_num_samples(0) {} + bool initialized() const { return m_initialized; } + + // add a sample to the timestamp history. If step is true, it's been + // a minute since the last step + boost::uint32_t add_sample(boost::uint32_t sample, bool step); + boost::uint32_t base() const { TORRENT_ASSERT(m_initialized); return m_base; } + void adjust_base(int change); + +private: + + // this is a circular buffer + boost::uint32_t m_history[history_size]; + + // and this is the index we're currently at + // in the circular buffer + boost::uint16_t m_index; + + bool m_initialized:1; + + // this is the lowest sample seen in the + // last 'history_size' minutes + boost::uint32_t m_base; + + // this is the number of samples since the + // last time we stepped one minute. If we + // don't have enough samples, we won't step + int m_num_samples; +}; + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h new file mode 100644 index 0000000000..1accb00111 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath.h @@ -0,0 +1,584 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#ifndef BN_H_ +#define BN_H_ + +#include +#include +#include +#include +#include + +#include "libtorrent/tommath_class.h" + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#ifdef __cplusplus +extern "C" { + +/* C++ compilers don't like assigning void * to mp_digit * */ +#define OPT_CAST(x) (x *) + +#else + +/* C on the other hand doesn't care */ +#define OPT_CAST(x) + +#endif + + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__) + #if !(defined(MP_64BIT) && defined(MP_16BIT) && defined(MP_8BIT)) + #define MP_64BIT + #endif +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT + typedef unsigned char mp_digit; + typedef unsigned short mp_word; +#elif defined(MP_16BIT) + typedef unsigned short mp_digit; + typedef unsigned long mp_word; +#elif defined(MP_64BIT) + /* for GCC only on supported platforms */ +#ifndef CRYPT + typedef unsigned long long ulong64; + typedef signed long long long64; +#endif + + typedef unsigned long mp_digit; + typedef unsigned long mp_word __attribute__ ((mode(TI))); + + #define DIGIT_BIT 60 +#else + /* this is the default case, 28-bit digits */ + + /* this is to make porting into LibTomCrypt easier :-) */ +#ifndef CRYPT + #if defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned __int64 ulong64; + typedef signed __int64 long64; + #else + typedef unsigned long long ulong64; + typedef signed long long long64; + #endif +#endif + + typedef unsigned long mp_digit; + typedef ulong64 mp_word; + +#ifdef MP_31BIT + /* this is an extension that uses 31-bit digits */ + #define DIGIT_BIT 31 +#else + /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ + #define DIGIT_BIT 28 + #define MP_28BIT +#endif +#endif + +/* define heap macros */ +#ifndef CRYPT + /* default to libc stuff */ + #ifndef XMALLOC + #define XMALLOC malloc + #define XFREE free + #define XREALLOC realloc + #define XCALLOC calloc + #else + /* prototypes for our heap functions */ + extern void *XMALLOC(size_t n); + extern void *XREALLOC(void *p, size_t n); + extern void *XCALLOC(size_t n, size_t s); + extern void XFREE(void *p); + #endif +#endif + + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT + #define DIGIT_BIT ((int)((CHAR_BIT * sizeof(mp_digit) - 1))) /* bits per digit */ +#endif + +#define MP_DIGIT_BIT DIGIT_BIT +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* equalities */ +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ +#define MP_RANGE MP_VAL + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS 0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef int mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + TOOM_MUL_CUTOFF, + TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat); + + +#define USED(m) ((m)->used) +#define DIGIT(m,k) ((m)->dp[(k)]) +#define SIGN(m) ((m)->sign) + +/* error code to char* string */ +char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int * a); + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int (mp_int * a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**d */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* returns true if a can be reduced with mp_reduce_2k_l */ +int mp_reduce_is_2k_l(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT + #define PRIME_SIZE 31 +#else + #define PRIME_SIZE 256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit ltm_prime_tab[]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96 + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4 + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_read_radix(mp_int *a, const char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c); +int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int mode); +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int mode); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +#ifdef __cplusplus + } +#endif + +#endif + + +/* $Source: /cvs/libtom/libtommath/tommath.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h new file mode 100644 index 0000000000..af50ecf061 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_class.h @@ -0,0 +1,1000 @@ +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) +#if defined(LTM2) +#define LTM3 +#endif +#if defined(LTM1) +#define LTM2 +#endif +#define LTM1 + +#if defined(LTM_ALL) +#define BN_ERROR_C +#define BN_FAST_MP_INVMOD_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_FAST_S_MP_MUL_DIGS_C +#define BN_FAST_S_MP_MUL_HIGH_DIGS_C +#define BN_FAST_S_MP_SQR_C +#define BN_MP_2EXPT_C +#define BN_MP_ABS_C +#define BN_MP_ADD_C +#define BN_MP_ADD_D_C +#define BN_MP_ADDMOD_C +#define BN_MP_AND_C +#define BN_MP_CLAMP_C +#define BN_MP_CLEAR_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_CMP_C +#define BN_MP_CMP_D_C +#define BN_MP_CMP_MAG_C +#define BN_MP_CNT_LSB_C +#define BN_MP_COPY_C +#define BN_MP_COUNT_BITS_C +#define BN_MP_DIV_C +#define BN_MP_DIV_2_C +#define BN_MP_DIV_2D_C +#define BN_MP_DIV_3_C +#define BN_MP_DIV_D_C +#define BN_MP_DR_IS_MODULUS_C +#define BN_MP_DR_REDUCE_C +#define BN_MP_DR_SETUP_C +#define BN_MP_EXCH_C +#define BN_MP_EXPT_D_C +#define BN_MP_EXPTMOD_C +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_EXTEUCLID_C +#define BN_MP_FREAD_C +#define BN_MP_FWRITE_C +#define BN_MP_GCD_C +#define BN_MP_GET_INT_C +#define BN_MP_GROW_C +#define BN_MP_INIT_C +#define BN_MP_INIT_COPY_C +#define BN_MP_INIT_MULTI_C +#define BN_MP_INIT_SET_C +#define BN_MP_INIT_SET_INT_C +#define BN_MP_INIT_SIZE_C +#define BN_MP_INVMOD_C +#define BN_MP_INVMOD_SLOW_C +#define BN_MP_IS_SQUARE_C +#define BN_MP_JACOBI_C +#define BN_MP_KARATSUBA_MUL_C +#define BN_MP_KARATSUBA_SQR_C +#define BN_MP_LCM_C +#define BN_MP_LSHD_C +#define BN_MP_MOD_C +#define BN_MP_MOD_2D_C +#define BN_MP_MOD_D_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_MP_MUL_C +#define BN_MP_MUL_2_C +#define BN_MP_MUL_2D_C +#define BN_MP_MUL_D_C +#define BN_MP_MULMOD_C +#define BN_MP_N_ROOT_C +#define BN_MP_NEG_C +#define BN_MP_OR_C +#define BN_MP_PRIME_FERMAT_C +#define BN_MP_PRIME_IS_DIVISIBLE_C +#define BN_MP_PRIME_IS_PRIME_C +#define BN_MP_PRIME_MILLER_RABIN_C +#define BN_MP_PRIME_NEXT_PRIME_C +#define BN_MP_PRIME_RABIN_MILLER_TRIALS_C +#define BN_MP_PRIME_RANDOM_EX_C +#define BN_MP_RADIX_SIZE_C +#define BN_MP_RADIX_SMAP_C +#define BN_MP_RAND_C +#define BN_MP_READ_RADIX_C +#define BN_MP_READ_SIGNED_BIN_C +#define BN_MP_READ_UNSIGNED_BIN_C +#define BN_MP_REDUCE_C +#define BN_MP_REDUCE_2K_C +#define BN_MP_REDUCE_2K_L_C +#define BN_MP_REDUCE_2K_SETUP_C +#define BN_MP_REDUCE_2K_SETUP_L_C +#define BN_MP_REDUCE_IS_2K_C +#define BN_MP_REDUCE_IS_2K_L_C +#define BN_MP_REDUCE_SETUP_C +#define BN_MP_RSHD_C +#define BN_MP_SET_C +#define BN_MP_SET_INT_C +#define BN_MP_SHRINK_C +#define BN_MP_SIGNED_BIN_SIZE_C +#define BN_MP_SQR_C +#define BN_MP_SQRMOD_C +#define BN_MP_SQRT_C +#define BN_MP_SUB_C +#define BN_MP_SUB_D_C +#define BN_MP_SUBMOD_C +#define BN_MP_TO_SIGNED_BIN_C +#define BN_MP_TO_SIGNED_BIN_N_C +#define BN_MP_TO_UNSIGNED_BIN_C +#define BN_MP_TO_UNSIGNED_BIN_N_C +#define BN_MP_TOOM_MUL_C +#define BN_MP_TOOM_SQR_C +#define BN_MP_TORADIX_C +#define BN_MP_TORADIX_N_C +#define BN_MP_UNSIGNED_BIN_SIZE_C +#define BN_MP_XOR_C +#define BN_MP_ZERO_C +#define BN_PRIME_TAB_C +#define BN_REVERSE_C +#define BN_S_MP_ADD_C +#define BN_S_MP_EXPTMOD_C +#define BN_S_MP_MUL_DIGS_C +#define BN_S_MP_MUL_HIGH_DIGS_C +#define BN_S_MP_SQR_C +#define BN_S_MP_SUB_C +#define BNCORE_C +#endif + +#if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C +#endif + +#if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CLAMP_C) +#endif + +#if defined(BN_MP_CLEAR_C) +#endif + +#if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C +#endif + +#if defined(BN_MP_CMP_D_C) +#endif + +#if defined(BN_MP_CMP_MAG_C) +#endif + +#if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_COUNT_BITS_C) +#endif + +#if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DR_IS_MODULUS_C) +#endif + +#if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_DR_SETUP_C) +#endif + +#if defined(BN_MP_EXCH_C) +#endif + +#if defined(BN_MP_EXPT_D_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MUL_C +#endif + +#if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C +#endif + +#if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C +#endif + +#if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C +#endif + +#if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_GET_INT_C) +#endif + +#if defined(BN_MP_GROW_C) +#endif + +#if defined(BN_MP_INIT_C) +#endif + +#if defined(BN_MP_INIT_COPY_C) + #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C +#endif + +#if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C +#endif + +#if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C +#endif + +#if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C +#endif + +#if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_MP_SUB_C + #define BN_S_MP_ADD_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C +#endif + +#if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C +#endif + +#if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_SETUP_C) +#endif + +#if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C +#endif + +#if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_N_ROOT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C +#endif + +#if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) +#endif + +#if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C +#endif + +#if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C +#endif + +#if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_L_C) +#endif + +#if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C +#endif + +#if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SHRINK_C) +#endif + +#if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C +#endif + +#if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C +#endif + +#if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_REVERSE_C + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_ZERO_C) +#endif + +#if defined(BN_PRIME_TAB_C) +#endif + +#if defined(BN_REVERSE_C) +#endif + +#if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C +#endif + +#if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C +#endif + +#if defined(BNCORE_C) +#endif + +#ifdef LTM3 +#define LTM_LAST +#endif +#include "libtorrent/tommath_superclass.h" +#include "libtorrent/tommath_class.h" +#else +#define LTM_LAST +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_class.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/07/28 11:59:32 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h new file mode 100644 index 0000000000..641d5b1833 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tommath_superclass.h @@ -0,0 +1,84 @@ +/* super class file for PK algos */ + +/* default ... include all MPI */ +//#define LTM_ALL + +// these are the only functions used by libtorrent +#define BN_MP_EXPTMOD_C +#define BN_MP_UNSIGNED_BIN_SIZE_C +#define BN_MP_TO_UNSIGNED_BIN_C +#define BN_MP_READ_UNSIGNED_BIN_C +#define BN_MP_SET_INT_C +#define BNCORE_C + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ + +/* For reference.... On an Athlon64 optimizing for speed... + + LTM's mpi.o with all functions [striped] is 142KiB in size. + +*/ + +/* Works for RSA only, mpi.o is 68KiB */ +#ifdef SC_RSA_1 + #define BN_MP_SHRINK_C + #define BN_MP_LCM_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_INVMOD_C + #define BN_MP_GCD_C + #define BN_MP_MOD_C + #define BN_MP_MULMOD_C + #define BN_MP_ADDMOD_C + #define BN_MP_EXPTMOD_C + #define BN_MP_SET_INT_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_MOD_D_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_REVERSE_C + #define BN_PRIME_TAB_C + + /* other modifiers */ + #define BN_MP_DIV_SMALL /* Slower division, not critical */ + + /* here we are on the last pass so we turn things off. The functions classes are still there + * but we remove them specifically from the build. This also invokes tweaks in functions + * like removing support for even moduli, etc... + */ +#ifdef LTM_LAST + #undef BN_MP_TOOM_MUL_C + #undef BN_MP_TOOM_SQR_C + #undef BN_MP_KARATSUBA_MUL_C + #undef BN_MP_KARATSUBA_SQR_C + #undef BN_MP_REDUCE_C + #undef BN_MP_REDUCE_SETUP_C + #undef BN_MP_DR_IS_MODULUS_C + #undef BN_MP_DR_SETUP_C + #undef BN_MP_DR_REDUCE_C + #undef BN_MP_REDUCE_IS_2K_C + #undef BN_MP_REDUCE_2K_SETUP_C + #undef BN_MP_REDUCE_2K_C + #undef BN_S_MP_EXPTMOD_C + #undef BN_MP_DIV_3_C + #undef BN_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_MP_INVMOD_C + + /* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold + * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] + * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without + * trouble. + */ + #undef BN_S_MP_MUL_DIGS_C + #undef BN_S_MP_SQR_C + #undef BN_MP_MONTGOMERY_REDUCE_C +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_superclass.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/05/14 13:29:17 $ */ diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp new file mode 100644 index 0000000000..fe634f3343 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent.hpp @@ -0,0 +1,1437 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HPP_INCLUDE +#define TORRENT_TORRENT_HPP_INCLUDE + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" +#include "libtorrent/storage_defs.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/union_endpoint.hpp" + +#if TORRENT_COMPLETE_TYPES_REQUIRED +#include "libtorrent/peer_connection.hpp" +#endif + +// define as 0 to disable. 1 enables debug output of the pieces and requested +// blocks. 2 also enables trace output of the time critical piece picking +// logic +#define TORRENT_DEBUG_STREAMING 0 + +namespace libtorrent +{ + class http_parser; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + struct logger; +#endif + + class piece_manager; + struct torrent_plugin; + struct bitfield; + struct announce_entry; + struct tracker_request; + struct add_torrent_params; + struct storage_interface; + class bt_peer_connection; + struct listen_socket_t; + + namespace aux + { + struct session_impl; + struct piece_checker_data; + } + + struct time_critical_piece + { + // when this piece was first requested + ptime first_requested; + // when this piece was last requested + ptime last_requested; + // by what time we want this piece + ptime deadline; + // 1 = send alert with piece data when available + int flags; + // how many peers it's been requested from + int peers; + // the piece index + int piece; +#if TORRENT_DEBUG_STREAMING > 0 + // the number of multiple requests are allowed + // to blocks still not downloaded (debugging only) + int timed_out; +#endif + bool operator<(time_critical_piece const& rhs) const + { return deadline < rhs.deadline; } + }; + + // a torrent is a class that holds information + // for a specific download. It updates itself against + // the tracker + class TORRENT_EXTRA_EXPORT torrent: public request_callback + , public boost::enable_shared_from_this + { + public: + + torrent(aux::session_impl& ses, tcp::endpoint const& net_interface + , int block_size, int seq, add_torrent_params const& p + , sha1_hash const& info_hash); + ~torrent(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + sha1_hash const& obfuscated_hash() const + { return m_obfuscated_hash; } +#endif + + // This may be called from multiple threads + sha1_hash const& info_hash() const { return m_info_hash; } + + bool is_deleted() const { return m_deleted; } + + // starts the announce timer + void start(); + + void start_download_url(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); + void add_extension(boost::function(torrent*, void*)> const& ext + , void* userdata); + void notify_extension_add_peer(tcp::endpoint const& ip, int src, int flags); +#endif + + peer_connection* find_lowest_ranking_peer() const; + +#if TORRENT_USE_ASSERTS + bool has_peer(peer_connection* p) const + { return m_connections.find(p) != m_connections.end(); } +#endif + + // this is called when the torrent has metadata. + // it will initialize the storage and the piece-picker + void init(); + + // find the peer that introduced us to the given endpoint. This is + // used when trying to holepunch. We need the introducer so that we + // can send a rendezvous connect message + bt_peer_connection* find_introducer(tcp::endpoint const& ep) const; + + // if we're connected to a peer at ep, return its peer connection + // only count BitTorrent peers + bt_peer_connection* find_peer(tcp::endpoint const& ep) const; + + void on_resume_data_checked(int ret, disk_io_job const& j); + void on_force_recheck(int ret, disk_io_job const& j); + void on_piece_checked(int ret, disk_io_job const& j); + void files_checked(); + void start_checking(); + + void start_announcing(); + void stop_announcing(); + + void send_share_mode(); + void send_upload_only(); + + void set_share_mode(bool s); + bool share_mode() const { return m_share_mode; } + + bool graceful_pause() const { return m_graceful_pause_mode; } + + void set_upload_mode(bool b); + bool upload_mode() const { return m_upload_mode || m_graceful_pause_mode; } + bool is_upload_only() const { return is_finished() || upload_mode(); } + + int seed_rank(session_settings const& s) const; + + enum flags_t { overwrite_existing = 1 }; + void add_piece(int piece, char const* data, int flags = 0); + void on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p); + void on_disk_cache_complete(int ret, disk_io_job const& j); + + void set_progress_ppm(int p) { m_progress_ppm = p; } + struct read_piece_struct + { + boost::shared_array piece_data; + int blocks_left; + bool fail; + error_code error; + }; + void read_piece(int piece); + void on_disk_read_complete(int ret, disk_io_job const& j, peer_request r, read_piece_struct* rp); + + storage_mode_t storage_mode() const { return (storage_mode_t)m_storage_mode; } + storage_interface* get_storage() + { + if (!m_owning_storage) return 0; + return m_owning_storage->get_storage_impl(); + } + + // this will flag the torrent as aborted. The main + // loop in session_impl will check for this state + // on all torrents once every second, and take + // the necessary actions then. + void abort(); + bool is_aborted() const { return m_abort; } + + void new_external_ip(); + + torrent_status::state_t state() const { return (torrent_status::state_t)m_state; } + void set_state(torrent_status::state_t s); + + session_settings const& settings() const; + + aux::session_impl& session() { return m_ses; } + + void set_sequential_download(bool sd); + bool is_sequential_download() const + { return m_sequential_download; } + + void queue_up(); + void queue_down(); + void set_queue_position(int p); + int queue_position() const { return m_sequence_number; } + + void second_tick(stat& accumulator, int tick_interval_ms); + + // see if we need to connect to web seeds, and if so, + // connect to them + void maybe_connect_web_seeds(); + + std::string name() const; + + stat statistics() const { return m_stat; } + void add_stats(stat const& s); + size_type bytes_left() const; + int block_bytes_wanted(piece_block const& p) const; + void bytes_done(torrent_status& st, bool accurate) const; + size_type quantized_bytes_done() const; + + void ip_filter_updated() { m_policy.ip_filter_updated(); } + + void handle_disk_error(disk_io_job const& j, peer_connection* c = 0); + void clear_error(); + void set_error(error_code const& ec, std::string const& file); + bool has_error() const { return !!m_error; } + error_code error() const { return m_error; } + + void flush_cache(); + void pause(bool graceful = false); + void resume(); + void set_allow_peers(bool b, bool graceful_pause = false); + void set_announce_to_dht(bool b) { m_announce_to_dht = b; } + void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; } + void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; } + + ptime started() const { return m_started; } + void do_pause(); + void do_resume(); + + bool is_paused() const; + bool allows_peers() const { return m_allow_peers; } + bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; } + void force_recheck(); + void save_resume_data(int flags); + + bool is_active_download() const; + bool is_active_finished() const; + void update_guage(); + + bool need_save_resume_data() const + { + // save resume data every 15 minutes regardless, just to + // keep stats up to date + return m_need_save_resume_data || time(0) - m_last_saved_resume > 15 * 60; + } + + bool is_auto_managed() const { return m_auto_managed; } + void auto_managed(bool a); + + bool should_check_files() const; + + bool delete_files(); + + // ============ start deprecation ============= + void filter_piece(int index, bool filter); + void filter_pieces(std::vector const& bitmask); + bool is_piece_filtered(int index) const; + void filtered_pieces(std::vector& bitmask) const; + void filter_files(std::vector const& files); +#if !TORRENT_NO_FPU + void file_progress(std::vector& fp) const; +#endif + // ============ end deprecation ============= + + void piece_availability(std::vector& avail) const; + + void set_piece_priority(int index, int priority); + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces); + void piece_priorities(std::vector*) const; + + void set_file_priority(int index, int priority); + int file_priority(int index) const; + + void prioritize_files(std::vector const& files); + void file_priorities(std::vector*) const; + + void cancel_non_critical(); + void set_piece_deadline(int piece, int t, int flags); + void reset_piece_deadline(int piece); + void clear_time_critical(); + void update_piece_priorities(); + + void status(torrent_status* st, boost::uint32_t flags); + + // this torrent changed state, if the user is subscribing to + // it, add it to the m_state_updates list in session_impl + void state_updated(); + + void file_progress(std::vector& fp, int flags = 0) const; + + void use_interface(std::string net_interface); + tcp::endpoint get_interface() const; + + void connect_to_url_seed(std::list::iterator url); + bool connect_to_peer(policy::peer* peerinfo, bool ignore_limit = false); + + int priority() const { return m_priority; } + void set_priority(int prio) + { + TORRENT_ASSERT(prio <= 255 && prio >= 0); + if (prio > 255) prio = 255; + else if (prio < 0) prio = 0; + m_priority = prio; + state_updated(); + } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void resolve_countries(bool r) + { m_resolve_countries = r; } + + bool resolving_countries() const + { + return m_resolve_countries && !m_ses.settings().force_proxy; + } +#endif + +// -------------------------------------------- + // BANDWIDTH MANAGEMENT + + bandwidth_channel m_bandwidth_channel[2]; + + int bandwidth_throttle(int channel) const; + +// -------------------------------------------- + // PEER MANAGEMENT + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + void log_to_all_peers(char const* message); +#endif + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void add_web_seed(std::string const& url, web_seed_entry::type_t type); + void add_web_seed(std::string const& url, web_seed_entry::type_t type + , std::string const& auth, web_seed_entry::headers_t const& extra_headers); + + void remove_web_seed(std::string const& url, web_seed_entry::type_t type); + void disconnect_web_seed(peer_connection* p); + + void retry_web_seed(peer_connection* p, int retry = 0); + + void remove_web_seed(peer_connection* p); + + std::list web_seeds() const + { return m_web_seeds; } + + std::set web_seeds(web_seed_entry::type_t type) const; + + bool free_upload_slots() const + { return m_num_uploads < m_max_uploads; } + + bool choke_peer(peer_connection& c); + bool unchoke_peer(peer_connection& c, bool optimistic = false); + + // used by peer_connection to attach itself to a torrent + // since incoming connections don't know what torrent + // they're a part of until they have received an info_hash. + // false means attach failed + bool attach_peer(peer_connection* p); + + // this will remove the peer and make sure all + // the pieces it had have their reference counter + // decreased in the piece_picker + void remove_peer(peer_connection* p); + + void cancel_block(piece_block block); + + bool want_more_peers() const; + bool try_connect_peer(); + void add_peer(tcp::endpoint const& adr, int source); + + // the number of peers that belong to this torrent + int num_peers() const { return (int)m_connections.size(); } + int num_seeds() const; + + typedef std::set::iterator peer_iterator; + typedef std::set::const_iterator const_peer_iterator; + + const_peer_iterator begin() const { return m_connections.begin(); } + const_peer_iterator end() const { return m_connections.end(); } + + peer_iterator begin() { return m_connections.begin(); } + peer_iterator end() { return m_connections.end(); } + + void resolve_peer_country(boost::intrusive_ptr const& p) const; + + void get_full_peer_list(std::vector& v) const; + void get_peer_info(std::vector& v); + void get_download_queue(std::vector* queue); + + void refresh_explicit_cache(int cache_size); + +// -------------------------------------------- + // TRACKER MANAGEMENT + + // these are callbacks called by the tracker_connection instance + // (either http_tracker_connection or udp_tracker_connection) + // when this torrent got a response from its tracker request + // or when a failure occured + virtual void tracker_response( + tracker_request const& r + , address const& tracker_ip + , std::list
const& ip_list + , std::vector& e, int interval, int min_interval + , int complete, int incomplete, int downloaded + , address const& external_ip, std::string const& trackerid); + virtual void tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& msg + , int retry_interval); + virtual void tracker_warning(tracker_request const& req + , std::string const& msg); + virtual void tracker_scrape_response(tracker_request const& req + , int complete, int incomplete, int downloaded, int downloaders); + + void update_scrape_state(); + + // if no password and username is set + // this will return an empty string, otherwise + // it will concatenate the login and password + // ready to be sent over http (but without + // base64 encoding). + std::string tracker_login() const; + + // generate the tracker key for this torrent. + // The key is passed to http trackers as ``&key=``. + boost::uint32_t tracker_key() const; + + // if we need a connect boost, connect some peers + // immediately + void do_connect_boost(); + + // returns the absolute time when the next tracker + // announce will take place. + ptime next_announce() const; + + // forcefully sets next_announce to the current time + void force_tracker_request(ptime, int tracker_idx); + void scrape_tracker(); + void announce_with_tracker(tracker_request::event_t e + = tracker_request::none + , address const& bind_interface = address_v4::any()); + int seconds_since_last_scrape() const { return m_last_scrape; } + +#ifndef TORRENT_DISABLE_DHT + void dht_announce(); +#endif + + // sets the username and password that will be sent to + // the tracker + void set_tracker_login(std::string const& name, std::string const& pw); + + // the tcp::endpoint of the tracker that we managed to + // announce ourself at the last time we tried to announce + tcp::endpoint current_tracker() const; + + announce_entry* find_tracker(tracker_request const& r); + +// -------------------------------------------- + // PIECE MANAGEMENT + + void recalc_share_mode(); + + void update_sparse_piece_prio(int piece, int cursor, int reverse_cursor); + + void get_suggested_pieces(std::vector& s) const; + + bool super_seeding() const + { + // we're not super seeding if we're not a seed + return m_super_seeding && is_seed(); + } + + void super_seeding(bool on); + int get_piece_to_super_seed(bitfield const&); + + // returns true if we have downloaded the given piece + bool have_piece(int index) const + { + if (!valid_metadata()) return false; + if (!has_picker()) return is_seed(); + return m_picker->have_piece(index); + } + + // called when we learn that we have a piece + // only once per piece + void we_have(int index); + + int num_have() const + { + // pretend we have every piece when in seed mode + if (m_seed_mode) { + return m_torrent_file->num_pieces(); + } + + return has_picker() + ? m_picker->num_have() + : m_torrent_file->num_pieces(); + } + + // when we get a have message, this is called for that piece + void peer_has(int index, peer_connection const* peer) + { + if (has_picker()) + { + m_picker->inc_refcount(index, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + // when we get a bitfield message, this is called for that piece + void peer_has(bitfield const& bits, peer_connection const* peer) + { + if (has_picker()) + { + if (bits.all_set() && bits.size() > 0) + m_picker->inc_refcount_all(peer); + else + m_picker->inc_refcount(bits, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_has_all(peer_connection const* peer) + { + if (has_picker()) + { + m_picker->inc_refcount_all(peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_lost(bitfield const& bits, peer_connection const* peer) + { + if (has_picker()) + { + if (bits.all_set() && bits.size() > 0) + m_picker->dec_refcount_all(peer); + else + m_picker->dec_refcount(bits, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + void peer_lost(int index, peer_connection const* peer) + { + if (has_picker()) + { + m_picker->dec_refcount(index, peer); + } +#ifdef TORRENT_DEBUG + else + { + TORRENT_ASSERT(is_seed()); + } +#endif + } + + int block_size() const { TORRENT_ASSERT(m_block_size_shift > 0); return 1 << m_block_size_shift; } + peer_request to_req(piece_block const& p) const; + + void disconnect_all(error_code const& ec); + int disconnect_peers(int num, error_code const& ec); + + // this is called wheh the torrent has completed + // the download. It will post an event, disconnect + // all seeds and let the tracker know we're finished. + void completed(); + +#if TORRENT_USE_I2P + void on_i2p_resolve(error_code const& ec, char const* dest); +#endif + + // this is the asio callback that is called when a name + // lookup for a PEER is completed. + void on_peer_name_lookup(error_code const& e, tcp::resolver::iterator i + , peer_id pid); + + // this is the asio callback that is called when a name + // lookup for a WEB SEED is completed. + void on_name_lookup(error_code const& e, tcp::resolver::iterator i + , std::list::iterator url, tcp::endpoint proxy); + + void connect_web_seed(std::list::iterator web, tcp::endpoint a); + + // this is the asio callback that is called when a name + // lookup for a proxy for a web seed is completed. + void on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator i + , std::list::iterator url); + + // remove a web seed, or schedule it for removal in case there + // are outstanding operations on it + void remove_web_seed(std::list::iterator web); + + // this is called when the torrent has finished. i.e. + // all the pieces we have not filtered have been downloaded. + // If no pieces are filtered, this is called first and then + // completed() is called immediately after it. + void finished(); + + // This is the opposite of finished. It is called if we used + // to be finished but enabled some files for download so that + // we wasn't finished anymore. + void resume_download(); + + void async_verify_piece(int piece_index, boost::function const&); + + // this is called from the peer_connection + // each time a piece has failed the hash + // test + void piece_finished(int index, int passed_hash_check); + + // piece_passed is called when a piece passes the hash check + // this will tell all peers that we just got his piece + // and also let the piece picker know that we have this piece + // so it wont pick it for download + void piece_passed(int index); + + // piece_failed is called when a piece fails the hash check + void piece_failed(int index); + + // this will restore the piece picker state for a piece + // by re marking all the requests to blocks in this piece + // that are still outstanding in peers' download queues. + // this is done when a piece fails + void restore_piece_state(int index); + + enum wasted_reason_t + { + piece_timed_out, piece_cancelled, piece_unknown, piece_seed, piece_end_game, piece_closing + , waste_reason_max + }; + void add_redundant_bytes(int b, wasted_reason_t reason); + void add_failed_bytes(int b); + + // this is true if we have all the pieces + bool is_seed() const + { + return valid_metadata() + && (!m_picker + || m_state == torrent_status::seeding + || m_picker->num_have() == m_picker->num_pieces()); + } + + // this is true if we have all the pieces that we want + bool is_finished() const + { + if (is_seed()) return true; + return valid_metadata() && m_torrent_file->num_pieces() + - m_picker->num_have() - m_picker->num_filtered() == 0; + } + + bool is_inactive() const + { return m_inactive; } + + std::string save_path() const; + alert_manager& alerts() const; + piece_picker& picker() + { + TORRENT_ASSERT(m_picker.get()); + return *m_picker; + } + bool has_picker() const + { + return m_picker.get() != 0; + } + policy& get_policy() { return m_policy; } + piece_manager& filesystem(); + torrent_info const& torrent_file() const + { return *m_torrent_file; } + + boost::intrusive_ptr get_torrent_copy(); + + std::string const& uuid() const { return m_uuid; } + void set_uuid(std::string const& s) { m_uuid = s; } + std::string const& url() const { return m_url; } + void set_url(std::string const& s) { m_url = s; } + std::string const& source_feed_url() const { return m_source_feed_url; } + void set_source_feed_url(std::string const& s) { m_source_feed_url = s; } + + std::vector const& trackers() const + { return m_trackers; } + + void replace_trackers(std::vector const& urls); + + // returns true if the tracker was added, and false if it was already + // in the tracker list (in which case the source was added to the + // entry in the list) + bool add_tracker(announce_entry const& url); + + torrent_handle get_handle(); + + void write_resume_data(entry& rd) const; + void read_resume_data(lazy_entry const& rd); + + void seen_complete() { m_last_seen_complete = time(0); } + int time_since_complete() const { return int(time(0) - m_last_seen_complete); } + time_t last_seen_complete() const { return m_last_seen_complete; } + + // LOGGING +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + virtual void debug_log(const char* fmt, ...) const; +#endif + + // DEBUG +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + +// -------------------------------------------- + // RESOURCE MANAGEMENT + + int get_peer_upload_limit(tcp::endpoint ip) const; + int get_peer_download_limit(tcp::endpoint ip) const; + void set_peer_upload_limit(tcp::endpoint ip, int limit); + void set_peer_download_limit(tcp::endpoint ip, int limit); + + void set_upload_limit(int limit, bool state_update = true); + int upload_limit() const; + void set_download_limit(int limit, bool state_update = true); + int download_limit() const; + + void set_max_uploads(int limit, bool state_update = true); + int max_uploads() const { return m_max_uploads; } + void set_max_connections(int limit, bool state_update = true); + int max_connections() const { return m_max_connections; } + + // flags are defined in storage.hpp + void move_storage(std::string const& save_path, int flags); + + // renames the file with the given index to the new name + // the name may include a directory path + // returns false on failure + bool rename_file(int index, std::string const& name); + + // unless this returns true, new connections must wait + // with their initialization. + bool ready_for_connections() const + { return m_connections_initialized; } + bool valid_metadata() const + { return m_torrent_file->is_valid(); } + bool are_files_checked() const + { return m_files_checked; } + + // parses the info section from the given + // bencoded tree and moves the torrent + // to the checker thread for initial checking + // of the storage. + // a return value of false indicates an error + bool set_metadata(char const* metadata_buf, int metadata_size); + + void on_torrent_download(error_code const& ec, http_parser const& parser + , char const* data, int size); + + int sequence_number() const { return m_sequence_number; } + + bool seed_mode() const { return m_seed_mode; } + void leave_seed_mode(bool seed); + + bool all_verified() const + { return int(m_num_verified) == m_torrent_file->num_pieces(); } + bool verified_piece(int piece) const + { + TORRENT_ASSERT(piece < int(m_verified.size())); + TORRENT_ASSERT(piece >= 0); + return m_verified.get_bit(piece); + } + void verified(int piece); + + bool add_merkle_nodes(std::map const& n, int piece); + + // this is called once periodically for torrents + // that are not private + void lsd_announce(); + + void update_last_upload() { m_last_upload = 0; } + + void set_apply_ip_filter(bool b); + bool apply_ip_filter() const { return m_apply_ip_filter; } + + void queue_torrent_check(); + void dequeue_torrent_check(); + + void clear_in_state_update() + { m_in_state_updates = false; } + + void inc_num_connecting() + { ++m_num_connecting; } + void dec_num_connecting() + { + TORRENT_ASSERT(m_num_connecting > 0); + --m_num_connecting; + } + + bool is_ssl_torrent() const { return m_ssl_torrent; } +#ifdef TORRENT_USE_OPENSSL + void set_ssl_cert(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase); + void set_ssl_cert_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params); + boost::asio::ssl::context* ssl_ctx() const { return m_ssl_ctx.get(); } +#endif + + int num_time_critical_pieces() const + { return m_time_critical_pieces.size(); } + + private: + + void on_files_deleted(int ret, disk_io_job const& j); + void on_files_released(int ret, disk_io_job const& j); + void on_torrent_paused(int ret, disk_io_job const& j); + void on_storage_moved(int ret, disk_io_job const& j); + void on_save_resume_data(int ret, disk_io_job const& j); + void on_file_renamed(int ret, disk_io_job const& j); + void on_cache_flushed(int ret, disk_io_job const& j); + + void on_piece_verified(int ret, disk_io_job const& j + , boost::function f); + + int prioritize_tracker(int tracker_index); + int deprioritize_tracker(int tracker_index); + + void on_country_lookup(error_code const& error, tcp::resolver::iterator i + , boost::intrusive_ptr p) const; + bool request_bandwidth_from_session(int channel) const; + + void update_peer_interest(bool was_finished); + void prioritize_udp_trackers(); + + void parse_response(const entry& e, std::vector& peer_list); + + void update_tracker_timer(ptime now); + + static void on_tracker_announce_disp(boost::weak_ptr p + , error_code const& e); + + void on_tracker_announce(); + +#ifndef TORRENT_DISABLE_DHT + static void on_dht_announce_response_disp(boost::weak_ptr t + , std::vector const& peers); + void on_dht_announce_response(std::vector const& peers); + bool should_announce_dht() const; +#endif + + void remove_time_critical_piece(int piece, bool finished = false); + void remove_time_critical_pieces(std::vector const& priority); + void request_time_critical_pieces(); + + policy m_policy; + + // all time totals of uploaded and downloaded payload + // stored in resume data + size_type m_total_uploaded; + size_type m_total_downloaded; + + // if this torrent is running, this was the time + // when it was started. This is used to have a + // bias towards keeping seeding torrents that + // recently was started, to avoid oscillation + ptime m_started; + + boost::intrusive_ptr m_torrent_file; + + // if this pointer is 0, the torrent is in + // a state where the metadata hasn't been + // received yet, or during shutdown. + // the piece_manager keeps the torrent object + // alive by holding a shared_ptr to it and + // the torrent keeps the piece manager alive + // with this intrusive_ptr. This cycle is + // broken when torrent::abort() is called + // Then the torrent releases the piece_manager + // and when the piece_manager is complete with all + // outstanding disk io jobs (that keeps + // the piece_manager alive) it will destruct + // and release the torrent file. The reason for + // this is that the torrent_info is used by + // the piece_manager, and stored in the + // torrent, so the torrent cannot destruct + // before the piece_manager. + boost::intrusive_ptr m_owning_storage; + + // this is a weak (non owninig) pointer to + // the piece_manager. This is used after the torrent + // has been aborted, and it can no longer own + // the object. + piece_manager* m_storage; + +#ifdef TORRENT_USE_OPENSSL + boost::shared_ptr m_ssl_ctx; + +#if BOOST_VERSION >= 104700 + bool verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx); +#endif + + void init_ssl(std::string const& cert); +#endif + + std::set m_connections; + + // of all peers in m_connections, this is the number + // of peers that are outgoing and still waiting to + // complete the connection. This is used to possibly + // kick out these connections when we get incoming + // connections (if we've reached the connection limit) + int m_num_connecting; + + // The list of web seeds in this torrent. Seeds + // with fatal errors are removed from the set + std::list m_web_seeds; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + + // used for tracker announces + deadline_timer m_tracker_timer; + + // this is the upload and download statistics for the whole torrent. + // it's updated from all its peers once every second. + libtorrent::stat m_stat; + + // ----------------------------- + + // a back reference to the session + // this torrent belongs to. + aux::session_impl& m_ses; + + // used to resolve hostnames for web seeds + mutable tcp::resolver m_host_resolver; + + std::vector m_file_priority; + + // this vector contains the number of bytes completely + // downloaded (as in passed-hash-check) in each file. + // this lets us trigger on individual files completing + std::vector m_file_progress; + + boost::scoped_ptr m_picker; + + std::vector m_trackers; + // this is an index into m_trackers + + // this list is sorted by time_critical_piece::deadline + std::deque m_time_critical_pieces; + + std::string m_trackerid; + std::string m_username; + std::string m_password; + + // the network interfaces outgoing connections + // are opened through. If there is more then one, + // they are used in a round-robin fasion + std::vector m_net_interfaces; + + std::string m_save_path; + + // if we don't have the metadata, this is a url to + // the torrent file + std::string m_url; + + // if this was added from an RSS feed, this is the unique + // identifier in the feed. + std::string m_uuid; + + // if this torrent was added by an RSS feed, this is the + // URL to that feed + std::string m_source_feed_url; + + // this is used as temporary storage while downloading + // the .torrent file from m_url + std::vector m_torrent_file_buf; + + // each bit represents a piece. a set bit means + // the piece has had its hash verified. This + // is only used in seed mode (when m_seed_mode + // is true) + bitfield m_verified; + + // set if there's an error on this torrent + error_code m_error; + // if the error ocurred on a file, this is the file + std::string m_error_file; + + // used if there is any resume data + std::vector m_resume_data; + lazy_entry m_resume_entry; + + // if the torrent is started without metadata, it may + // still be given a name until the metadata is received + // once the metadata is received this field will no + // longer be used and will be reset + boost::scoped_ptr m_name; + + storage_constructor_type m_storage_constructor; + + // the posix time this torrent was added and when + // it was completed. If the torrent isn't yet + // completed, m_completed_time is 0 + time_t m_added_time; + time_t m_completed_time; + time_t m_last_saved_resume; + + // this was the last time _we_ saw a seed in this swarm + time_t m_last_seen_complete; + + // this is the time last any of our peers saw a seed + // in this swarm + time_t m_swarm_last_seen_complete; + + // m_num_verified = m_verified.count() + boost::uint32_t m_num_verified; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is SHA1("req2" + info-hash), used for + // encrypted hand shakes + sha1_hash m_obfuscated_hash; +#endif + + // keep a copy if the info-hash here, so it can be accessed from multiple + // threads, and be cheap to access from the client + sha1_hash m_info_hash; + + // the average time it takes to download one time critical piece + boost::uint32_t m_average_piece_time; + // the average piece download time deviation + boost::uint32_t m_piece_time_deviation; + + // the number of bytes that has been + // downloaded that failed the hash-test + boost::uint32_t m_total_failed_bytes; + boost::uint32_t m_total_redundant_bytes; + + // the sequence number for this torrent, this is a + // monotonically increasing number for each added torrent + int m_sequence_number; + + // ============================== + // The following members are specifically + // ordered to make the 24 bit members + // properly 32 bit aligned by inserting + // 8 bits after each one + // ============================== + + // the number of seconds we've been in upload mode + unsigned int m_upload_mode_time:24; + + // the state of this torrent (queued, checking, downloading, etc.) + unsigned int m_state:3; + + // determines the storage state for this torrent. + unsigned int m_storage_mode:2; + + // this is true while tracker announcing is enabled + // is is disabled while paused and checking files + bool m_announcing:1; + + // this is true while the tracker deadline timer + // is in use. i.e. one or more trackers are waiting + // for a reannounce + bool m_waiting_tracker:1; + + // this means we haven't verified the file content + // of the files we're seeding. the m_verified bitfield + // indicates which pieces have been verified and which + // haven't + bool m_seed_mode:1; + +// ---- + + // total time we've been available on this torrent + // does not count when the torrent is stopped or paused + // in seconds + unsigned int m_active_time:24; + + // the index to the last tracker that worked + boost::int8_t m_last_working_tracker; + +// ---- + + // total time we've been finished with this torrent + // does not count when the torrent is stopped or paused + unsigned int m_finished_time:24; + + // in case the piece picker hasn't been constructed + // when this settings is set, this variable will keep + // its value until the piece picker is created + bool m_sequential_download:1; + + // is false by default and set to + // true when the first tracker reponse + // is received + bool m_got_tracker_response:1; + + // this is set to false as long as the connections + // of this torrent hasn't been initialized. If we + // have metadata from the start, connections are + // initialized immediately, if we didn't have metadata, + // they are initialized right after files_checked(). + // valid_resume_data() will return false as long as + // the connections aren't initialized, to avoid + // them from altering the piece-picker before it + // has been initialized with files_checked(). + bool m_connections_initialized:1; + + // if this is true, we're currently super seeding this + // torrent. + bool m_super_seeding:1; + + // this is set when we don't want to load seed_mode, + // paused or auto_managed from the resume data + bool m_override_resume_data:1; + + // this is true while there is a country + // resolution in progress. To avoid flodding + // the DNS request queue, only one ip is resolved + // at a time. + mutable bool m_resolving_country:1; + + // this is true if the user has enabled + // country resolution in this torrent + bool m_resolve_countries:1; + + // set to false when saving resume data. Set to true + // whenever something is downloaded + bool m_need_save_resume_data:1; + +// ---- + + // total time we've been available as a seed on this torrent + // does not count when the torrent is stopped or paused + unsigned int m_seeding_time:24; + + // this is a counter that is decreased every + // second, and when it reaches 0, the policy::pulse() + // is called and the time scaler is reset to 10. + boost::int8_t m_time_scaler; + +// ---- + + // the maximum number of uploads for this torrent + unsigned int m_max_uploads:24; + + // these are the flags sent in on a call to save_resume_data + // we need to save them to check them in write_resume_data + boost::uint8_t m_save_resume_flags; + +// ---- + + // the number of unchoked peers in this torrent + unsigned int m_num_uploads:24; + + // the size of a request block + // each piece is divided into these + // blocks when requested. The block size is + // 1 << m_block_size_shift + unsigned int m_block_size_shift:5; + + // is set to true every time there is an incoming + // connection to this torrent + bool m_has_incoming:1; + + // this is set to true when the files are checked + // before the files are checked, we don't try to + // connect to peers + bool m_files_checked:1; + + // this is true if the torrent has been added to + // checking queue in the session + bool m_queued_for_checking:1; + +// ---- + + // the maximum number of connections for this torrent + unsigned int m_max_connections:24; + + // set to true when this torrent has been paused but + // is waiting to finish all current download requests + // before actually closing all connections + bool m_graceful_pause_mode:1; + + // this is set to true when the torrent starts up + // The first tracker response, when this is true, + // will attempt to connect to a bunch of peers immediately + // and set this to false. We only do this once to get + // the torrent kick-started + bool m_need_connect_boost:1; + + // rotating sequence number for LSD announces sent out. + // used to only use IP broadcast for every 8th lsd announce + boost::uint8_t m_lsd_seq:3; + + // this is set to true if the torrent was started without + // metadata. It is used to save metadata in the resume file + // by default for such torrents. It does not necessarily + // have to be a magnet link. + bool m_magnet_link:1; + + // set to true if the session IP filter applies to this + // torrent or not. Defaults to true. + bool m_apply_ip_filter:1; + + // if set to true, add tracker URLs loaded from resume + // data into this torrent instead of replacing them + bool m_merge_resume_trackers:1; + +// ---- + + // the number of bytes of padding files + boost::uint32_t m_padding:24; + + // this is the priority of the torrent. The higher + // the value is, the more bandwidth is assigned to + // the torrent's peers + boost::uint32_t m_priority:8; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + boost::uint32_t m_complete:24; + + // state subscription. If set, a pointer to this torrent + // will be added to the m_state_updates set in session_impl + // whenever this torrent's state changes (any state). + bool m_state_subscription:1; + + // in state_updates list. When adding a torrent to the + // session_impl's m_state_update list, this bit is set + // to never add the same torrent twice + bool m_in_state_updates:1; + + // these represent whether or not this torrent is counted + // in the total counters of active seeds and downloads + // in the session. + bool m_is_active_download:1; + bool m_is_active_finished:1; + + // even if we're not built to support SSL torrents, + // remember that this is an SSL torrent, so that we don't + // accidentally start seeding it without any authentication. + bool m_ssl_torrent:1; + + // this is set to true if we're trying to delete the + // files belonging to it. When set, don't write any + // more blocks to disk! + bool m_deleted:1; + + // set to true while moving the storage + bool m_moving_storage:1; + + // this is true if this torrent is considered inactive from the + // queuing mechanism's point of view. If a torrent doesn't transfer + // at high enough rates, it's inactive. + bool m_inactive:1; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + boost::uint32_t m_incomplete:24; + + // is set to true when the torrent has + // been aborted. + bool m_abort:1; + + // true when the torrent should announce to + // the DHT + bool m_announce_to_dht:1; + + // true when this torrent should anncounce to + // trackers + bool m_announce_to_trackers:1; + + // true when this torrent should anncounce to + // the local network + bool m_announce_to_lsd:1; + + // is true if this torrent has allows having peers + bool m_allow_peers:1; + + // set to true when this torrent may not download anything + bool m_upload_mode:1; + + // if this is true, libtorrent may pause and resume + // this torrent depending on queuing rules. Torrents + // started with auto_managed flag set may be added in + // a paused state in case there are no available + // slots. + bool m_auto_managed:1; + + // this is set when the torrent is in share-mode + bool m_share_mode:1; + +// ---- + + // the number of seconds since the last piece passed for + // this torrent + boost::uint64_t m_last_download:24; + + // the number of seconds since the last scrape request to + // one of the trackers in this torrent + boost::uint64_t m_last_scrape:16; + + // the number of seconds since the last byte was uploaded + // from this torrent + boost::uint64_t m_last_upload:24; + +// ---- + + // the scrape data from the tracker response, this + // is optional and may be 0xffffff + unsigned int m_downloaded:24; + + // round-robin index into m_interfaces + mutable boost::uint8_t m_interface_index; + +// ---- + + // progress parts per million (the number of + // millionths of completeness) + unsigned int m_progress_ppm:20; + + // the number of seconds this torrent has been under the inactive + // threshold in terms of sending and receiving data. When this counter + // reaches the settings.inactive_torrent_timeout it will be considered + // inactive and possibly open up another queue slot, to start another, + // queued, torrent. Every second it's above the threshold + boost::int16_t m_inactive_counter; + + // if this is set, accept the save path saved in the resume data, if + // present + bool m_use_resume_save_path:1; + +#if TORRENT_USE_ASSERTS + public: + // set to false until we've loaded resume data + bool m_resume_data_loaded; +#endif + }; +} + +#endif // TORRENT_TORRENT_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp new file mode 100644 index 0000000000..594feb36be --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_handle.hpp @@ -0,0 +1,1602 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HANDLE_HPP_INCLUDED +#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/socket.hpp" // tcp::endpoint + +namespace libtorrent +{ + namespace aux + { + struct session_impl; + } + + struct torrent_plugin; + struct peer_info; + struct peer_list_entry; + struct torrent_status; + class torrent; + + // allows torrent_handle to be used in unordered_map and unordered_set. + TORRENT_EXPORT std::size_t hash_value(torrent_status const& ts); + +#ifndef BOOST_NO_EXCEPTIONS + // for compatibility with 0.14 + typedef libtorrent_exception duplicate_torrent; + typedef libtorrent_exception invalid_handle; + void throw_invalid_handle(); +#endif + + // holds the state of a block in a piece. Who we requested + // it from and how far along we are at downloading it. + struct TORRENT_EXPORT block_info + { + // this is the enum used for the block_info::state field. + enum block_state_t + { + // This block has not been downloaded or requested form any peer. + none, + // The block has been requested, but not completely downloaded yet. + requested, + // The block has been downloaded and is currently queued for being + // written to disk. + writing, + // The block has been written to disk. + finished + }; + + private: + TORRENT_UNION addr_t + { + address_v4::bytes_type v4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + } addr; + + boost::uint16_t port; + public: + + // The peer is the ip address of the peer this block was downloaded from. + void set_peer(tcp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + is_v6_addr = ep.address().is_v6(); + if (is_v6_addr) + addr.v6 = ep.address().to_v6().to_bytes(); + else +#endif + addr.v4 = ep.address().to_v4().to_bytes(); + port = ep.port(); + } + tcp::endpoint peer() const + { +#if TORRENT_USE_IPV6 + if (is_v6_addr) + return tcp::endpoint(address_v6(addr.v6), port); + else +#endif + return tcp::endpoint(address_v4(addr.v4), port); + } + + // the number of bytes that have been received for this block + unsigned bytes_progress:15; + + // the total number of bytes in this block. + unsigned block_size:15; + + // the state this block is in (see block_state_t) + unsigned state:2; + + // the number of peers that is currently requesting this block. Typically + // this is 0 or 1, but at the end of the torrent blocks may be requested + // by more peers in parallel to speed things up. + unsigned num_peers:14; + private: +#if TORRENT_USE_IPV6 + // the type of the addr union + unsigned is_v6_addr:1; +#endif + }; + + // This class holds information about pieces that have outstanding requests + // or outstanding writes + struct TORRENT_EXPORT partial_piece_info + { + // the index of the piece in question. ``blocks_in_piece`` is the number + // of blocks in this particular piece. This number will be the same for + // most pieces, but + // the last piece may have fewer blocks than the standard pieces. + int piece_index; + + // the number of blocks in this piece + int blocks_in_piece; + + // the number of blocks that are in the finished state + int finished; + + // the number of blocks that are in the writing state + int writing; + + // the number of blocks that are in the requested state + int requested; + + // this is an array of ``blocks_in_piece`` number of + // items. One for each block in the piece. + // + // .. warning:: This is a pointer that points to an array + // that's owned by the session object. The next time + // get_download_queue() is called, it will be invalidated. + block_info* blocks; + + // the speed classes. These may be used by the piece picker to + // coalesce requests of similar download rates + enum state_t { none, slow, medium, fast }; + + // the download speed class this piece falls into. + // this is used internally to cluster peers of the same + // speed class together when requesting blocks. + // + // set to either ``fast``, ``medium``, ``slow`` or ``none``. It tells + // which download rate category the peers downloading this piece falls + // into. ``none`` means that no peer is currently downloading any part of + // the piece. Peers prefer picking pieces from the same category as + // themselves. The reason for this is to keep the number of partially + // downloaded pieces down. Pieces set to ``none`` can be converted into + // any of ``fast``, ``medium`` or ``slow`` as soon as a peer want to + // download from it. + state_t piece_state; + }; + + // You will usually have to store your torrent handles somewhere, since it's + // the object through which you retrieve information about the torrent and + // aborts the torrent. + // + // .. warning:: + // Any member function that returns a value or fills in a value has to be + // made synchronously. This means it has to wait for the main thread to + // complete the query before it can return. This might potentially be + // expensive if done from within a GUI thread that needs to stay + // responsive. Try to avoid quering for information you don't need, and + // try to do it in as few calls as possible. You can get most of the + // interesting information about a torrent from the + // torrent_handle::status() call. + // + // The default constructor will initialize the handle to an invalid state. + // Which means you cannot perform any operation on it, unless you first + // assign it a valid handle. If you try to perform any operation on an + // uninitialized handle, it will throw ``invalid_handle``. + // + // .. warning:: + // All operations on a torrent_handle may throw libtorrent_exception + // exception, in case the handle is no longer refering to a torrent. + // There is one exception is_valid() will never throw. Since the torrents + // are processed by a background thread, there is no guarantee that a + // handle will remain valid between two calls. + // + struct TORRENT_EXPORT torrent_handle + { + friend class invariant_access; + friend struct aux::session_impl; + friend struct feed; + friend class torrent; + friend std::size_t hash_value(torrent_handle const& th); + + // constructs a torrent handle that does not refer to a torrent. + // i.e. is_valid() will return false. + torrent_handle() {} + + // flags for add_piece(). + enum flags_t { overwrite_existing = 1 }; + + // This function will write ``data`` to the storage as piece ``piece``, + // as if it had been downloaded from a peer. ``data`` is expected to + // point to a buffer of as many bytes as the size of the specified piece. + // The data in the buffer is copied and passed on to the disk IO thread + // to be written at a later point. + // + // By default, data that's already been downloaded is not overwritten by + // this buffer. If you trust this data to be correct (and pass the piece + // hash check) you may pass the overwrite_existing flag. This will + // instruct libtorrent to overwrite any data that may already have been + // downloaded with this data. + // + // Since the data is written asynchronously, you may know that is passed + // or failed the hash check by waiting for piece_finished_alert or + // hash_failed_alert. + void add_piece(int piece, char const* data, int flags = 0) const; + + // This function starts an asynchronous read operation of the specified + // piece from this torrent. You must have completed the download of the + // specified piece before calling this function. + // + // When the read operation is completed, it is passed back through an + // alert, read_piece_alert. Since this alert is a reponse to an explicit + // call, it will always be posted, regardless of the alert mask. + // + // Note that if you read multiple pieces, the read operations are not + // guaranteed to finish in the same order as you initiated them. + void read_piece(int piece) const; + + // Returns true if this piece has been completely downloaded, and false + // otherwise. + bool have_piece(int piece) const; + + // internal + void get_full_peer_list(std::vector& v) const; + + // takes a reference to a vector that will be cleared and filled with one + // entry for each peer connected to this torrent, given the handle is + // valid. If the torrent_handle is invalid, it will throw + // libtorrent_exception exception. Each entry in the vector contains + // information about that particular peer. See peer_info. + void get_peer_info(std::vector& v) const; + + // flags to pass in to status() to specify which properties of the + // torrent to query for. By default all flags are set. + enum status_flags_t + { + // calculates ``distributed_copies``, ``distributed_full_copies`` and + // ``distributed_fraction``. + query_distributed_copies = 1, + // includes partial downloaded blocks in ``total_done`` and + // ``total_wanted_done``. + query_accurate_download_counters = 2, + // includes ``last_seen_complete``. + query_last_seen_complete = 4, + // includes ``pieces``. + query_pieces = 8, + // includes ``verified_pieces`` (only applies to torrents in *seed + // mode*). + query_verified_pieces = 16, + // includes ``torrent_file``, which is all the static information from + // the .torrent file. + query_torrent_file = 32, + // includes ``name``, the name of the torrent. This is either derived + // from the .torrent file, or from the ``&dn=`` magnet link argument + // or possibly some other source. If the name of the torrent is not + // known, this is an empty string. + query_name = 64, + // includes ``save_path``, the path to the directory the files of the + // torrent are saved to. + query_save_path = 128 + }; + + // ``status()`` will return a structure with information about the status + // of this torrent. If the torrent_handle is invalid, it will throw + // libtorrent_exception exception. See torrent_status. The ``flags`` + // argument filters what information is returned in the torrent_status. + // Some information in there is relatively expensive to calculate, and if + // you're not interested in it (and see performance issues), you can + // filter them out. + // + // By default everything is included. The flags you can use to decide + // what to *include* are defined in the status_flags_t enum. + torrent_status status(boost::uint32_t flags = 0xffffffff) const; + + // ``get_download_queue()`` takes a non-const reference to a vector which + // it will fill with information about pieces that are partially + // downloaded or not downloaded at all but partially requested. See + // partial_piece_info for the fields in the returned vector. + void get_download_queue(std::vector& queue) const; + + // flags for set_piece_deadline(). + enum deadline_flags { alert_when_available = 1 }; + + // This function sets or resets the deadline associated with a specific + // piece index (``index``). libtorrent will attempt to download this + // entire piece before the deadline expires. This is not necessarily + // possible, but pieces with a more recent deadline will always be + // prioritized over pieces with a deadline further ahead in time. The + // deadline (and flags) of a piece can be changed by calling this + // function again. + // + // The ``flags`` parameter can be used to ask libtorrent to send an alert + // once the piece has been downloaded, by passing alert_when_available. + // When set, the read_piece_alert alert will be delivered, with the piece + // data, when it's downloaded. + // + // If the piece is already downloaded when this call is made, nothing + // happens, unless the alert_when_available flag is set, in which case it + // will do the same thing as calling read_piece() for ``index``. + // + // ``deadline`` is the number of milliseconds until this piece should be + // completed. + // + // ``reset_piece_deadline`` removes the deadline from the piece. If it + // hasn't already been downloaded, it will no longer be considered a + // priority. + // + // ``clear_piece_deadlines()`` removes deadlines on all pieces in + // the torrent. As if reset_piece_deadline() was called on all pieces. + void set_piece_deadline(int index, int deadline, int flags = 0) const; + void reset_piece_deadline(int index) const; + void clear_piece_deadlines() const; + + // This sets the bandwidth priority of this torrent. The priority of a + // torrent determines how much bandwidth its peers are assigned when + // distributing upload and download rate quotas. A high number gives more + // bandwidth. The priority must be within the range [0, 255]. + // + // The default priority is 0, which is the lowest priority. + // + // To query the priority of a torrent, use the + // ``torrent_handle::status()`` call. + // + // Torrents with higher priority will not nececcarily get as much + // bandwidth as they can consume, even if there's is more quota. Other + // peers will still be weighed in when bandwidth is being distributed. + // With other words, bandwidth is not distributed strictly in order of + // priority, but the priority is used as a weight. + // + // Peers whose Torrent has a higher priority will take precedence when + // distributing unchoke slots. This is a strict prioritization where + // every interested peer on a high priority torrent will be unchoked + // before any other, lower priority, torrents have any peers unchoked. + void set_priority(int prio) const; + +#ifndef TORRENT_NO_DEPRECATE +#if !TORRENT_NO_FPU + // fills the specified vector with the download progress [0, 1] + // of each file in the torrent. The files are ordered as in + // the torrent_info. + TORRENT_DEPRECATED_PREFIX + void file_progress(std::vector& progress) const TORRENT_DEPRECATED; +#endif +#endif + + // flags to be passed in file_progress(). + enum file_progress_flags_t + { + // only calculate file progress at piece granularity. This makes + // the file_progress() call cheaper and also only takes bytes that + // have passed the hash check into account, so progress cannot + // regress in this mode. + piece_granularity = 1 + }; + + // This function fills in the supplied vector with the the number of + // bytes downloaded of each file in this torrent. The progress values are + // ordered the same as the files in the torrent_info. This operation is + // not very cheap. Its complexity is *O(n + mj)*. Where *n* is the number + // of files, *m* is the number of downloading pieces and *j* is the + // number of blocks in a piece. + // + // The ``flags`` parameter can be used to specify the granularity of the + // file progress. If left at the default value of 0, the progress will be + // as accurate as possible, but also more expensive to calculate. If + // ``torrent_handle::piece_granularity`` is specified, the progress will + // be specified in piece granularity. i.e. only pieces that have been + // fully downloaded and passed the hash check count. When specifying + // piece granularity, the operation is a lot cheaper, since libtorrent + // already keeps track of this internally and no calculation is required. + void file_progress(std::vector& progress, int flags = 0) const; + + // If the torrent is in an error state (i.e. ``torrent_status::error`` is + // non-empty), this will clear the error and start the torrent again. + void clear_error() const; + + // ``trackers()`` will return the list of trackers for this torrent. The + // announce entry contains both a string ``url`` which specify the + // announce url for the tracker as well as an int ``tier``, which is + // specifies the order in which this tracker is tried. If you want + // libtorrent to use another list of trackers for this torrent, you can + // use ``replace_trackers()`` which takes a list of the same form as the + // one returned from ``trackers()`` and will replace it. If you want an + // immediate effect, you have to call force_reannounce(). See + // announce_entry. + // + // ``add_tracker()`` will look if the specified tracker is already in the + // set. If it is, it doesn't do anything. If it's not in the current set + // of trackers, it will insert it in the tier specified in the + // announce_entry. + // + // The updated set of trackers will be saved in the resume data, and when + // a torrent is started with resume data, the trackers from the resume + // data will replace the original ones. + std::vector trackers() const; + void replace_trackers(std::vector const&) const; + void add_tracker(announce_entry const&) const; + + // ``add_url_seed()`` adds another url to the torrent's list of url + // seeds. If the given url already exists in that list, the call has no + // effect. The torrent will connect to the server and try to download + // pieces from it, unless it's paused, queued, checking or seeding. + // ``remove_url_seed()`` removes the given url if it exists already. + // ``url_seeds()`` return a set of the url seeds currently in this + // torrent. Note that urls that fails may be removed automatically from + // the list. + // + // See http-seeding_ for more information. + void add_url_seed(std::string const& url) const; + void remove_url_seed(std::string const& url) const; + std::set url_seeds() const; + + // These functions are identical as the ``*_url_seed()`` variants, but + // they operate on `BEP 17`_ web seeds instead of `BEP 19`_. + // + // See http-seeding_ for more information. + void add_http_seed(std::string const& url) const; + void remove_http_seed(std::string const& url) const; + std::set http_seeds() const; + + // add the specified extension to this torrent. The ``ext`` argument is + // a function that will be called from within libtorrent's context + // passing in the internal torrent object and the specified userdata + // pointer. The function is expected to return a shared pointer to + // a torrent_plugin instance. + void add_extension( + boost::function(torrent*, void*)> const& ext + , void* userdata = 0); + + // ``set_metadata`` expects the *info* section of metadata. i.e. The + // buffer passed in will be hashed and verified against the info-hash. If + // it fails, a ``metadata_failed_alert`` will be generated. If it passes, + // a ``metadata_received_alert`` is generated. The function returns true + // if the metadata is successfully set on the torrent, and false + // otherwise. If the torrent already has metadata, this function will not + // affect the torrent, and false will be returned. + bool set_metadata(char const* metadata, int size) const; + + // Returns true if this handle refers to a valid torrent and false if it + // hasn't been initialized or if the torrent it refers to has been + // aborted. Note that a handle may become invalid after it has been added + // to the session. Usually this is because the storage for the torrent is + // somehow invalid or if the filenames are not allowed (and hence cannot + // be opened/created) on your filesystem. If such an error occurs, a + // file_error_alert is generated and all handles that refers to that + // torrent will become invalid. + bool is_valid() const; + + // flags for torrent_session::pause() + enum pause_flags_t { graceful_pause = 1 }; + + // ``pause()``, and ``resume()`` will disconnect all peers and reconnect + // all peers respectively. When a torrent is paused, it will however + // remember all share ratios to all peers and remember all potential (not + // connected) peers. Torrents may be paused automatically if there is a + // file error (e.g. disk full) or something similar. See + // file_error_alert. + // + // To know if a torrent is paused or not, call + // ``torrent_handle::status()`` and inspect ``torrent_status::paused``. + // + // The ``flags`` argument to pause can be set to + // ``torrent_handle::graceful_pause`` which will delay the disconnect of + // peers that we're still downloading outstanding requests from. The + // torrent will not accept any more requests and will disconnect all idle + // peers. As soon as a peer is done transferring the blocks that were + // requested from it, it is disconnected. This is a graceful shut down of + // the torrent in the sense that no downloaded bytes are wasted. + // + // torrents that are auto-managed may be automatically resumed again. It + // does not make sense to pause an auto-managed torrent without making it + // not automanaged first. Torrents are auto-managed by default when added + // to the session. For more information, see queuing_. + void pause(int flags = 0) const; + void resume() const; + + // Explicitly sets the upload mode of the torrent. In upload mode, the + // torrent will not request any pieces. If the torrent is auto managed, + // it will automatically be taken out of upload mode periodically (see + // ``session_settings::optimistic_disk_retry``). Torrents are + // automatically put in upload mode whenever they encounter a disk write + // error. + // + // ``m`` should be true to enter upload mode, and false to leave it. + // + // To test if a torrent is in upload mode, call + // ``torrent_handle::status()`` and inspect + // ``torrent_status::upload_mode``. + void set_upload_mode(bool b) const; + + // Enable or disable share mode for this torrent. When in share mode, the + // torrent will not necessarily be downloaded, especially not the whole + // of it. Only parts that are likely to be distributed to more than 2 + // other peers are downloaded, and only if the previous prediction was + // correct. + void set_share_mode(bool b) const; + + // Instructs libtorrent to flush all the disk caches for this torrent and + // close all file handles. This is done asynchronously and you will be + // notified that it's complete through cache_flushed_alert. + // + // Note that by the time you get the alert, libtorrent may have cached + // more data for the torrent, but you are guaranteed that whatever cached + // data libtorrent had by the time you called + // ``torrent_handle::flush_cache()`` has been written to disk. + void flush_cache() const; + + // Set to true to apply the session global IP filter to this torrent + // (which is the default). Set to false to make this torrent ignore the + // IP filter. + void apply_ip_filter(bool b) const; + + // ``force_recheck`` puts the torrent back in a state where it assumes to + // have no resume data. All peers will be disconnected and the torrent + // will stop announcing to the tracker. The torrent will be added to the + // checking queue, and will be checked (all the files will be read and + // compared to the piece hashes). Once the check is complete, the torrent + // will start connecting to peers again, as normal. + void force_recheck() const; + + // flags used in the save_resume_data call to control additional + // actions or fields to save. + enum save_resume_flags_t + { + // the disk cache will be flushed before creating the resume data. + // This avoids a problem with file timestamps in the resume data in + // case the cache hasn't been flushed yet. + flush_disk_cache = 1, + + // the resume data will contain the metadata from the torrent file as + // well. This is default for any torrent that's added without a + // torrent file (such as a magnet link or a URL). + save_info_dict = 2 + }; + + // ``save_resume_data()`` generates fast-resume data and returns it as an + // entry. This entry is suitable for being bencoded. For more information + // about how fast-resume works, see fast-resume_. + // + // The ``flags`` argument is a bitmask of flags ORed together. see + // save_resume_flags_t + // + // This operation is asynchronous, ``save_resume_data`` will return + // immediately. The resume data is delivered when it's done through an + // save_resume_data_alert. + // + // The fast resume data will be empty in the following cases: + // + // 1. The torrent handle is invalid. + // 2. The torrent is checking (or is queued for checking) its storage, it + // will obviously not be ready to write resume data. + // 3. The torrent hasn't received valid metadata and was started without + // metadata (see libtorrent's metadata-from-peers_ extension) + // + // Note that by the time you receive the fast resume data, it may already + // be invalid if the torrent is still downloading! The recommended + // practice is to first pause the session, then generate the fast resume + // data, and then close it down. Make sure to not remove_torrent() before + // you receive the save_resume_data_alert though. There's no need to + // pause when saving intermittent resume data. + // + //.. warning:: + // If you pause every torrent individually instead of pausing the + // session, every torrent will have its paused state saved in the + // resume data! + // + //.. warning:: + // The resume data contains the modification timestamps for all files. + // If one file has been modified when the torrent is added again, the + // will be rechecked. When shutting down, make sure to flush the disk + // cache before saving the resume data. This will make sure that the + // file timestamps are up to date and won't be modified after saving + // the resume data. The recommended way to do this is to pause the + // torrent, which will flush the cache and disconnect all peers. + // + //.. note:: + // It is typically a good idea to save resume data whenever a torrent + // is completed or paused. In those cases you don't need to pause the + // torrent or the session, since the torrent will do no more writing to + // its files. If you save resume data for torrents when they are + // paused, you can accelerate the shutdown process by not saving resume + // data again for paused torrents. Completed torrents should have their + // resume data saved when they complete and on exit, since their + // statistics might be updated. + // + // In full allocation mode the reume data is never invalidated by + // subsequent writes to the files, since pieces won't move around. This + // means that you don't need to pause before writing resume data in full + // or sparse mode. If you don't, however, any data written to disk after + // you saved resume data and before the session closed is lost. + // + // It also means that if the resume data is out dated, libtorrent will + // not re-check the files, but assume that it is fairly recent. The + // assumption is that it's better to loose a little bit than to re-check + // the entire file. + // + // It is still a good idea to save resume data periodically during + // download as well as when closing down. + // + // Example code to pause and save resume data for all torrents and wait + // for the alerts: + // + // .. code:: c++ + // + // extern int outstanding_resume_data; // global counter of outstanding resume data + // std::vector handles = ses.get_torrents(); + // ses.pause(); + // for (std::vector::iterator i = handles.begin(); + // i != handles.end(); ++i) + // { + // torrent_handle& h = *i; + // if (!h.is_valid()) continue; + // torrent_status s = h.status(); + // if (!s.has_metadata) continue; + // if (!s.need_save_resume_data()) continue; + // + // h.save_resume_data(); + // ++outstanding_resume_data; + // } + // + // while (outstanding_resume_data > 0) + // { + // alert const* a = ses.wait_for_alert(seconds(10)); + // + // // if we don't get an alert within 10 seconds, abort + // if (a == 0) break; + // + // std::auto_ptr holder = ses.pop_alert(); + // + // if (alert_cast(a)) + // { + // process_alert(a); + // --outstanding_resume_data; + // continue; + // } + // + // save_resume_data_alert const* rd = alert_cast(a); + // if (rd == 0) + // { + // process_alert(a); + // continue; + // } + // + // torrent_handle h = rd->handle; + // torrent_status st = h.status(torrent_handle::query_save_path | torrent_handle::query_name); + // std::ofstream out((st.save_path + // + "/" + st.name + ".fastresume").c_str() + // , std::ios_base::binary); + // out.unsetf(std::ios_base::skipws); + // bencode(std::ostream_iterator(out), *rd->resume_data); + // --outstanding_resume_data; + // } + // + //.. note:: + // Note how ``outstanding_resume_data`` is a global counter in this + // example. This is deliberate, otherwise there is a race condition for + // torrents that was just asked to save their resume data, they posted + // the alert, but it has not been received yet. Those torrents would + // report that they don't need to save resume data again, and skipped by + // the initial loop, and thwart the counter otherwise. + void save_resume_data(int flags = 0) const; + + // This function returns true if any whole chunk has been downloaded + // since the torrent was first loaded or since the last time the resume + // data was saved. When saving resume data periodically, it makes sense + // to skip any torrent which hasn't downloaded anything since the last + // time. + // + //.. note:: + // A torrent's resume data is considered saved as soon as the alert is + // posted. It is important to make sure this alert is received and + // handled in order for this function to be meaningful. + bool need_save_resume_data() const; + + // changes whether the torrent is auto managed or not. For more info, + // see queuing_. + void auto_managed(bool m) const; + + // Every torrent that is added is assigned a queue position exactly one + // greater than the greatest queue position of all existing torrents. + // Torrents that are being seeded have -1 as their queue position, since + // they're no longer in line to be downloaded. + // + // When a torrent is removed or turns into a seed, all torrents with + // greater queue positions have their positions decreased to fill in the + // space in the sequence. + // + // ``queue_position()`` returns the torrent's position in the download + // queue. The torrents with the smallest numbers are the ones that are + // being downloaded. The smaller number, the closer the torrent is to the + // front of the line to be started. + // + // The queue position is also available in the torrent_status. + // + // The ``queue_position_*()`` functions adjust the torrents position in + // the queue. Up means closer to the front and down means closer to the + // back of the queue. Top and bottom refers to the front and the back of + // the queue respectively. + int queue_position() const; + void queue_position_up() const; + void queue_position_down() const; + void queue_position_top() const; + void queue_position_bottom() const; + + // Sets or gets the flag that derermines if countries should be resolved + // for the peers of this torrent. It defaults to false. If it is set to + // true, the peer_info structure for the peers in this torrent will have + // their ``country`` member set. See peer_info for more information on + // how to interpret this field. + void resolve_countries(bool r); + bool resolve_countries() const; + + // For SSL torrents, use this to specify a path to a .pem file to use as + // this client's certificate. The certificate must be signed by the + // certificate in the .torrent file to be valid. + // + // The set_ssl_certificate_buffer() overload takes the actual certificate, + // private key and DH params as strings, rather than paths to files. This + // overload is only available when libtorrent is built against boost + // 1.54 or later. + // + // ``cert`` is a path to the (signed) certificate in .pem format + // corresponding to this torrent. + // + // ``private_key`` is a path to the private key for the specified + // certificate. This must be in .pem format. + // + // ``dh_params`` is a path to the Diffie-Hellman parameter file, which + // needs to be in .pem format. You can generate this file using the + // openssl command like this: ``openssl dhparam -outform PEM -out + // dhparams.pem 512``. + // + // ``passphrase`` may be specified if the private key is encrypted and + // requires a passphrase to be decrypted. + // + // Note that when a torrent first starts up, and it needs a certificate, + // it will suspend connecting to any peers until it has one. It's + // typically desirable to resume the torrent after setting the ssl + // certificate. + // + // If you receive a torrent_need_cert_alert, you need to call this to + // provide a valid cert. If you don't have a cert you won't be allowed to + // connect to any peers. + void set_ssl_certificate(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase = ""); + void set_ssl_certificate_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params); + + // Returns the storage implementation for this torrent. This depends on the + // storage contructor function that was passed to add_torrent. + storage_interface* get_storage_impl() const; + + // Returns a pointer to the torrent_info object associated with this + // torrent. The torrent_info object may be a copy of the internal object. + // If the torrent doesn't have metadata, the pointer will not be + // initialized (i.e. a NULL pointer). The torrent may be in a state + // without metadata only if it was started without a .torrent file, e.g. + // by using the libtorrent extension of just supplying a tracker and + // info-hash. + boost::intrusive_ptr torrent_file() const; + +#ifndef TORRENT_NO_DEPRECATE + + // ================ start deprecation ============ + + // deprecated in 1.0 + // use status() instead (with query_save_path) + TORRENT_DEPRECATED_PREFIX + std::string save_path() const TORRENT_DEPRECATED; + + // deprecated in 1.0 + // use status() instead (with query_name) + // returns the name of this torrent, in case it doesn't + // have metadata it returns the name assigned to it + // when it was added. + TORRENT_DEPRECATED_PREFIX + std::string name() const TORRENT_DEPRECATED; + + // use torrent_file() instead + TORRENT_DEPRECATED_PREFIX + const torrent_info& get_torrent_info() const TORRENT_DEPRECATED; + + // deprecated in 0.16, feature will be removed + TORRENT_DEPRECATED_PREFIX + int get_peer_upload_limit(tcp::endpoint ip) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + int get_peer_download_limit(tcp::endpoint ip) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_peer_upload_limit(tcp::endpoint ip, int limit) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void set_peer_download_limit(tcp::endpoint ip, int limit) const TORRENT_DEPRECATED; + + // deprecated in 0.16, feature will be removed + TORRENT_DEPRECATED_PREFIX + void set_ratio(float up_down_ratio) const TORRENT_DEPRECATED; + + // deprecated in 0.16. use status() instead + TORRENT_DEPRECATED_PREFIX + bool is_seed() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_finished() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_paused() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_auto_managed() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_sequential_download() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool has_metadata() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool super_seeding() const TORRENT_DEPRECATED; + + // deprecated in 0.13 + // all these are deprecated, use piece + // priority functions instead + // marks the piece with the given index as filtered + // it will not be downloaded + TORRENT_DEPRECATED_PREFIX + void filter_piece(int index, bool filter) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void filter_pieces(std::vector const& pieces) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + bool is_piece_filtered(int index) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::vector filtered_pieces() const TORRENT_DEPRECATED; + // marks the file with the given index as filtered + // it will not be downloaded + TORRENT_DEPRECATED_PREFIX + void filter_files(std::vector const& files) const TORRENT_DEPRECATED; + + // deprecated in 0.14 + // use save_resume_data() instead. It is async. and + // will return the resume data in an alert + TORRENT_DEPRECATED_PREFIX + entry write_resume_data() const TORRENT_DEPRECATED; + // ================ end deprecation ============ +#endif + + // ``use_interface()`` sets the network interface this torrent will use + // when it opens outgoing connections. By default, it uses the same + // interface as the session uses to listen on. The parameter must be a + // string containing one or more, comma separated, ip-address (either an + // IPv4 or IPv6 address). When specifying multiple interfaces, the + // torrent will round-robin which interface to use for each outgoing + // conneciton. This is useful for clients that are multi-homed. + void use_interface(const char* net_interface) const; + + // Fills the specified ``std::vector`` with the availability for + // each piece in this torrent. libtorrent does not keep track of + // availability for seeds, so if the torrent is seeding the availability + // for all pieces is reported as 0. + // + // The piece availability is the number of peers that we are connected + // that has advertized having a particular piece. This is the information + // that libtorrent uses in order to prefer picking rare pieces. + void piece_availability(std::vector& avail) const; + + // These functions are used to set and get the prioritiy of individual + // pieces. By default all pieces have priority 1. That means that the + // random rarest first algorithm is effectively active for all pieces. + // You may however change the priority of individual pieces. There are 8 + // different priority levels: + // + // 0. piece is not downloaded at all + // 1. normal priority. Download order is dependent on availability + // 2. higher than normal priority. Pieces are preferred over pieces with + // the same availability, but not over pieces with lower availability + // 3. pieces are as likely to be picked as partial pieces. + // 4. pieces are preferred over partial pieces, but not over pieces with + // lower availability + // 5. *currently the same as 4* + // 6. piece is as likely to be picked as any piece with availability 1 + // 7. maximum priority, availability is disregarded, the piece is + // preferred over any other piece with lower priority + // + // The exact definitions of these priorities are implementation details, + // and subject to change. The interface guarantees that higher number + // means higher priority, and that 0 means do not download. + // + // ``piece_priority`` sets or gets the priority for an individual piece, + // specified by ``index``. + // + // ``prioritize_pieces`` takes a vector of integers, one integer per + // piece in the torrent. All the piece priorities will be updated with + // the priorities in the vector. + // + // ``piece_priorities`` returns a vector with one element for each piece + // in the torrent. Each element is the current priority of that piece. + void piece_priority(int index, int priority) const; + int piece_priority(int index) const; + void prioritize_pieces(std::vector const& pieces) const; + std::vector piece_priorities() const; + + // ``index`` must be in the range [0, number_of_files). + // + // ``file_priority()`` queries or sets the priority of file ``index``. + // + // ``prioritize_files()`` takes a vector that has at as many elements as + // there are files in the torrent. Each entry is the priority of that + // file. The function sets the priorities of all the pieces in the + // torrent based on the vector. + // + // ``file_priorities()`` returns a vector with the priorities of all + // files. + // + // The priority values are the same as for piece_priority(). + // + // Whenever a file priority is changed, all other piece priorities are + // reset to match the file priorities. In order to maintain special + // priorities for particular pieces, piece_priority() has to be called + // again for those pieces. + // + // You cannot set the file priorities on a torrent that does not yet have + // metadata or a torrent that is a seed. ``file_priority(int, int)`` and + // prioritize_files() are both no-ops for such torrents. + void file_priority(int index, int priority) const; + int file_priority(int index) const; + void prioritize_files(std::vector const& files) const; + std::vector file_priorities() const; + + // ``force_reannounce()`` will force this torrent to do another tracker + // request, to receive new peers. The ``seconds`` argument specifies how + // many seconds from now to issue the tracker announces. + // + // If the tracker's ``min_interval`` has not passed since the last + // announce, the forced announce will be scheduled to happen immediately + // as the ``min_interval`` expires. This is to honor trackers minimum + // re-announce interval settings. + // + // The ``tracker_index`` argument specifies which tracker to re-announce. + // If set to -1 (which is the default), all trackers are re-announce. + // + // ``force_dht_announce`` will announce the torrent to the DHT + // immediately. + void force_reannounce(int seconds = 0, int tracker_index = -1) const; + void force_dht_announce() const; + +#ifndef TORRENT_NO_DEPRECATE + // forces a reannounce in the specified amount of time. + // This overrides the default announce interval, and no + // announce will take place until the given time has + // timed out. + TORRENT_DEPRECATED_PREFIX + void force_reannounce(boost::posix_time::time_duration) const TORRENT_DEPRECATED; +#endif + + // ``scrape_tracker()`` will send a scrape request to the tracker. A + // scrape request queries the tracker for statistics such as total number + // of incomplete peers, complete peers, number of downloads etc. + // + // This request will specifically update the ``num_complete`` and + // ``num_incomplete`` fields in the torrent_status struct once it + // completes. When it completes, it will generate a scrape_reply_alert. + // If it fails, it will generate a scrape_failed_alert. + void scrape_tracker() const; + + // ``set_upload_limit`` will limit the upload bandwidth used by this + // particular torrent to the limit you set. It is given as the number of + // bytes per second the torrent is allowed to upload. + // ``set_download_limit`` works the same way but for download bandwidth + // instead of upload bandwidth. Note that setting a higher limit on a + // torrent then the global limit + // (``session_settings::upload_rate_limit``) will not override the global + // rate limit. The torrent can never upload more than the global rate + // limit. + // + // ``upload_limit`` and ``download_limit`` will return the current limit + // setting, for upload and download, respectively. + void set_upload_limit(int limit) const; + int upload_limit() const; + void set_download_limit(int limit) const; + int download_limit() const; + + // ``set_sequential_download()`` enables or disables *sequential + // download*. When enabled, the piece picker will pick pieces in sequence + // instead of rarest first. In this mode, piece priorities are ignored, + // with the exception of priority 7, which are still preferred over the + // sequential piece order. + // + // Enabling sequential download will affect the piece distribution + // negatively in the swarm. It should be used sparingly. + void set_sequential_download(bool sd) const; + + // ``connect_peer()`` is a way to manually connect to peers that one + // believe is a part of the torrent. If the peer does not respond, or is + // not a member of this torrent, it will simply be disconnected. No harm + // can be done by using this other than an unnecessary connection attempt + // is made. If the torrent is uninitialized or in queued or checking + // mode, this will throw libtorrent_exception. The second (optional) + // argument will be bitwised ORed into the source mask of this peer. + // Typically this is one of the source flags in peer_info. i.e. + // ``tracker``, ``pex``, ``dht`` etc. + void connect_peer(tcp::endpoint const& adr, int source = 0) const; + + // ``set_max_uploads()`` sets the maximum number of peers that's unchoked + // at the same time on this torrent. If you set this to -1, there will be + // no limit. This defaults to infinite. The primary setting controlling + // this is the global unchoke slots limit, set by unchoke_slots_limit in + // session_settings. + // + // ``max_uploads()`` returns the current settings. + void set_max_uploads(int max_uploads) const; + int max_uploads() const; + + // ``set_max_connections()`` sets the maximum number of connection this + // torrent will open. If all connections are used up, incoming + // connections may be refused or poor connections may be closed. This + // must be at least 2. The default is unlimited number of connections. If + // -1 is given to the function, it means unlimited. There is also a + // global limit of the number of connections, set by + // ``connections_limit`` in session_settings. + // + // ``max_connections()`` returns the current settings. + void set_max_connections(int max_connections) const; + int max_connections() const; + + // sets a username and password that will be sent along in the HTTP-request + // of the tracker announce. Set this if the tracker requires authorization. + void set_tracker_login(std::string const& name + , std::string const& password) const; + + // Moves the file(s) that this torrent are currently seeding from or + // downloading to. If the given ``save_path`` is not located on the same + // drive as the original save path, the files will be copied to the new + // drive and removed from their original location. This will block all + // other disk IO, and other torrents download and upload rates may drop + // while copying the file. + // + // Since disk IO is performed in a separate thread, this operation is + // also asynchronous. Once the operation completes, the + // ``storage_moved_alert`` is generated, with the new path as the + // message. If the move fails for some reason, + // ``storage_moved_failed_alert`` is generated instead, containing the + // error message. + // + // The ``flags`` argument determines the behavior of the copying/moving + // of the files in the torrent. see move_flags_t. + // + // * always_replace_files = 0 + // * fail_if_exist = 1 + // * dont_replace = 2 + // + // ``always_replace_files`` is the default and replaces any file that + // exist in both the source directory and the target directory. + // + // ``fail_if_exist`` first check to see that none of the copy operations + // would cause an overwrite. If it would, it will fail. Otherwise it will + // proceed as if it was in ``always_replace_files`` mode. Note that there + // is an inherent race condition here. If the files in the target + // directory appear after the check but before the copy or move + // completes, they will be overwritten. When failing because of files + // already existing in the target path, the ``error`` of + // ``move_storage_failed_alert`` is set to + // ``boost::system::errc::file_exists``. + // + // The intention is that a client may use this as a probe, and if it + // fails, ask the user which mode to use. The client may then re-issue + // the ``move_storage`` call with one of the other modes. + // + // ``dont_replace`` always takes the existing file in the target + // directory, if there is one. The source files will still be removed in + // that case. + // + // Files that have been renamed to have absolute pahts are not moved by + // this function. Keep in mind that files that don't belong to the + // torrent but are stored in the torrent's directory may be moved as + // well. This goes for files that have been renamed to absolute paths + // that still end up inside the save path. + void move_storage(std::string const& save_path, int flags = 0) const; + + // Renames the file with the given index asynchronously. The rename + // operation is complete when either a file_renamed_alert or + // file_rename_failed_alert is posted. + void rename_file(int index, std::string const& new_name) const; + +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void move_storage(std::wstring const& save_path, int flags = 0) const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_name) const TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // Enables or disabled super seeding/initial seeding for this torrent. + // The torrent needs to be a seed for this to take effect. + void super_seeding(bool on) const; + + // ``info_hash()`` returns the info-hash of the torrent. If this handle + // is to a torrent that hasn't loaded yet (for instance by being added) + // by a URL, the returned value is undefined. + sha1_hash info_hash() const; + + // comparison operators. The order of the torrents is unspecified + // but stable. + bool operator==(const torrent_handle& h) const + { return m_torrent.lock() == h.m_torrent.lock(); } + bool operator!=(const torrent_handle& h) const + { return m_torrent.lock() != h.m_torrent.lock(); } + bool operator<(const torrent_handle& h) const + { return m_torrent.lock() < h.m_torrent.lock(); } + + // This function is intended only for use by plugins and the alert + // dispatch function. Any code that runs in libtorrent's network thread + // may not use the public API of torrent_handle. Doing so results in a + // dead-lock. For such routines, the ``native_handle`` gives access to + // the underlying type representing the torrent. This type does not have + // a stable API and should be relied on as little as possible. + boost::shared_ptr native_handle() const; + + private: + + torrent_handle(boost::weak_ptr const& t) + : m_torrent(t) + {} + + boost::weak_ptr m_torrent; + + }; + + // holds a snapshot of the status of a torrent, as queried by + // torrent_handle::status(). + struct TORRENT_EXPORT torrent_status + { + // hidden + torrent_status(); + ~torrent_status(); + + // compres if the torrent status objects come from the same torrent. i.e. + // only the torrent_handle field is compared. + bool operator==(torrent_status const& st) const + { return handle == st.handle; } + + // a handle to the torrent whose status the object represents. + torrent_handle handle; + + // the different overall states a torrent can be in + enum state_t + { + // The torrent is in the queue for being checked. But there + // currently is another torrent that are being checked. + // This torrent will wait for its turn. + queued_for_checking, + + // The torrent has not started its download yet, and is + // currently checking existing files. + checking_files, + + // The torrent is trying to download metadata from peers. + // This assumes the metadata_transfer extension is in use. + downloading_metadata, + + // The torrent is being downloaded. This is the state + // most torrents will be in most of the time. The progress + // meter will tell how much of the files that has been + // downloaded. + downloading, + + // In this state the torrent has finished downloading but + // still doesn't have the entire torrent. i.e. some pieces + // are filtered and won't get downloaded. + finished, + + // In this state the torrent has finished downloading and + // is a pure seeder. + seeding, + + // If the torrent was started in full allocation mode, this + // indicates that the (disk) storage for the torrent is + // allocated. + allocating, + + // The torrent is currently checking the fastresume data and + // comparing it to the files on disk. This is typically + // completed in a fraction of a second, but if you add a + // large number of torrents at once, they will queue up. + checking_resume_data + }; + + // may be set to an error message describing why the torrent + // was paused, in case it was paused by an error. If the torrent + // is not paused or if it's paused but not because of an error, + // this string is empty. + std::string error; + + // the path to the directory where this torrent's files are stored. + // It's typically the path as was given to async_add_torrent() or + // add_torrent() when this torrent was started. This field is only + // included if the torrent status is queried with + // ``torrent_handle::query_save_path``. + std::string save_path; + + // the name of the torrent. Typically this is derived from the + // .torrent file. In case the torrent was started without metadata, + // and hasn't completely received it yet, it returns the name given + // to it when added to the session. See ``session::add_torrent``. + // This field is only included if the torrent status is queried + // with ``torrent_handle::query_name``. + std::string name; + + // set to point to the ``torrent_info`` object for this torrent. It's + // only included if the torrent status is queried with + // ``torrent_handle::query_torrent_file``. + boost::intrusive_ptr torrent_file; + + // the time until the torrent will announce itself to the tracker. + boost::posix_time::time_duration next_announce; + + // the time the tracker want us to wait until we announce ourself + // again the next time. + boost::posix_time::time_duration announce_interval; + + // the URL of the last working tracker. If no tracker request has + // been successful yet, it's set to an empty string. + std::string current_tracker; + + // the number of bytes downloaded and uploaded to all peers, accumulated, + // *this session* only. The session is considered to restart when a + // torrent is paused and restarted again. When a torrent is paused, these + // counters are reset to 0. If you want complete, persistent, stats, see + // ``all_time_upload`` and ``all_time_download``. + size_type total_download; + size_type total_upload; + + // counts the amount of bytes send and received this session, but only + // the actual payload data (i.e the interesting data), these counters + // ignore any protocol overhead. The session is considered to restart + // when a torrent is paused and restarted again. When a torrent is + // paused, these counters are reset to 0. + size_type total_payload_download; + size_type total_payload_upload; + + // the number of bytes that has been downloaded and that has failed the + // piece hash test. In other words, this is just how much crap that has + // been downloaded since the torrent was last started. If a torrent is + // paused and then restarted again, this counter will be reset. + size_type total_failed_bytes; + + // the number of bytes that has been downloaded even though that data + // already was downloaded. The reason for this is that in some situations + // the same data can be downloaded by mistake. When libtorrent sends + // requests to a peer, and the peer doesn't send a response within a + // certain timeout, libtorrent will re-request that block. Another + // situation when libtorrent may re-request blocks is when the requests + // it sends out are not replied in FIFO-order (it will re-request blocks + // that are skipped by an out of order block). This is supposed to be as + // low as possible. This only counts bytes since the torrent was last + // started. If a torrent is paused and then restarted again, this counter + // will be reset. + size_type total_redundant_bytes; + + // a bitmask that represents which pieces we have (set to true) and the + // pieces we don't have. It's a pointer and may be set to 0 if the + // torrent isn't downloading or seeding. + bitfield pieces; + + // a bitmask representing which pieces has had their hash checked. This + // only applies to torrents in *seed mode*. If the torrent is not in seed + // mode, this bitmask may be empty. + bitfield verified_pieces; + + // the total number of bytes of the file(s) that we have. All this does + // not necessarily has to be downloaded during this session (that's + // ``total_payload_download``). + size_type total_done; + + // the number of bytes we have downloaded, only counting the pieces that + // we actually want to download. i.e. excluding any pieces that we have + // but have priority 0 (i.e. not wanted). + size_type total_wanted_done; + + // The total number of bytes we want to download. This may be smaller + // than the total torrent size in case any pieces are prioritized to 0, + // i.e. not wanted + size_type total_wanted; + + // are accumulated upload and download payload byte counters. They are + // saved in and restored from resume data to keep totals across sessions. + size_type all_time_upload; + size_type all_time_download; + + // the posix-time when this torrent was added. i.e. what ``time(NULL)`` + // returned at the time. + time_t added_time; + + // the posix-time when this torrent was finished. If the torrent is not + // yet finished, this is 0. + time_t completed_time; + + // the time when we, or one of our peers, last saw a complete copy of + // this torrent. + time_t last_seen_complete; + + // The allocation mode for the torrent. See storage_mode_t for the + // options. For more information, see storage-allocation_. + storage_mode_t storage_mode; + + // a value in the range [0, 1], that represents the progress of the + // torrent's current task. It may be checking files or downloading. + float progress; + + // progress parts per million (progress * 1000000) when disabling + // floating point operations, this is the only option to query progress + + // reflects the same value as ``progress``, but instead in a range [0, + // 1000000] (ppm = parts per million). When floating point operations are + // disabled, this is the only alternative to the floating point value in + // progress. + int progress_ppm; + + // the position this torrent has in the download + // queue. If the torrent is a seed or finished, this is -1. + int queue_position; + + // the total rates for all peers for this torrent. These will usually + // have better precision than summing the rates from all peers. The rates + // are given as the number of bytes per second. + int download_rate; + int upload_rate; + + // the total transfer rate of payload only, not counting protocol + // chatter. This might be slightly smaller than the other rates, but if + // projected over a long time (e.g. when calculating ETA:s) the + // difference may be noticeable. + int download_payload_rate; + int upload_payload_rate; + + // the number of peers that are seeding that this client is + // currently connected to. + int num_seeds; + + // the number of peers this torrent currently is connected to. Peer + // connections that are in the half-open state (is attempting to connect) + // or are queued for later connection attempt do not count. Although they + // are visible in the peer list when you call get_peer_info(). + int num_peers; + + // if the tracker sends scrape info in its announce reply, these fields + // will be set to the total number of peers that have the whole file and + // the total number of peers that are still downloading. set to -1 if the + // tracker did not send any scrape data in its announce reply. + int num_complete; + int num_incomplete; + + // the number of seeds in our peer list and the total number of peers + // (including seeds). We are not necessarily connected to all the peers + // in our peer list. This is the number of peers we know of in total, + // including banned peers and peers that we have failed to connect to. + int list_seeds; + int list_peers; + + // the number of peers in this torrent's peer list that is a candidate to + // be connected to. i.e. It has fewer connect attempts than the max fail + // count, it is not a seed if we are a seed, it is not banned etc. If + // this is 0, it means we don't know of any more peers that we can try. + int connect_candidates; + + // the number of pieces that has been downloaded. It is equivalent to: + // ``std::accumulate(pieces->begin(), pieces->end())``. So you don't have + // to count yourself. This can be used to see if anything has updated + // since last time if you want to keep a graph of the pieces up to date. + int num_pieces; + + // the number of distributed copies of the torrent. Note that one copy + // may be spread out among many peers. It tells how many copies there are + // currently of the rarest piece(s) among the peers this client is + // connected to. + int distributed_full_copies; + + // tells the share of pieces that have more copies than the rarest + // piece(s). Divide this number by 1000 to get the fraction. + // + // For example, if ``distributed_full_copies`` is 2 and + // ``distrbuted_fraction`` is 500, it means that the rarest pieces have + // only 2 copies among the peers this torrent is connected to, and that + // 50% of all the pieces have more than two copies. + // + // If we are a seed, the piece picker is deallocated as an optimization, + // and piece availability is no longer tracked. In this case the + // distributed copies members are set to -1. + int distributed_fraction; + + // the number of distributed copies of the file. note that one copy may + // be spread out among many peers. This is a floating point + // representation of the distributed copies. + // + // the integer part tells how many copies + // there are of the rarest piece(s) + // + // the fractional part tells the fraction of pieces that + // have more copies than the rarest piece(s). + float distributed_copies; + + // the size of a block, in bytes. A block is a sub piece, it is the + // number of bytes that each piece request asks for and the number of + // bytes that each bit in the ``partial_piece_info``'s bitset represents, + // see get_download_queue(). This is typically 16 kB, but it may be + // larger if the pieces are larger. + int block_size; + + // the number of unchoked peers in this torrent. + int num_uploads; + + // the number of peer connections this torrent has, including half-open + // connections that hasn't completed the bittorrent handshake yet. This + // is always >= ``num_peers``. + int num_connections; + + // the set limit of upload slots (unchoked peers) for this torrent. + int uploads_limit; + + // the set limit of number of connections for this torrent. + int connections_limit; + + // the number of peers in this torrent that are waiting for more + // bandwidth quota from the torrent rate limiter. This can determine if + // the rate you get from this torrent is bound by the torrents limit or + // not. If there is no limit set on this torrent, the peers might still + // be waiting for bandwidth quota from the global limiter, but then they + // are counted in the ``session_status`` object. + int up_bandwidth_queue; + int down_bandwidth_queue; + + // the number of seconds since any peer last uploaded from this torrent + // and the last time a downloaded piece passed the hash check, + // respectively. + int time_since_upload; + int time_since_download; + + // These keep track of the number of seconds this torrent has been active + // (not paused) and the number of seconds it has been active while being + // finished and active while being a seed. ``seeding_time`` should be <= + // ``finished_time`` which should be <= ``active_time``. They are all + // saved in and restored from resume data, to keep totals across + // sessions. + int active_time; + int finished_time; + int seeding_time; + + // A rank of how important it is to seed the torrent, it is used to + // determine which torrents to seed and which to queue. It is based on + // the peer to seed ratio from the tracker scrape. For more information, + // see queuing_. Higher value means more important to seed + int seed_rank; + + // the number of seconds since this torrent acquired scrape data. + // If it has never done that, this value is -1. + int last_scrape; + + // the number of regions of non-downloaded pieces in the torrent. This is + // an interesting metric on windows vista, since there is a limit on the + // number of sparse regions in a single file there. + int sparse_regions; + + // the priority of this torrent + int priority; + + // the main state the torrent is in. See torrent_status::state_t. + state_t state; + + // true if this torrent has unsaved changes + // to its download state and statistics since the last resume data + // was saved. + bool need_save_resume; + + // true if the session global IP filter applies + // to this torrent. This defaults to true. + bool ip_filter_applies; + + // true if the torrent is blocked from downloading. This typically + // happens when a disk write operation fails. If the torrent is + // auto-managed, it will periodically be taken out of this state, in the + // hope that the disk condition (be it disk full or permission errors) + // has been resolved. If the torrent is not auto-managed, you have to + // explicitly take it out of the upload mode by calling set_upload_mode() + // on the torrent_handle. + bool upload_mode; + + // true if the torrent is currently in share-mode, i.e. not downloading + // the torrent, but just helping the swarm out. + bool share_mode; + + // true if the torrent is in super seeding mode + bool super_seeding; + + // set to true if the torrent is paused and false otherwise. It's only + // true if the torrent itself is paused. If the torrent is not running + // because the session is paused, this is still false. To know if a + // torrent is active or not, you need to inspect both + // ``torrent_status::paused`` and ``session::is_paused()``. + bool paused; + + // set to true if the torrent is auto managed, i.e. libtorrent is + // responsible for determining whether it should be started or queued. + // For more info see queuing_ + bool auto_managed; + + // true when the torrent is in sequential download mode. In this mode + // pieces are downloaded in order rather than rarest first. + bool sequential_download; + + // true if all pieces have been downloaded. + bool is_seeding; + + // true if all pieces that have a priority > 0 are downloaded. There is + // only a distinction between finished and seeding if some pieces or + // files have been set to priority 0, i.e. are not downloaded. + bool is_finished; + + // true if this torrent has metadata (either it was started from a + // .torrent file or the metadata has been downloaded). The only scenario + // where this can be false is when the torrent was started torrent-less + // (i.e. with just an info-hash and tracker ip, a magnet link for + // instance). + bool has_metadata; + + // true if there has ever been an incoming connection attempt to this + // torrent. + bool has_incoming; + + // true if the torrent is in seed_mode. If the torrent was started in + // seed mode, it will leave seed mode once all pieces have been checked + // or as soon as one piece fails the hash check. + bool seed_mode; + + // this is true if this torrent's storage is currently being moved from + // one location to another. This may potentially be a long operation + // if a large file ends up being copied from one drive to another. + bool moving_storage; + + // the info-hash for this torrent + sha1_hash info_hash; + }; + +} + +#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp new file mode 100644 index 0000000000..5ad3e38d8a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/torrent_info.hpp @@ -0,0 +1,763 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_INFO_HPP_INCLUDED +#define TORRENT_TORRENT_INFO_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/ptime.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/file_storage.hpp" +#include "libtorrent/copy_ptr.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/policy.hpp" // for policy::peer + +namespace libtorrent +{ + class peer_connection; + struct session_settings; + + enum + { + // wait at least 5 seconds before retrying a failed tracker + tracker_retry_delay_min = 5 + // when tracker_failed_max trackers + // has failed, wait 60 minutes instead + , tracker_retry_delay_max = 60 * 60 + }; + + TORRENT_EXTRA_EXPORT int merkle_num_leafs(int); + TORRENT_EXTRA_EXPORT int merkle_num_nodes(int); + TORRENT_EXTRA_EXPORT int merkle_get_parent(int); + TORRENT_EXTRA_EXPORT int merkle_get_sibling(int); + TORRENT_EXTRA_EXPORT void trim_path_element(std::string& path_element); + + // this class holds information about one bittorrent tracker, as it + // relates to a specific torrent. + struct TORRENT_EXPORT announce_entry + { + // constructs a tracker announce entry with ``u`` as the URL. + announce_entry(std::string const& u); + announce_entry(); + ~announce_entry(); + + // tracker URL as it appeared in the torrent file + std::string url; + + // the current ``&trackerid=`` argument passed to the tracker. + // this is optional and is normally empty (in which case no + // trackerid is sent). + std::string trackerid; + + // if this tracker has returned an error or warning message + // that message is stored here + std::string message; + + // if this tracker failed the last time it was contacted + // this error code specifies what error occurred + error_code last_error; + + // returns the number of seconds to the next announce on + // this tracker. ``min_announce_in()`` returns the number of seconds until we are + // allowed to force another tracker update with this tracker. + // + // If the last time this tracker was contacted failed, ``last_error`` is the error + // code describing what error occurred. + int next_announce_in() const; + int min_announce_in() const; + + // the time of next tracker announce + ptime next_announce; + + // no announces before this time + ptime min_announce; + + // TODO: include the number of peers received from this tracker, at last announce + + // these are either -1 or the scrape information this tracker last responded with. *incomplete* is + // the current number of downloaders in the swarm, *complete* is the current number + // of seeds in the swarm and *downloaded* is the cumulative number of completed + // downloads of this torrent, since the beginning of time (from this tracker's point + // of view). + + // if this tracker has returned scrape data, these fields are filled + // in with valid numbers. Otherwise they are set to -1. + // the number of current downloaders + int scrape_incomplete; + int scrape_complete; + int scrape_downloaded; + + // the tier this tracker belongs to + boost::uint8_t tier; + + // the max number of failures to announce to this tracker in + // a row, before this tracker is not used anymore. 0 means unlimited + boost::uint8_t fail_limit; + + // the number of times in a row we have failed to announce to this + // tracker. + boost::uint8_t fails:7; + + // true while we're waiting for a response from the tracker. + bool updating:1; + + // flags for the source bitmask, each indicating where + // we heard about this tracker + enum tracker_source + { + // the tracker was part of the .torrent file + source_torrent = 1, + // the tracker was added programatically via the add_troacker()_ function + source_client = 2, + // the tracker was part of a magnet link + source_magnet_link = 4, + // the tracker was received from the swarm via tracker exchange + source_tex = 8 + }; + + // a bitmask specifying which sources we got this tracker from. + boost::uint8_t source:4; + + // set to true the first time we receive a valid response + // from this tracker. + bool verified:1; + + // set to true when we get a valid response from an announce + // with event=started. If it is set, we won't send start in the subsequent + // announces. + bool start_sent:1; + + // set to true when we send a event=completed. + bool complete_sent:1; + + // this is false the stats sent to this tracker will be 0 + bool send_stats:1; + + // reset announce counters and clears the started sent flag. + // The announce_entry will look like we've never talked to + // the tracker. + void reset() + { + start_sent = false; + next_announce = min_time(); + min_announce = min_time(); + } + + // updates the failure counter and time-outs for re-trying. + // This is called when the tracker announce fails. + void failed(session_settings const& sett, int retry_interval = 0); + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 1.0 + TORRENT_DEPRECATED_PREFIX + bool will_announce(ptime now) const TORRENT_DEPRECATED + { + return now <= next_announce + && (fails < fail_limit || fail_limit == 0) + && !updating; + } +#endif + + // returns true if we can announec to this tracker now. + // The current time is passed in as ``now``. The ``is_seed`` + // argument is necessary because once we become a seed, we + // need to announce right away, even if the re-announce timer + // hasn't expired yet. + bool can_announce(ptime now, bool is_seed) const; + + // returns true if the last time we tried to announce to this + // tracker succeeded, or if we haven't tried yet. + bool is_working() const + { return fails == 0; } + + // trims whitespace characters from the beginning of the URL. + void trim(); + }; + + // the web_seed_entry holds information about a web seed (also known + // as URL seed or HTTP seed). It is essentially a URL with some state + // associated with it. For more information, see `BEP 17`_ and `BEP 19`_. + struct web_seed_entry + { + // http seeds are different from url seeds in the + // protocol they use. http seeds follows the original + // http seed spec. by John Hoffman + enum type_t { url_seed, http_seed }; + + typedef std::vector > headers_t; + + web_seed_entry(std::string const& url_, type_t type_ + , std::string const& auth_ = std::string() + , headers_t const& extra_headers_ = headers_t()); + + // URL and type comparison + bool operator==(web_seed_entry const& e) const + { return url == e.url && type == e.type; } + + // URL and type less-than comparison + bool operator<(web_seed_entry const& e) const + { + if (url < e.url) return true; + if (url > e.url) return false; + return type < e.type; + } + + // The URL of the web seed + std::string url; + + // The type of web seed (see type_t) + type_t type; + + // Optional authentication. If this is set, it's passed + // in as HTTP basic auth to the web seed. The format is: + // username:password. + std::string auth; + + // Any extra HTTP headers that need to be passed to the web seed + headers_t extra_headers; + + // if this is > now, we can't reconnect yet + ptime retry; + + // this is initialized to true, but if we discover the + // server not to support it, it's set to false, and we + // make larger requests. + bool supports_keepalive; + + // this indicates whether or not we're resolving the + // hostname of this URL + bool resolving; + + // if the user wanted to remove this while + // we were resolving it. In this case, we set + // the removed flag to true, to make the resolver + // callback remove it + bool removed; + + // if the hostname of the web seed has been resolved, + // this is its IP address + tcp::endpoint endpoint; + + // this is the peer_info field used for the + // connection, just to count hash failures + // it's also used to hold the peer_connection + // pointer, when the web seed is connected + policy::ipv4_peer peer_info; + + // if the web server doesn't support keepalive or a block request was + // interrupted, the block received so far is kept here for the next + // connection to pick up + peer_request restart_request; + std::vector restart_piece; + }; + +#ifndef BOOST_NO_EXCEPTIONS + // for backwards compatibility with 0.14 + typedef libtorrent_exception invalid_torrent_file; +#endif + + // This class represents the information stored in a .torrent file + class TORRENT_EXPORT torrent_info : public intrusive_ptr_base + { + public: + + // The constructor that takes an info-hash will initialize the info-hash + // to the given value, but leave all other fields empty. This is used + // internally when downloading torrents without the metadata. The + // metadata will be created by libtorrent as soon as it has been + // downloaded from the swarm. + // + // The constructor that takes a lazy_entry will create a torrent_info + // object from the information found in the given torrent_file. The + // lazy_entry represents a tree node in an bencoded file. To load an + // ordinary .torrent file into a lazy_entry, use lazy_bdecode(). + // + // The version that takes a buffer pointer and a size will decode it as a + // .torrent file and initialize the torrent_info object for you. + // + // The version that takes a filename will simply load the torrent file + // and decode it inside the constructor, for convenience. This might not + // be the most suitable for applications that want to be able to report + // detailed errors on what might go wrong. + // + // There is an upper limit on the size of the torrent file that will be + // loaded by the overload taking a filename. If it's important that even + // very large torrent files are loaded, use one of the other overloads. + // + // The overloads that takes an ``error_code const&`` never throws if an + // error occur, they will simply set the error code to describe what went + // wrong and not fully initialize the torrent_info object. The overloads + // that do not take the extra error_code parameter will always throw if + // an error occurs. These overloads are not available when building + // without exception support. + // + // The ``flags`` argument is currently unused. +#ifndef BOOST_NO_EXCEPTIONS + torrent_info(lazy_entry const& torrent_file, int flags = 0); + torrent_info(char const* buffer, int size, int flags = 0); + torrent_info(std::string const& filename, int flags = 0); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + torrent_info(std::wstring const& filename, int flags = 0) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE +#endif + torrent_info(torrent_info const& t, int flags = 0); + torrent_info(sha1_hash const& info_hash, int flags = 0); + torrent_info(lazy_entry const& torrent_file, error_code& ec, int flags = 0); + torrent_info(char const* buffer, int size, error_code& ec, int flags = 0); + torrent_info(std::string const& filename, error_code& ec, int flags = 0); +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + torrent_info(std::wstring const& filename, error_code& ec, int flags = 0) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // frees all storage associated with this torrent_info object + ~torrent_info(); + + // The file_storage object contains the information on how to map the pieces to + // files. It is separated from the torrent_info object because when creating torrents + // a storage object needs to be created without having a torrent file. When renaming files + // in a storage, the storage needs to make its own copy of the file_storage in order + // to make its mapping differ from the one in the torrent file. + // + // ``orig_files()`` returns the original (unmodified) file storage for this torrent. This + // is used by the web server connection, which needs to request files with the original + // names. Filename may be chaged using ``torrent_info::rename_file()``. + // + // For more information on the file_storage object, see the separate document on how + // to create torrents. + file_storage const& files() const { return m_files; } + file_storage const& orig_files() const { return m_orig_files ? *m_orig_files : m_files; } + + // Renames a the file with the specified index to the new name. The new filename is + // reflected by the ``file_storage`` returned by ``files()`` but not by the one + // returned by ``orig_files()``. + // + // If you want to rename the base name of the torrent (for a multifile torrent), you + // can copy the ``file_storage`` (see files() and orig_files() ), change the name, and + // then use `remap_files()`_. + // + // The ``new_filename`` can both be a relative path, in which case the file name + // is relative to the ``save_path`` of the torrent. If the ``new_filename`` is + // an absolute path (i.e. ``is_complete(new_filename) == true``), then the file + // is detached from the ``save_path`` of the torrent. In this case the file is + // not moved when move_storage() is invoked. + void rename_file(int index, std::string const& new_filename) + { + copy_on_write(); + m_files.rename_file(index, new_filename); + } +#ifndef TORRENT_NO_DEPRECATE +#if TORRENT_USE_WSTRING + // all wstring APIs are deprecated since 0.16.11 + // instead, use the wchar -> utf8 conversion functions + // and pass in utf8 strings + TORRENT_DEPRECATED_PREFIX + void rename_file(int index, std::wstring const& new_filename) TORRENT_DEPRECATED; +#endif // TORRENT_USE_WSTRING +#endif // TORRENT_NO_DEPRECATE + + // Remaps the file storage to a new file layout. This can be used to, for instance, + // download all data in a torrent to a single file, or to a number of fixed size + // sector aligned files, regardless of the number and sizes of the files in the torrent. + // + // The new specified ``file_storage`` must have the exact same size as the current one. + void remap_files(file_storage const& f); + + // ``add_tracker()`` adds a tracker to the announce-list. The ``tier`` determines the order in + // which the trackers are to be tried. + // + // The ``trackers()`` function will return a sorted vector of ``announce_entry``. + // Each announce entry contains a string, which is the tracker url, and a tier index. The + // tier index is the high-level priority. No matter which trackers that works or not, the + // ones with lower tier will always be tried before the one with higher tier number. + // For more information, see announce_entry_. + void add_tracker(std::string const& url, int tier = 0); + std::vector const& trackers() const { return m_urls; } + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16. Use web_seeds() instead + TORRENT_DEPRECATED_PREFIX + std::vector url_seeds() const TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + std::vector http_seeds() const TORRENT_DEPRECATED; +#endif // TORRENT_NO_DEPRECATE + + // ``web_seeds()`` returns all url seeds and http seeds in the torrent. Each entry + // is a ``web_seed_entry`` and may refer to either a url seed or http seed. + // + // ``add_url_seed()`` and ``add_http_seed()`` adds one url to the list of + // url/http seeds. Currently, the only transport protocol supported for the url + // is http. + // + // The ``extern_auth`` argument can be used for other athorization schemese than + // basic HTTP authorization. If set, it will override any username and password + // found in the URL itself. The string will be sent as the HTTP authorization header's + // value (without specifying "Basic"). + // + // The ``extra_headers`` argument defaults to an empty list, but can be used to + // insert custom HTTP headers in the requests to a specific web seed. + // + // See http-seeding_ for more information. + void add_url_seed(std::string const& url + , std::string const& extern_auth = std::string() + , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); + void add_http_seed(std::string const& url + , std::string const& extern_auth = std::string() + , web_seed_entry::headers_t const& extra_headers = web_seed_entry::headers_t()); + std::vector const& web_seeds() const + { return m_web_seeds; } + + // ``total_size()``, ``piece_length()`` and ``num_pieces()`` returns the total + // number of bytes the torrent-file represents (all the files in it), the number of byte for + // each piece and the total number of pieces, respectively. The difference between + // ``piece_size()`` and ``piece_length()`` is that ``piece_size()`` takes + // the piece index as argument and gives you the exact size of that piece. It will always + // be the same as ``piece_length()`` except in the case of the last piece, which may + // be smaller. + size_type total_size() const { return m_files.total_size(); } + int piece_length() const { return m_files.piece_length(); } + int num_pieces() const { return m_files.num_pieces(); } + + // returns the info-hash of the torrent + const sha1_hash& info_hash() const { return m_info_hash; } + +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 1.0. Use the variants that take an index instead + // internal_file_entry is no longer exposed in the API + typedef file_storage::iterator file_iterator; + typedef file_storage::reverse_iterator reverse_file_iterator; + + // This class will need some explanation. First of all, to get a list of all files + // in the torrent, you can use ``begin_files()``, ``end_files()``, + // ``rbegin_files()`` and ``rend_files()``. These will give you standard vector + // iterators with the type ``internal_file_entry``, which is an internal type. + // + // You can resolve it into the public representation of a file (``file_entry``) + // using the ``file_storage::at`` function, which takes an index and an iterator. + TORRENT_DEPRECATED_PREFIX + file_iterator begin_files() const TORRENT_DEPRECATED { return m_files.begin_deprecated(); } + TORRENT_DEPRECATED_PREFIX + file_iterator end_files() const TORRENT_DEPRECATED { return m_files.end_deprecated(); } + reverse_file_iterator rbegin_files() const TORRENT_DEPRECATED { return m_files.rbegin_deprecated(); } + TORRENT_DEPRECATED_PREFIX + reverse_file_iterator rend_files() const TORRENT_DEPRECATED { return m_files.rend_deprecated(); } + + TORRENT_DEPRECATED_PREFIX + file_iterator file_at_offset(size_type offset) const TORRENT_DEPRECATED + { return m_files.file_at_offset_deprecated(offset); } + +#endif // TORRENT_NO_DEPRECATE + + // If you need index-access to files you can use the ``num_files()`` and ``file_at()`` + // to access files using indices. + int num_files() const { return m_files.num_files(); } + file_entry file_at(int index) const { return m_files.at(index); } + + // This function will map a piece index, a byte offset within that piece and + // a size (in bytes) into the corresponding files with offsets where that data + // for that piece is supposed to be stored. See file_slice. + std::vector map_block(int piece, size_type offset, int size) const + { return m_files.map_block(piece, offset, size); } + + // This function will map a range in a specific file into a range in the torrent. + // The ``file_offset`` parameter is the offset in the file, given in bytes, where + // 0 is the start of the file. See peer_request. + // + // The input range is assumed to be valid within the torrent. ``file_offset`` + // + ``size`` is not allowed to be greater than the file size. ``file_index`` + // must refer to a valid file, i.e. it cannot be >= ``num_files()``. + peer_request map_file(int file, size_type offset, int size) const + { return m_files.map_file(file, offset, size); } + +#ifndef TORRENT_NO_DEPRECATE +// ------- start deprecation ------- +// these functions will be removed in a future version + TORRENT_DEPRECATED_PREFIX + torrent_info(entry const& torrent_file) TORRENT_DEPRECATED; + TORRENT_DEPRECATED_PREFIX + void print(std::ostream& os) const TORRENT_DEPRECATED; +// ------- end deprecation ------- +#endif + + // Returns the SSL root certificate for the torrent, if it is an SSL + // torrent. Otherwise returns an empty string. The certificate is + // the the public certificate in x509 format. + std::string ssl_cert() const; + + // returns true if this torrent_info object has a torrent loaded. + // This is primarily used to determine if a magnet link has had its + // metadata resolved yet or not. + bool is_valid() const { return m_files.is_valid(); } + + // returns true if this torrent is private. i.e., it should not be + // distributed on the trackerless network (the kademlia DHT). + bool priv() const { return m_private; } + + // returns true if this is an i2p torrent. This is determined by whether + // or not it has a tracker whose URL domain name ends with ".i2p". i2p + // torrents disable the DHT and local peer discovery as well as talking + // to peers over anything other than the i2p network. + bool is_i2p() const { return m_i2p; } + + // ``hash_for_piece()`` takes a piece-index and returns the 20-bytes sha1-hash for that + // piece and ``info_hash()`` returns the 20-bytes sha1-hash for the info-section of the + // torrent file. + // ``hash_for_piece_ptr()`` returns a pointer to the 20 byte sha1 digest for the piece. + // Note that the string is not null-terminated. + int piece_size(int index) const { return m_files.piece_size(index); } + sha1_hash hash_for_piece(int index) const + { return sha1_hash(hash_for_piece_ptr(index)); } + char const* hash_for_piece_ptr(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_files.num_pieces()); + if (is_merkle_torrent()) + { + TORRENT_ASSERT(index < int(m_merkle_tree.size() - m_merkle_first_leaf)); + return (const char*)&m_merkle_tree[m_merkle_first_leaf + index][0]; + } + else + { + TORRENT_ASSERT(m_piece_hashes); + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + TORRENT_ASSERT(index < int(m_info_section_size / 20)); + return &m_piece_hashes[index*20]; + } + } + + // ``merkle_tree()`` returns a reference to the merkle tree for this torrent, if any. + // + // ``set_merkle_tree()`` moves the passed in merkle tree into the torrent_info object. + // i.e. ``h`` will not be identical after the call. You need to set the merkle tree for + // a torrent that you've just created (as a merkle torrent). The merkle tree is retrieved + // from the ``create_torrent::merkle_tree()`` function, and need to be saved separately + // from the torrent file itself. Once it's added to libtorrent, the merkle tree will be + // persisted in the resume data. + std::vector const& merkle_tree() const { return m_merkle_tree; } + void set_merkle_tree(std::vector& h) + { TORRENT_ASSERT(h.size() == m_merkle_tree.size() ); m_merkle_tree.swap(h); } + + // ``name()`` returns the name of the torrent. + // + // ``comment()`` returns the comment associated with the torrent. If there's no comment, + // it will return an empty string. ``creation_date()`` returns the creation date of + // the torrent as time_t (`posix time`_). If there's no time stamp in the torrent file, + // the optional object will be uninitialized. + // + // Both the name and the comment is UTF-8 encoded strings. + // + // ``creator()`` returns the creator string in the torrent. If there is no creator string + // it will return an empty string. + // + // .. _`posix time`: http://www.opengroup.org/onlinepubs/009695399/functions/time.html + const std::string& name() const { return m_files.name(); } + boost::optional creation_date() const; + const std::string& creator() const + { return m_created_by; } + const std::string& comment() const + { return m_comment; } + + // dht nodes to add to the routing table/bootstrap from + typedef std::vector > nodes_t; + + // If this torrent contains any DHT nodes, they are put in this vector in their original + // form (host name and port number). + nodes_t const& nodes() const + { return m_nodes; } + + // This is used when creating torrent. Use this to add a known DHT node. It may + // be used, by the client, to bootstrap into the DHT network. + void add_node(std::pair const& node) + { m_nodes.push_back(node); } + + // populates the torrent_info by providing just the info-dict buffer. This is used when + // loading a torrent from a magnet link for instance, where we only have the info-dict. + // The lazy_entry ``e`` points to a parsed info-dictionary. ``ec`` returns an error code + // if something fails (typically if the info dictionary is malformed). ``flags`` are currently + // unused. + bool parse_info_section(lazy_entry const& e, error_code& ec, int flags); + + // This function looks up keys from the info-dictionary of the loaded torrent file. + // It can be used to access extension values put in the .torrent file. If the specified + // key cannot be found, it returns NULL. + lazy_entry const* info(char const* key) const + { + if (m_info_dict.type() == lazy_entry::none_t) + { + error_code ec; + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); + if (ec) return NULL; + } + return m_info_dict.dict_find(key); + } + + // swap the content of this and ``ti```. + void swap(torrent_info& ti); + + // ``metadata()`` returns a the raw info section of the torrent file. The size + // of the metadata is returned by ``metadata_size()``. + int metadata_size() const { return m_info_section_size; } + boost::shared_array metadata() const + { return m_info_section; } + + // internal + bool add_merkle_nodes(std::map const& subtree + , int piece); + std::map build_merkle_list(int piece) const; + + // returns whether or not this is a merkle torrent. + // see BEP30__. + // + // __ http://bittorrent.org/beps/bep_0030.html + bool is_merkle_torrent() const { return !m_merkle_tree.empty(); } + + // if we're logging member offsets, we need access to them + private: + +#if TORRENT_USE_INVARIANT_CHECKS + friend class invariant_access; + void check_invariant() const; +#endif + + // not assignable + torrent_info const& operator=(torrent_info const&); + + void copy_on_write(); + bool parse_torrent_file(lazy_entry const& libtorrent, error_code& ec, int flags); + + // the index to the first leaf. This is where the hash for the + // first piece is stored + boost::uint32_t m_merkle_first_leaf; + + file_storage m_files; + + // if m_files is modified, it is first copied into + // m_orig_files so that the original name and + // filenames are preserved. + copy_ptr m_orig_files; + + // the urls to the trackers + std::vector m_urls; + std::vector m_web_seeds; + nodes_t m_nodes; + + // if this is a merkle torrent, this is the merkle + // tree. It has space for merkle_num_nodes(merkle_num_leafs(num_pieces)) + // hashes + std::vector m_merkle_tree; + + // this is a copy of the info section from the torrent. + // it use maintained in this flat format in order to + // make it available through the metadata extension + boost::shared_array m_info_section; + + // this is a pointer into the m_info_section buffer + // pointing to the first byte of the first sha-1 hash + char const* m_piece_hashes; + + // if a comment is found in the torrent file + // this will be set to that comment + std::string m_comment; + + // an optional string naming the software used + // to create the torrent file + std::string m_created_by; + + // the info section parsed. points into m_info_section + // parsed lazily + mutable lazy_entry m_info_dict; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + time_t m_creation_date; + + // the hash that identifies this torrent + sha1_hash m_info_hash; + + // the number of bytes in m_info_section + boost::uint32_t m_info_section_size:24; + + // this is used when creating a torrent. If there's + // only one file there are cases where it's impossible + // to know if it should be written as a multifile torrent + // or not. e.g. test/test there's one file and one directory + // and they have the same name. + bool m_multifile:1; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private:1; + + // this is true if one of the trackers has an .i2p top + // domain in its hostname. This means the DHT and LSD + // features are disabled for this torrent (unless the + // settings allows mixing i2p peers with regular peers) + bool m_i2p:1; + }; + +} + +#endif // TORRENT_TORRENT_INFO_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp new file mode 100644 index 0000000000..a48dab2160 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/tracker_manager.hpp @@ -0,0 +1,307 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TRACKER_MANAGER_HPP_INCLUDED +#define TORRENT_TRACKER_MANAGER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" // peer_entry +#include "libtorrent/session_settings.hpp" // proxy_settings +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/union_endpoint.hpp" +#include "libtorrent/udp_socket.hpp" // for udp_socket_observer +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +namespace libtorrent +{ + struct request_callback; + class tracker_manager; + struct timeout_handler; + struct tracker_connection; + namespace aux { struct session_impl; } + + // returns -1 if gzip header is invalid or the header size in bytes + TORRENT_EXTRA_EXPORT int gzip_header(const char* buf, int size); + + struct TORRENT_EXTRA_EXPORT tracker_request + { + tracker_request() + : kind(announce_request) + , downloaded(-1) + , uploaded(-1) + , left(-1) + , corrupt(0) + , redundant(0) + , listen_port(0) + , event(none) + , key(0) + , num_want(0) + , send_stats(true) + , apply_ip_filter(true) +#ifdef TORRENT_USE_OPENSSL + , ssl_ctx(0) +#endif + {} + + enum + { + announce_request, + scrape_request + } kind; + + enum event_t + { + none, + completed, + started, + stopped, + paused + }; + + sha1_hash info_hash; + peer_id pid; + size_type downloaded; + size_type uploaded; + size_type left; + size_type corrupt; + size_type redundant; + unsigned short listen_port; + event_t event; + std::string url; + std::string trackerid; + boost::uint32_t key; + int num_want; + address bind_ip; + bool send_stats; + bool apply_ip_filter; +#ifdef TORRENT_USE_OPENSSL + boost::asio::ssl::context* ssl_ctx; +#endif + }; + + struct TORRENT_EXTRA_EXPORT request_callback + { + friend class tracker_manager; + request_callback(): m_manager(0) {} + virtual ~request_callback() {} + virtual void tracker_warning(tracker_request const& req + , std::string const& msg) = 0; + virtual void tracker_scrape_response(tracker_request const& /*req*/ + , int /*complete*/, int /*incomplete*/, int /*downloads*/ + , int /*downloaders*/) {} + virtual void tracker_response( + tracker_request const& req + , address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& trackerid) = 0; + virtual void tracker_request_error( + tracker_request const& req + , int response_code + , error_code const& ec + , const std::string& msg + , int retry_interval) = 0; + + union_endpoint m_tracker_address; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + virtual void debug_log(const char* fmt, ...) const = 0; +#else + private: +#endif + tracker_manager* m_manager; + }; + + struct TORRENT_EXTRA_EXPORT timeout_handler + : intrusive_ptr_base + , boost::noncopyable + { + timeout_handler(io_service& str); + + void set_timeout(int completion_timeout, int read_timeout); + void restart_read_timeout(); + void cancel(); + bool cancelled() const { return m_abort; } + + virtual void on_timeout(error_code const& ec) = 0; + virtual ~timeout_handler() {} + + io_service& get_io_service() { return m_timeout.get_io_service(); } + + private: + + void timeout_callback(error_code const&); + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + // used for timeouts + // this is set when the request has been sent + ptime m_start_time; + // this is set every time something is received + ptime m_read_time; + // the asio async operation + deadline_timer m_timeout; + + int m_completion_timeout; + int m_read_timeout; + + typedef mutex mutex_t; + mutable mutex_t m_mutex; + bool m_abort; + }; + + struct TORRENT_EXTRA_EXPORT tracker_connection + : timeout_handler + { + tracker_connection(tracker_manager& man + , tracker_request const& req + , io_service& ios + , boost::weak_ptr r); + + boost::shared_ptr requester() const; + virtual ~tracker_connection() {} + + tracker_request const& tracker_req() const { return m_req; } + + void fail(error_code const& ec, int code = -1, char const* msg = "" + , int interval = 0, int min_interval = 0); + virtual void start() = 0; + virtual void close(); + address const& bind_interface() const { return m_req.bind_ip; } + void sent_bytes(int bytes); + void received_bytes(int bytes); + virtual bool on_receive(error_code const& ec, udp::endpoint const& ep + , char const* buf, int size) { return false; } + virtual bool on_receive_hostname(error_code const& ec, char const* hostname + , char const* buf, int size) { return false; } + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + protected: + + void fail_impl(error_code const& ec, int code = -1, std::string msg = std::string() + , int interval = 0, int min_interval = 0); + + boost::weak_ptr m_requester; + + tracker_manager& m_man; + + private: + + const tracker_request m_req; + }; + + class TORRENT_EXTRA_EXPORT tracker_manager: public udp_socket_observer, boost::noncopyable + { + public: + + tracker_manager(aux::session_impl& ses, proxy_settings const& ps) + : m_ses(ses) + , m_proxy(ps) + , m_abort(false) {} + ~tracker_manager(); + + void queue_request( + io_service& ios + , connection_queue& cc + , tracker_request r + , std::string const& auth + , boost::weak_ptr c + = boost::weak_ptr()); + void abort_all_requests(bool all = false); + + void remove_request(tracker_connection const*); + bool empty() const; + int num_requests() const; + + void sent_bytes(int bytes); + void received_bytes(int bytes); + + virtual bool incoming_packet(error_code const& e, udp::endpoint const& ep + , char const* buf, int size); + + // this is only used for SOCKS packets, since + // they may be addressed to hostname + virtual bool incoming_packet(error_code const& e, char const* hostname + , char const* buf, int size); + + private: + + typedef mutex mutex_t; + mutable mutex_t m_mutex; + + typedef std::list > + tracker_connections_t; + tracker_connections_t m_connections; + aux::session_impl& m_ses; + proxy_settings const& m_proxy; + bool m_abort; + }; +} + +#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp new file mode 100644 index 0000000000..2a448227ad --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_socket.hpp @@ -0,0 +1,311 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UDP_SOCKET_HPP_INCLUDED +#define TORRENT_UDP_SOCKET_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include + +namespace libtorrent +{ + class connection_queue; + + struct udp_socket_observer + { + // return true if the packet was handled (it won't be + // propagated to the next observer) + virtual bool incoming_packet(error_code const& ec + , udp::endpoint const&, char const* buf, int size) = 0; + virtual bool incoming_packet(error_code const& ec + , char const* hostname, char const* buf, int size) { return false; } + + // called when the socket becomes writeable, after having + // failed with EWOULDBLOCK + virtual void writable() {} + + // called every time the socket is drained of packets + virtual void socket_drained() {} + }; + + class udp_socket + { + public: + udp_socket(io_service& ios, connection_queue& cc); + ~udp_socket(); + + enum flags_t { dont_drop = 1, peer_connection = 2, dont_queue = 4 }; + + bool is_open() const + { + return m_ipv4_sock.is_open() +#if TORRENT_USE_IPV6 + || m_ipv6_sock.is_open() +#endif + ; + } + io_service& get_io_service() { return m_ipv4_sock.get_io_service(); } + + void subscribe(udp_socket_observer* o); + void unsubscribe(udp_socket_observer* o); + + // this is only valid when using a socks5 proxy + void send_hostname(char const* hostname, int port, char const* p + , int len, error_code& ec, int flags = 0); + + void send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + void bind(udp::endpoint const& ep, error_code& ec); + void close(); + int local_port() const { return m_bind_port; } + + void set_proxy_settings(proxy_settings const& ps); + proxy_settings const& get_proxy_settings() { return m_proxy_settings; } + void set_force_proxy(bool f) { m_force_proxy = f; } + + bool is_closed() const { return m_abort; } + tcp::endpoint local_endpoint(error_code& ec) const + { + udp::endpoint ep = m_ipv4_sock.local_endpoint(ec); + return tcp::endpoint(ep.address(), ep.port()); + } + + void set_buf_size(int s); + + template + void get_option(SocketOption const& opt, error_code& ec) + { + m_ipv4_sock.get_option(opt, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.get_option(opt, ec); +#endif + } + + template + void set_option(SocketOption const& opt, error_code& ec) + { + m_ipv4_sock.set_option(opt, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.set_option(opt, ec); +#endif + } + + template + void get_option(SocketOption& opt, error_code& ec) + { + m_ipv4_sock.get_option(opt, ec); + } + + udp::endpoint proxy_addr() const { return m_proxy_addr; } + + protected: + + struct queued_packet + { + udp::endpoint ep; + char* hostname; + buffer buf; + int flags; + }; + + // number of outstanding UDP socket operations + // using the UDP socket buffer + int num_outstanding() const + { + return m_v4_outstanding +#if TORRENT_USE_IPV6 + + m_v6_outstanding +#endif + ; + } + + private: + + // non-copyable + udp_socket(udp_socket const&); + udp_socket& operator=(udp_socket const&); + + // observers on this udp socket + std::vector m_observers; + std::vector m_added_observers; + + // this is true while iterating over the observers + // vector, invoking observer hooks. We may not + // add new observers during this time, since it + // may invalidate the iterator. If this is true, + // instead add new observers to m_added_observers + // and they will be added later + bool m_observers_locked; + + void call_handler(error_code const& ec, udp::endpoint const& ep + , char const* buf, int size); + void call_handler(error_code const& ec, const char* host + , char const* buf, int size); + void call_drained_handler(); + void call_writable_handler(); + + void on_writable(error_code const& ec, udp::socket* s); + + void setup_read(udp::socket* s); + void on_read(error_code const& ec, udp::socket* s); + void on_read_impl(udp::socket* sock, udp::endpoint const& ep + , error_code const& e, std::size_t bytes_transferred); + void on_name_lookup(error_code const& e, tcp::resolver::iterator i); + void on_timeout(); + void on_connect(int ticket); + void on_connected(error_code const& ec, int ticket); + void handshake1(error_code const& e); + void handshake2(error_code const& e); + void handshake3(error_code const& e); + void handshake4(error_code const& e); + void socks_forward_udp(); + void connect1(error_code const& e); + void connect2(error_code const& e); + void hung_up(error_code const& e); + + void drain_queue(); + + void wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec); + void wrap(char const* hostname, int port, char const* p, int len, error_code& ec); + void unwrap(error_code const& e, char const* buf, int size); + +#if TORRENT_USE_ASSERTS + +#if defined BOOST_HAS_PTHREADS + mutable pthread_t m_thread; +#endif + bool is_single_thread() const + { +#if defined BOOST_HAS_PTHREADS + if (m_thread == 0) + m_thread = pthread_self(); + return m_thread == pthread_self(); +#endif + return true; + } +#endif + + udp::socket m_ipv4_sock; + int m_buf_size; + + // if the buffer size is attempted + // to be changed while the buffer is + // being used, this member is set to + // the desired size, and it's resized + // later + int m_new_buf_size; + char* m_buf; + +#if TORRENT_USE_IPV6 + udp::socket m_ipv6_sock; +#endif + + boost::uint16_t m_bind_port; + boost::uint8_t m_v4_outstanding; +#if TORRENT_USE_IPV6 + boost::uint8_t m_v6_outstanding; +#endif + + tcp::socket m_socks5_sock; + int m_connection_ticket; + proxy_settings m_proxy_settings; + connection_queue& m_cc; + tcp::resolver m_resolver; + char m_tmp_buf[270]; + bool m_queue_packets; + bool m_tunnel_packets; + bool m_force_proxy; + bool m_abort; + + // this is the endpoint the proxy server lives at. + // when performing a UDP associate, we get another + // endpoint (presumably on the same IP) where we're + // supposed to send UDP packets. + udp::endpoint m_proxy_addr; + + // this is where UDP packets that are to be forwarded + // are sent. The result from UDP ASSOCIATE is stored + // in here. + udp::endpoint m_udp_proxy_addr; + + // while we're connecting to the proxy + // we have to queue the packets, we'll flush + // them once we're connected + std::deque m_queue; + + // counts the number of outstanding async + // operations hanging on this socket + int m_outstanding_ops; + +#if TORRENT_USE_IPV6 + bool m_v6_write_subscribed:1; +#endif + bool m_v4_write_subscribed:1; + +#if TORRENT_USE_ASSERTS + bool m_started; + int m_magic; + int m_outstanding_when_aborted; + int m_outstanding_connect; + int m_outstanding_timeout; + int m_outstanding_resolve; + int m_outstanding_connect_queue; + int m_outstanding_socks; + + char timeout_stack[2000]; +#endif + }; + + struct rate_limited_udp_socket : public udp_socket + { + rate_limited_udp_socket(io_service& ios, connection_queue& cc); + void set_rate_limit(int limit) { m_rate_limit = limit; } + bool send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + + private: + + int m_rate_limit; + int m_quota; + ptime m_last_tick; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp new file mode 100644 index 0000000000..adc2b89412 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/udp_tracker_connection.hpp @@ -0,0 +1,144 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux { struct session_impl; } + + class TORRENT_EXTRA_EXPORT udp_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + udp_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl& ses + , proxy_settings const& ps); + + void start(); + void close(); + + private: + + enum action_t + { + action_connect, + action_announce, + action_scrape, + action_error + }; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void name_lookup(error_code const& error, tcp::resolver::iterator i); + void timeout(error_code const& error); + void start_announce(); + + bool on_receive(error_code const& e, udp::endpoint const& ep + , char const* buf, int size); + bool on_receive_hostname(error_code const& e, char const* hostname + , char const* buf, int size); + bool on_connect_response(char const* buf, int size); + bool on_announce_response(char const* buf, int size); + bool on_scrape_response(char const* buf, int size); + + // wraps tracker_connection::fail + void fail(error_code const& ec, int code = -1 + , char const* msg = "", int interval = 0, int min_interval = 0); + + void send_udp_connect(); + void send_udp_announce(); + void send_udp_scrape(); + + virtual void on_timeout(error_code const& ec); + + udp::endpoint pick_target_endpoint() const; + + bool m_abort; + std::string m_hostname; + udp::endpoint m_target; + std::list m_endpoints; + + int m_transaction_id; + aux::session_impl& m_ses; + int m_attempts; + + struct connection_cache_entry + { + boost::int64_t connection_id; + ptime expires; + }; + + static std::map m_connection_cache; + static mutex m_cache_mutex; + + action_t m_state; + + proxy_settings m_proxy; + }; + +} + +#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp new file mode 100644 index 0000000000..f611fd531a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/union_endpoint.hpp @@ -0,0 +1,134 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UNION_ENDPOINT_HPP_INCLUDED +#define TORRENT_UNION_ENDPOINT_HPP_INCLUDED + +#include "libtorrent/socket.hpp" +#include "libtorrent/address.hpp" + +namespace libtorrent +{ + + struct union_endpoint + { + union_endpoint(tcp::endpoint const& ep) + { + *this = ep; + } + + union_endpoint(udp::endpoint const& ep) + { + *this = ep; + } + + union_endpoint() + { + *this = tcp::endpoint(); + } + + union_endpoint& operator=(udp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + v4 = ep.address().is_v4(); + if (v4) + addr.v4 = ep.address().to_v4().to_bytes(); + else + addr.v6 = ep.address().to_v6().to_bytes(); +#else + addr.v4 = ep.address().to_v4().to_bytes(); +#endif + port = ep.port(); + return *this; + } + + operator udp::endpoint() const + { +#if TORRENT_USE_IPV6 + if (v4) return udp::endpoint(address_v4(addr.v4), port); + else return udp::endpoint(address_v6(addr.v6), port); +#else + return udp::endpoint(address_v4(addr.v4), port); +#endif + } + + union_endpoint& operator=(tcp::endpoint const& ep) + { +#if TORRENT_USE_IPV6 + v4 = ep.address().is_v4(); + if (v4) + addr.v4 = ep.address().to_v4().to_bytes(); + else + addr.v6 = ep.address().to_v6().to_bytes(); +#else + addr.v4 = ep.address().to_v4().to_bytes(); +#endif + port = ep.port(); + return *this; + } + + libtorrent::address address() const + { +#if TORRENT_USE_IPV6 + if (v4) return address_v4(addr.v4); + else return address_v6(addr.v6); +#else + return address_v4(addr.v4); +#endif + } + + operator tcp::endpoint() const + { +#if TORRENT_USE_IPV6 + if (v4) return tcp::endpoint(address_v4(addr.v4), port); + else return tcp::endpoint(address_v6(addr.v6), port); +#else + return tcp::endpoint(address_v4(addr.v4), port); +#endif + } + + TORRENT_UNION addr_t + { + address_v4::bytes_type v4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type v6; +#endif + } addr; + boost::uint16_t port; +#if TORRENT_USE_IPV6 + bool v4:1; +#endif + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp new file mode 100644 index 0000000000..f28cc112ff --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/upnp.hpp @@ -0,0 +1,393 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UPNP_HPP +#define TORRENT_UPNP_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/intrusive_ptr_base.hpp" +#include "libtorrent/thread.hpp" +#include "libtorrent/deadline_timer.hpp" + +#include +#include +#include +#include +#include + + +#if defined(TORRENT_UPNP_LOGGING) +#include +#endif + +namespace libtorrent +{ + + namespace upnp_errors + { + // error codes for the upnp_error_category. They hold error codes + // returned by UPnP routers when mapping ports + enum error_code_enum + { + // No error + no_error = 0, + // One of the arguments in the request is invalid + invalid_argument = 402, + // The request failed + action_failed = 501, + // The specified value does not exist in the array + value_not_in_array = 714, + // The source IP address cannot be wild-carded, but + // must be fully specified + source_ip_cannot_be_wildcarded = 715, + // The external port cannot be wildcarded, but must + // be specified + external_port_cannot_be_wildcarded = 716, + // The port mapping entry specified conflicts with a + // mapping assigned previously to another client + port_mapping_conflict = 718, + // Internal and external port value must be the same + internal_port_must_match_external = 724, + // The NAT implementation only supports permanent + // lease times on port mappings + only_permanent_leases_supported = 725, + // RemoteHost must be a wildcard and cannot be a + // specific IP addres or DNS name + remote_host_must_be_wildcard = 726, + // ExternalPort must be a wildcard and cannot be a + // specific port + external_port_must_be_wildcard = 727 + }; + + // hidden + TORRENT_EXPORT boost::system::error_code make_error_code(error_code_enum e); + } + + // the boost.system error category for UPnP errors + TORRENT_EXPORT boost::system::error_category& get_upnp_category(); + +// int: port-mapping index +// address: external address as queried from router +// int: external port +// std::string: error message +// an empty string as error means success +// a port-mapping index of -1 means it's +// an informational log message +typedef boost::function portmap_callback_t; +typedef boost::function log_callback_t; + +// TODO: support using the windows API for UPnP operations as well +class TORRENT_EXTRA_EXPORT upnp : public intrusive_ptr_base +{ +public: + upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb, log_callback_t const& lcb + , bool ignore_nonrouters, void* state = 0); + ~upnp(); + + void* drain_state(); + + enum protocol_type { none = 0, udp = 1, tcp = 2 }; + + // Attempts to add a port mapping for the specified protocol. Valid protocols are + // ``upnp::tcp`` and ``upnp::udp`` for the UPnP class and ``natpmp::tcp`` and + // ``natpmp::udp`` for the NAT-PMP class. + // + // ``external_port`` is the port on the external address that will be mapped. This + // is a hint, you are not guaranteed that this port will be available, and it may + // end up being something else. In the portmap_alert_ notification, the actual + // external port is reported. + // + // ``local_port`` is the port in the local machine that the mapping should forward + // to. + // + // The return value is an index that identifies this port mapping. This is used + // to refer to mappings that fails or succeeds in the portmap_error_alert_ and + // portmap_alert_ respectively. If The mapping fails immediately, the return value + // is -1, which means failure. There will not be any error alert notification for + // mappings that fail with a -1 return value. + int add_mapping(protocol_type p, int external_port, int local_port); + + // This function removes a port mapping. ``mapping_index`` is the index that refers + // to the mapping you want to remove, which was returned from add_mapping(). + void delete_mapping(int mapping_index); + + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; + + void discover_device(); + void close(); + + // This is only available for UPnP routers. If the model is advertized by + // the router, it can be queried through this function. + std::string router_model() + { + mutex::scoped_lock l(m_mutex); + return m_model; + } + +private: + + void map_timer(error_code const& ec); + void try_map_upnp(mutex::scoped_lock& l, bool timer = false); + void discover_device_impl(mutex::scoped_lock& l); + static address_v4 upnp_multicast_address; + static udp::endpoint upnp_multicast_endpoint; + + // there are routers that's don't support timed + // port maps, without returning error 725. It seems + // safer to always assume that we have to ask for + // permanent leases + enum { default_lease_time = 0 }; + + void resend_request(error_code const& e); + void on_reply(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred); + + struct rootdevice; + void next(rootdevice& d, int i, mutex::scoped_lock& l); + void update_map(rootdevice& d, int i, mutex::scoped_lock& l); + + + void on_upnp_xml(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c); + void on_upnp_get_ip_address_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c); + void on_upnp_map_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping, http_connection& c); + void on_upnp_unmap_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping, http_connection& c); + void on_expire(error_code const& e); + + void disable(error_code const& ec, mutex::scoped_lock& l); + void return_error(int mapping, int code, mutex::scoped_lock& l); + void log(char const* msg, mutex::scoped_lock& l); + + void get_ip_address(rootdevice& d); + void delete_port_mapping(rootdevice& d, int i); + void create_port_mapping(http_connection& c, rootdevice& d, int i); + void post(upnp::rootdevice const& d, char const* soap + , char const* soap_action, mutex::scoped_lock& l); + + int num_mappings() const { return int(m_mappings.size()); } + + struct global_mapping_t + { + global_mapping_t() + : protocol(none) + , external_port(0) + , local_port(0) + {} + int protocol; + int external_port; + int local_port; + }; + + struct mapping_t + { + enum action_t { action_none, action_add, action_delete }; + mapping_t() + : action(action_none) + , local_port(0) + , external_port(0) + , protocol(none) + , failcount(0) + {} + + // the time the port mapping will expire + ptime expires; + + int action; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + // 2 = udp, 1 = tcp + int protocol; + + // the number of times this mapping has failed + int failcount; + }; + + struct rootdevice + { + rootdevice(): service_namespace(0) + , port(0) + , lease_duration(default_lease_time) + , supports_specific_external(true) + , disabled(false) + , non_router(false) + { +#if TORRENT_USE_ASSERTS + magic = 1337; +#endif + } + +#if TORRENT_USE_ASSERTS + ~rootdevice() + { + TORRENT_ASSERT(magic == 1337); + magic = 0; + } +#endif + + // the interface url, through which the list of + // supported interfaces are fetched + std::string url; + + // the url to the WANIP or WANPPP interface + std::string control_url; + // either the WANIP namespace or the WANPPP namespace + char const* service_namespace; + + std::vector mapping; + + // this is the hostname, port and path + // component of the url or the control_url + // if it has been found + std::string hostname; + int port; + std::string path; + address external_ip; + + int lease_duration; + // true if the device supports specifying a + // specific external port, false if it doesn't + bool supports_specific_external; + + bool disabled; + + // this is true if the IP of this device is not + // one of our default routes. i.e. it may be someone + // else's router, we just happen to have multicast + // enabled across networks + // this is only relevant if ignore_non_routers is set. + bool non_router; + + mutable boost::shared_ptr upnp_connection; + +#if TORRENT_USE_ASSERTS + int magic; +#endif + void close() const + { + TORRENT_ASSERT(magic == 1337); + if (!upnp_connection) return; + upnp_connection->close(); + upnp_connection.reset(); + } + + bool operator<(rootdevice const& rhs) const + { return url < rhs.url; } + }; + + struct upnp_state_t + { + std::vector mappings; + std::set devices; + }; + + std::vector m_mappings; + + std::string const& m_user_agent; + + // the set of devices we've found + std::set m_devices; + + portmap_callback_t m_callback; + log_callback_t m_log_callback; + + // current retry count + int m_retry_count; + + io_service& m_io_service; + + // the udp socket used to send and receive + // multicast messages on the network + broadcast_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + // this timer fires one second after the last UPnP response. This is the + // point where we assume we have received most or all SSDP reponses. If we + // are ignoring non-routers and at this point we still haven't received a + // response from a router UPnP device, we override the ignoring behavior and + // map them anyway. + deadline_timer m_map_timer; + + bool m_disabled; + bool m_closing; + bool m_ignore_non_routers; + + connection_queue& m_cc; + + mutex m_mutex; + + std::string m_model; +}; + +} + +#if BOOST_VERSION >= 103500 + +namespace boost { namespace system { + + template<> struct is_error_code_enum + { static const bool value = true; }; + + template<> struct is_error_condition_enum + { static const bool value = true; }; +} } + +#endif // BOOST_VERSION + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp new file mode 100644 index 0000000000..a89f677292 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utf8.hpp @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTF8_HPP_INCLUDED +#define TORRENT_UTF8_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +// on windows we need these functions for +// convert_to_native and convert_from_native +#if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS + +#include +#include + +namespace libtorrent +{ + + // results from UTF-8 conversion functions utf8_wchar and + // wchar_utf8 + enum utf8_conv_result_t + { + // conversion successful + conversion_ok, + + // partial character in source, but hit end + source_exhausted, + + // insuff. room in target for conversion + target_exhausted, + + // source sequence is illegal/malformed + source_illegal + }; + + // ``utf8_wchar`` converts a UTF-8 string (``utf8``) to a wide character + // string (``wide``). ``wchar_utf8`` converts a wide character string + // (``wide``) to a UTF-8 string (``utf8``). The return value is one of + // the enumeration values from utf8_conv_result_t. + TORRENT_EXPORT utf8_conv_result_t utf8_wchar( + const std::string &utf8, std::wstring &wide); + TORRENT_EXPORT utf8_conv_result_t wchar_utf8( + const std::wstring &wide, std::string &utf8); +} +#endif // !BOOST_NO_STD_WSTRING + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp new file mode 100644 index 0000000000..0c0f06a763 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_socket_manager.hpp @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED +#define TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED + +#include + +#include "libtorrent/socket_type.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/enum_net.hpp" + +namespace libtorrent +{ + class udp_socket; + class utp_stream; + struct utp_socket_impl; + + typedef boost::function const&)> incoming_utp_callback_t; + + struct utp_socket_manager : udp_socket_observer + { + utp_socket_manager(session_settings const& sett, udp_socket& s, incoming_utp_callback_t cb); + ~utp_socket_manager(); + + void get_status(utp_status& s) const; + + // return false if this is not a uTP packet + virtual bool incoming_packet(error_code const& ec, udp::endpoint const& ep + , char const* p, int size); + virtual bool incoming_packet(error_code const& ec, char const* host, char const* p, int size) + { return false; } + virtual void writable(); + + virtual void socket_drained(); + + void tick(ptime now); + + tcp::endpoint local_endpoint(address const& remote, error_code& ec) const; + int local_port(error_code& ec) const; + + // flags for send_packet + enum { dont_fragment = 1 }; + void send_packet(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags = 0); + void subscribe_writable(utp_socket_impl* s); + + // internal, used by utp_stream + void remove_socket(boost::uint16_t id); + + utp_socket_impl* new_utp_socket(utp_stream* str); + int gain_factor() const { return m_sett.utp_gain_factor; } + int target_delay() const { return m_sett.utp_target_delay * 1000; } + int syn_resends() const { return m_sett.utp_syn_resends; } + int fin_resends() const { return m_sett.utp_fin_resends; } + int num_resends() const { return m_sett.utp_num_resends; } + int connect_timeout() const { return m_sett.utp_connect_timeout; } + int min_timeout() const { return m_sett.utp_min_timeout; } + int loss_multiplier() const { return m_sett.utp_loss_multiplier; } + bool allow_dynamic_sock_buf() const { return m_sett.utp_dynamic_sock_buf; } + + void mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu); + void set_sock_buf(int size); + int num_sockets() const { return m_utp_sockets.size(); } + + void defer_ack(utp_socket_impl* s); + void subscribe_drained(utp_socket_impl* s); + + enum counter_t + { + packet_loss = 0, + timeout, + packets_in, + packets_out, + fast_retransmit, + packet_resend, + samples_above_target, + samples_below_target, + payload_pkts_in, + payload_pkts_out, + invalid_pkts_in, + redundant_pkts_in, + + num_counters + }; + + // used to keep stats of uTP events + void inc_stats_counter(int counter); + + private: + udp_socket& m_sock; + incoming_utp_callback_t m_cb; + + // replace with a hash-map + typedef std::multimap socket_map_t; + socket_map_t m_utp_sockets; + + // this is a list of sockets that needs to send an ack. + // once the UDP socket is drained, all of these will + // have a chance to do that. This is to avoid sending + // an ack for every single packet + std::vector m_deferred_acks; + + // sockets that have received or sent packets this + // round, may subscribe to the event of draining the + // UDP socket. At that point they may call the + // user callback function to indicate bytes have been + // sent or received. + std::vector m_drained_event; + + // list of sockets that received EWOULDBLOCK from the + // underlying socket. They are notified when the socket + // becomes writable again + std::vector m_stalled_sockets; + + // the last socket we received a packet on + utp_socket_impl* m_last_socket; + + int m_new_connection; + + session_settings const& m_sett; + + // this is a copy of the routing table, used + // to initialize MTU sizes of uTP sockets + mutable std::vector m_routes; + + // the timestamp for the last time we updated + // the routing table + mutable ptime m_last_route_update; + + // cache of interfaces + mutable std::vector m_interfaces; + mutable ptime m_last_if_update; + + // the buffer size of the socket. This is used + // to now lower the buffer size + int m_sock_buf_size; + + // stats counters + boost::uint64_t m_counters[num_counters]; + }; +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp new file mode 100644 index 0000000000..7d8afe0194 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/utp_stream.hpp @@ -0,0 +1,464 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UTP_STREAM_HPP_INCLUDED +#define TORRENT_UTP_STREAM_HPP_INCLUDED + +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/proxy_base.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/packet_buffer.hpp" +#include "libtorrent/error_code.hpp" + +#include +#include +#include + +#ifndef BOOST_NO_EXCEPTIONS +#include +#endif + +#define CCONTROL_TARGET 100 + +namespace libtorrent +{ + struct utp_socket_manager; + + // internal: some MTU and protocol header sizes constants + enum + { + TORRENT_IPV4_HEADER = 20, + TORRENT_IPV6_HEADER = 40, + TORRENT_UDP_HEADER = 8, + TORRENT_SOCKS5_HEADER = 6, // plus the size of the destination address + + TORRENT_ETHERNET_MTU = 1500, + TORRENT_TEREDO_MTU = 1280, + TORRENT_INET_MIN_MTU = 576, + TORRENT_INET_MAX_MTU = 0xffff + }; + + // internal: the point of the bif_endian_int is two-fold + // one purpuse is to not have any alignment requirements + // so that any byffer received from the network can be cast + // to it and read as an integer of various sizes without + // triggering a bus error. The other purpose is to convert + // from network byte order to host byte order when read and + // written, to offer a convenient interface to both interpreting + // and writing network packets + template struct big_endian_int + { + big_endian_int& operator=(T v) + { + char* p = m_storage; + detail::write_impl(v, p); + return *this; + } + operator T() const + { + const char* p = m_storage; + return detail::read_impl(p, detail::type()); + } + private: + char m_storage[sizeof(T)]; + }; + + typedef big_endian_int be_uint64; + typedef big_endian_int be_uint32; + typedef big_endian_int be_uint16; + typedef big_endian_int be_int64; + typedef big_endian_int be_int32; + typedef big_endian_int be_int16; + +/* + uTP header from BEP 29 + + 0 4 8 16 24 32 + +-------+-------+---------------+---------------+---------------+ + | type | ver | extension | connection_id | + +-------+-------+---------------+---------------+---------------+ + | timestamp_microseconds | + +---------------+---------------+---------------+---------------+ + | timestamp_difference_microseconds | + +---------------+---------------+---------------+---------------+ + | wnd_size | + +---------------+---------------+---------------+---------------+ + | seq_nr | ack_nr | + +---------------+---------------+---------------+---------------+ + +*/ + +// internal: the different kinds of uTP packets +enum utp_socket_state_t +{ ST_DATA, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES }; + +struct utp_header +{ + unsigned char type_ver; + unsigned char extension; + be_uint16 connection_id; + be_uint32 timestamp_microseconds; + be_uint32 timestamp_difference_microseconds; + be_uint32 wnd_size; + be_uint16 seq_nr; + be_uint16 ack_nr; + + int get_type() const { return type_ver >> 4; } + int get_version() const { return type_ver & 0xf; } +}; + +struct utp_socket_impl; + +utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id + , boost::uint16_t send_id, void* userdata + , utp_socket_manager* sm); +void detach_utp_impl(utp_socket_impl* s); +void delete_utp_impl(utp_socket_impl* s); +bool should_delete(utp_socket_impl* s); +void tick_utp_impl(utp_socket_impl* s, ptime const& now); +void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu); +bool utp_incoming_packet(utp_socket_impl* s, char const* p + , int size, udp::endpoint const& ep, ptime receive_time); +bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id); +udp::endpoint utp_remote_endpoint(utp_socket_impl* s); +boost::uint16_t utp_receive_id(utp_socket_impl* s); +int utp_socket_state(utp_socket_impl const* s); +void utp_send_ack(utp_socket_impl* s); +void utp_socket_drained(utp_socket_impl* s); +void utp_writable(utp_socket_impl* s); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +int socket_impl_size(); +#endif + +// this is the user-level stream interface to utp sockets. +// the reason why it's split up in a utp_stream class and +// an implementation class is because the socket state has +// to be able to out-live the user level socket. For instance +// when sending data on a stream and then closing it, the +// state holding the send buffer has to be kept around until +// it has been flushed, which may be longer than the client +// will keep the utp_stream object around for. +// for more details, see utp_socket_impl, which is analogous +// to the kernel state for a socket. It's defined in utp_stream.cpp +class TORRENT_EXTRA_EXPORT utp_stream +{ +public: + + typedef utp_stream lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit utp_stream(asio::io_service& io_service); + ~utp_stream(); + + lowest_layer_type& lowest_layer() { return *this; } + + // used for incoming connections + void set_impl(utp_socket_impl* s); + utp_socket_impl* get_impl(); + +#ifndef BOOST_NO_EXCEPTIONS + template + void io_control(IO_Control_Command& ioc) {} +#endif + + template + void io_control(IO_Control_Command& ioc, error_code& ec) {} + +#ifndef BOOST_NO_EXCEPTIONS + void bind(endpoint_type const& /*endpoint*/) {} +#endif + + void bind(endpoint_type const& endpoint, error_code& ec); + +#ifndef BOOST_NO_EXCEPTIONS + template + void set_option(SettableSocketOption const& opt) {} +#endif + + template + error_code set_option(SettableSocketOption const& opt, error_code& ec) { return ec; } + +#ifndef BOOST_NO_EXCEPTIONS + template + void get_option(GettableSocketOption& opt) {} +#endif + + template + error_code get_option(GettableSocketOption& opt, error_code& ec) { return ec; } + + + void close(); + void close(error_code const& /*ec*/) { close(); } + bool is_open() const { return m_open; } + + int read_buffer_size() const; + static void on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill); + static void on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill); + static void on_connect(void* self, error_code const& ec, bool kill); + + typedef void(*handler_t)(void*, size_t, error_code const&, bool); + typedef void(*connect_handler_t)(void*, error_code const&, bool); + + void add_read_buffer(void* buf, size_t len); + void set_read_handler(handler_t h); + void add_write_buffer(void const* buf, size_t len); + void set_write_handler(handler_t h); + size_t read_some(bool clear_buffers); + + int send_delay() const; + int recv_delay() const; + + void do_connect(tcp::endpoint const& ep, connect_handler_t h); + + endpoint_type local_endpoint() const + { + error_code ec; + return local_endpoint(ec); + } + + endpoint_type local_endpoint(error_code& ec) const; + + endpoint_type remote_endpoint() const + { + error_code ec; + return remote_endpoint(ec); + } + + endpoint_type remote_endpoint(error_code& ec) const; + + std::size_t available() const; + std::size_t available(error_code& /*ec*/) const { return available(); } + + asio::io_service& get_io_service() { return m_io_service; } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + if (!endpoint.address().is_v4()) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + m_connect_handler = handler; + do_connect(endpoint, &utp_stream::on_connect); + } + + template + void async_read_some(boost::asio::null_buffers const& buffers, Handler const& handler) + { + TORRENT_ASSERT(false); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_read_handler); + if (m_read_handler) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + int bytes_added = 0; + for (typename Mutable_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + if (buffer_size(*i) == 0) continue; + using asio::buffer_cast; + using asio::buffer_size; + add_read_buffer(buffer_cast(*i), buffer_size(*i)); + bytes_added += buffer_size(*i); + } + if (bytes_added == 0) + { + // if we're reading 0 bytes, post handler immediately + // asio's SSL layer depends on this behavior + m_io_service.post(boost::bind(handler, error_code(), 0)); + return; + } + + m_read_handler = handler; + set_read_handler(&utp_stream::on_read); + } + + void do_async_connect(endpoint_type const& ep + , boost::function const& handler); + + template + void open(Protocol const& p, error_code& ec) + { m_open = true; } + + template + void open(Protocol const& p) + { m_open = true; } + + template + std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec) + { + TORRENT_ASSERT(!m_read_handler); + if (m_impl == 0) + { + ec = asio::error::not_connected; + return 0; + } + + if (read_buffer_size() == 0) + { + ec = asio::error::would_block; + return 0; + } +#if TORRENT_USE_ASSERTS + size_t buf_size = 0; +#endif + + for (typename Mutable_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + using asio::buffer_cast; + using asio::buffer_size; + add_read_buffer(buffer_cast(*i), buffer_size(*i)); +#if TORRENT_USE_ASSERTS + buf_size += buffer_size(*i); +#endif + } + std::size_t ret = read_some(true); + TORRENT_ASSERT(ret <= buf_size); + TORRENT_ASSERT(ret > 0); + return ret; + } + + template + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + TORRENT_ASSERT(false && "not implemented!"); + // TODO: implement blocking write. Low priority since it's not used (yet) + return 0; + } + +#ifndef BOOST_NO_EXCEPTIONS + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + error_code ec; + std::size_t ret = read_some(buffers, ec); + if (ec) + boost::throw_exception(boost::system::system_error(ec)); + return ret; + } + + template + std::size_t write_some(Const_Buffers const& buffers) + { + error_code ec; + std::size_t ret = write_some(buffers, ec); + if (ec) + boost::throw_exception(boost::system::system_error(ec)); + return ret; + } +#endif + + template + void async_write_some(boost::asio::null_buffers const& buffers, Handler const& handler) + { + TORRENT_ASSERT(false); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_write_handler); + if (m_write_handler) + { + m_io_service.post(boost::bind(handler, asio::error::operation_not_supported, 0)); + return; + } + + int bytes_added = 0; + for (typename Const_Buffers::const_iterator i = buffers.begin() + , end(buffers.end()); i != end; ++i) + { + if (buffer_size(*i) == 0) continue; + using asio::buffer_cast; + using asio::buffer_size; + add_write_buffer((void*)buffer_cast(*i), buffer_size(*i)); + bytes_added += buffer_size(*i); + } + if (bytes_added == 0) + { + // if we're reading 0 bytes, post handler immediately + // asio's SSL layer depends on this behavior + m_io_service.post(boost::bind(handler, error_code(), 0)); + return; + } + m_write_handler = handler; + set_write_handler(&utp_stream::on_write); + } + +//private: + + void cancel_handlers(error_code const&); + + boost::function1 m_connect_handler; + boost::function2 m_read_handler; + boost::function2 m_write_handler; + + asio::io_service& m_io_service; + utp_socket_impl* m_impl; + + // this field requires another 8 bytes (including padding) + bool m_open; +}; + +} + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp new file mode 100644 index 0000000000..b83085e1a0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/version.hpp @@ -0,0 +1,47 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_VERSION_HPP_INCLUDED +#define TORRENT_VERSION_HPP_INCLUDED + +#define LIBTORRENT_VERSION_MAJOR 1 +#define LIBTORRENT_VERSION_MINOR 0 +#define LIBTORRENT_VERSION_TINY 5 + +// the format of this version is: MMmmtt +// M = Major version, m = minor version, t = tiny version +#define LIBTORRENT_VERSION_NUM ((LIBTORRENT_VERSION_MAJOR * 10000) + (LIBTORRENT_VERSION_MINOR * 100) + LIBTORRENT_VERSION_TINY) + +#define LIBTORRENT_VERSION "1.0.5.0" +#define LIBTORRENT_REVISION "$Rev: 11072 $" + +#endif diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp new file mode 100644 index 0000000000..1299b1ebd5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/web_connection_base.hpp @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef WEB_CONNECTION_BASE_HPP_INCLUDED +#define WEB_CONNECTION_BASE_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +// parse_url +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT web_connection_base + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + web_connection_base( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + void start(); + + ~web_connection_base(); + + // called from the main loop when this connection has any + // work to do. + void on_sent(error_code const& error + , std::size_t bytes_transferred); + + virtual std::string const& url() const = 0; + + bool in_handshake() const; + + // the following functions appends messages + // to the send buffer + void write_choke() {} + void write_unchoke() {} + void write_interested() {} + void write_not_interested() {} + virtual void write_request(peer_request const& r) = 0; + void write_cancel(peer_request const& r) {} + void write_have(int index) {} + void write_piece(peer_request const& r, disk_buffer_holder& buffer) { TORRENT_ASSERT(false); } + void write_keepalive() {} + void on_connected(); + void write_reject_request(peer_request const&) {} + void write_allow_fast(int) {} + void write_suggest(int piece) {} + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + virtual void get_specific_peer_info(peer_info& p) const; + + protected: + + virtual void add_headers(std::string& request + , proxy_settings const& ps, bool using_proxy) const; + + // this has one entry per bittorrent request + std::deque m_requests; + + std::string m_server_string; + http_parser m_parser; + std::string m_basic_auth; + std::string m_host; + int m_port; + std::string m_path; + + std::string m_external_auth; + web_seed_entry::headers_t m_extra_headers; + + // the first request will contain a little bit more data + // than subsequent ones, things that aren't critical are left + // out to save bandwidth. + bool m_first_request; + + // true if we're using ssl + bool m_ssl; + + // the number of bytes into the receive buffer where + // current read cursor is. + int m_body_start; + }; +} + +#endif // TORRENT_WEB_CONNECTION_BASE_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp new file mode 100644 index 0000000000..3e45046d77 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXTRA_EXPORT web_peer_connection + : public web_connection_base + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + web_peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web); + + virtual void on_connected(); + + virtual int type() const { return peer_connection::url_seed_connection; } + + // called from the main loop when this connection has any + // work to do. + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual void disconnect(error_code const& ec, int error = 0); + + virtual void write_request(peer_request const& r); + + virtual bool received_invalid_data(int index, bool single_peer); + + private: + + bool maybe_harvest_block(); + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + void handle_padfile(buffer::const_interval& recv_buffer); + + // this has one entry per http-request + // (might be more than the bt requests) + std::deque m_file_requests; + + std::string m_url; + + web_seed_entry* m_web; + + // this is used for intermediate storage of pieces + // that are received in more than one HTTP response + // TODO: 1 if we make this be a disk_buffer_holder instead + // we would save a copy sometimes + // use allocate_disk_receive_buffer and release_disk_receive_buffer + std::vector m_piece; + + // the number of bytes received in the current HTTP + // response. used to know where in the buffer the + // next response starts + size_type m_received_body; + + // position in the current range response + size_type m_range_pos; + + // the position in the current block + int m_block_pos; + + // this is the offset inside the current receive + // buffer where the next chunk header will be. + // this is updated for each chunk header that's + // parsed. It does not necessarily point to a valid + // offset in the receive buffer, if we haven't received + // it yet. This offset never includes the HTTP header + size_type m_chunk_pos; + + // this is the number of bytes we've already received + // from the next chunk header we're waiting for + int m_partial_chunk_header; + + // the number of responses we've received so far on + // this connection + int m_num_responses; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp b/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp new file mode 100644 index 0000000000..674f2f21f9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/include/libtorrent/xml_parse.hpp @@ -0,0 +1,71 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_XML_PARSE_HPP +#define TORRENT_XML_PARSE_HPP + +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" + +#include + +namespace libtorrent +{ + enum + { + xml_start_tag, + xml_end_tag, + xml_empty_tag, + xml_declaration_tag, + xml_string, + xml_attribute, + xml_comment, + xml_parse_error, + // used for tags that don't follow the convention of + // key-value pairs inside the tag brackets. Like !DOCTYPE + xml_tag_content + }; + + // callback(int type, char const* name, char const* val) + // str2 is only used for attributes. name is element or attribute + // name and val is attribute value + TORRENT_EXTRA_EXPORT void xml_parse(char* p, char* end + , boost::function callback); +} + + +#endif + diff --git a/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in b/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in new file mode 100644 index 0000000000..ee5f519430 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/libtorrent-rasterbar-cmake.pc.in @@ -0,0 +1,6 @@ +Name: libtorrent-rasterbar +Description: Bittorrent library. +Version: @VERSION@ +Libs: -L${CMAKE_INSTALL_PREFIX}/lib -ltorrent-rasterbar +Cflags: -I${CMAKE_INSTALL_PREFIX}/include -I${CMAKE_INSTALL_PREFIX}/include/libtorrent @COMPILETIME_OPTIONS@ @CXX_DEFINES@ + diff --git a/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp b/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp new file mode 100644 index 0000000000..f93a1d56f1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ConvertUTF.cpp @@ -0,0 +1,539 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "libtorrent/ConvertUTF.h" +#ifdef CVTUTF_DEBUG +#include +#endif + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF32* target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG +if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); +} +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source]+1; + if (source+length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF32* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; + case 4: ch += *source++; ch <<= 6; + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up the source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ diff --git a/apps/Launcher/ext/libtorrent/src/GeoIP.c b/apps/Launcher/ext/libtorrent/src/GeoIP.c new file mode 100644 index 0000000000..cb4437ee7e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/GeoIP.c @@ -0,0 +1,1077 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* GeoIP.c + * + * Copyright (C) 2006 MaxMind LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libtorrent/GeoIP.h" + +#include "libtorrent/ConvertUTF.h" + +#ifndef WIN32 +#include +#include +#include /* For ntohl */ +#include + +#include + +#else +#include +#include +#define snprintf _snprintf +#endif +#include +#include +#include +#include +#include +#include /* for fstat */ +#include /* for fstat */ + +#ifdef HAVE_STDINT_H +#include /* For uint32_t */ +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +#define COUNTRY_BEGIN 16776960 +#define STATE_BEGIN_REV0 16700000 +#define STATE_BEGIN_REV1 16000000 +#define STRUCTURE_INFO_MAX_SIZE 20 +#define DATABASE_INFO_MAX_SIZE 100 +#define MAX_ORG_RECORD_LENGTH 300 +#define US_OFFSET 1 +#define CANADA_OFFSET 677 +#define WORLD_OFFSET 1353 +#define FIPS_RANGE 360 + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char GeoIP_country_code[253][3] = { "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN", + "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB", + "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO", + "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD", + "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR", + "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO", + "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ", + "FK","FM","FO","FR","FX","GA","GB","GD","GE","GF", + "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT", + "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID", + "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO", + "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW", + "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT", + "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML", + "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV", + "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI", + "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF", + "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW", + "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD", + "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO", + "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH", + "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", + "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", + "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", + "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE", + "BL","MF"}; + +const char GeoIP_country_code3[253][4] = { "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT", + "AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB", + "BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL", + "BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD", + "CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI", + "CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM", + "DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI", + "FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF", + "GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM", + "GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN", + "IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR", + "JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT", + "CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", + "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI", + "MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV", + "MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC", + "NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF", + "PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW", + "PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN", + "SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM", + "SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA", + "TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN", + "TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN", + "VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SRB","ZAF", + "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY", + "BLM","MAF"}; + +const char * GeoIP_country_name[253] = {"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", + "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", + "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", + "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", + "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", + "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", + "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", + "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", + "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", + "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", + "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", + "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", + "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", + "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", + "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", + "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", + "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", + "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", + "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", + "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", + "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", + "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", + "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", + "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", + "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", + "Saint Barthelemy","Saint Martin"}; + +/* Possible continent codes are AF, AS, EU, NA, OC, SA for Africa, Asia, Europe, North America, Oceania +and South America. */ + +const char GeoIP_country_continent[253][3] = {"--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA", + "AF","AN","SA","OC","EU","OC","SA","AS","EU","SA", + "AS","EU","AF","EU","AS","AF","AF","SA","AS","SA", + "SA","SA","AS","AF","AF","EU","SA","NA","AS","AF", + "AF","AF","EU","AF","OC","SA","AF","AS","SA","SA", + "SA","AF","AS","AS","EU","EU","AF","EU","SA","SA", + "AF","SA","EU","AF","AF","AF","EU","AF","EU","OC", + "SA","OC","EU","EU","EU","AF","EU","SA","AS","SA", + "AF","EU","SA","AF","AF","SA","AF","EU","SA","SA", + "OC","AF","SA","AS","AF","SA","EU","SA","EU","AS", + "EU","AS","AS","AS","AS","AS","EU","EU","SA","AS", + "AS","AF","AS","AS","OC","AF","SA","AS","AS","AS", + "SA","AS","AS","AS","SA","EU","AS","AF","AF","EU", + "EU","EU","AF","AF","EU","EU","AF","OC","EU","AF", + "AS","AS","AS","OC","SA","AF","SA","EU","AF","AS", + "AF","NA","AS","AF","AF","OC","AF","OC","AF","SA", + "EU","EU","AS","OC","OC","OC","AS","SA","SA","OC", + "OC","AS","AS","EU","SA","OC","SA","AS","EU","OC", + "SA","AS","AF","EU","AS","AF","AS","OC","AF","AF", + "EU","AS","AF","EU","EU","EU","AF","EU","AF","AF", + "SA","AF","SA","AS","AF","SA","AF","AF","AF","AS", + "AS","OC","AS","AF","OC","AS","AS","SA","OC","AS", + "AF","EU","AF","OC","NA","SA","AS","EU","SA","SA", + "SA","SA","AS","OC","OC","OC","AS","AF","EU","AF", + "AF","EU","AF","--","--","--","EU","EU","EU","EU", + "SA","SA"}; + +const char * GeoIPDBDescription[NUM_DB_TYPES] = {NULL, "GeoIP Country Edition", "GeoIP City Edition, Rev 1", "GeoIP Region Edition, Rev 1", "GeoIP ISP Edition", "GeoIP Organization Edition", "GeoIP City Edition, Rev 0", "GeoIP Region Edition, Rev 0","GeoIP Proxy Edition","GeoIP ASNum Edition","GeoIP Netspeed Edition","GeoIP Domain Name Edition"}; + +char * custom_directory = NULL; + +void GeoIP_setup_custom_directory (char * dir) { + custom_directory = dir; +} +/* +char *_GeoIP_full_path_to(const char *file_name) { + int len; + char *path = malloc(sizeof(char) * 1024); + + if (custom_directory == NULL){ +#ifndef WIN32 + memset(path, 0, sizeof(char) * 1024); + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", GEOIPDATADIR, file_name); +#else + char buf[MAX_PATH], *p, *q = NULL; + memset(buf, 0, sizeof(buf)); + len = GetModuleFileName(GetModuleHandle(NULL), buf, sizeof(buf) - 1); + for (p = buf + len; p > buf; p--) + if (*p == '\\') + { + if (!q) + q = p; + else + *p = '/'; + } + *q = 0; + memset(path, 0, sizeof(char) * 1024); + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", buf, file_name); +#endif + } else { + len = strlen(custom_directory); + if (custom_directory[len-1] != '/') { + snprintf(path, sizeof(char) * 1024 - 1, "%s/%s",custom_directory, file_name); + } else { + snprintf(path, sizeof(char) * 1024 - 1, "%s%s", custom_directory, file_name); + } + } + return path; +} + +char ** GeoIPDBFileName = NULL; + +void _GeoIP_setup_dbfilename() { + if (NULL == GeoIPDBFileName) { + GeoIPDBFileName = malloc(sizeof(char *) * NUM_DB_TYPES); + memset(GeoIPDBFileName, 0, sizeof(char *) * NUM_DB_TYPES); + + GeoIPDBFileName[GEOIP_COUNTRY_EDITION] = _GeoIP_full_path_to("GeoIP.dat"); + GeoIPDBFileName[GEOIP_REGION_EDITION_REV0] = _GeoIP_full_path_to("GeoIPRegion.dat"); + GeoIPDBFileName[GEOIP_REGION_EDITION_REV1] = _GeoIP_full_path_to("GeoIPRegion.dat"); + GeoIPDBFileName[GEOIP_CITY_EDITION_REV0] = _GeoIP_full_path_to("GeoIPCity.dat"); + GeoIPDBFileName[GEOIP_CITY_EDITION_REV1] = _GeoIP_full_path_to("GeoIPCity.dat"); + GeoIPDBFileName[GEOIP_ISP_EDITION] = _GeoIP_full_path_to("GeoIPISP.dat"); + GeoIPDBFileName[GEOIP_ORG_EDITION] = _GeoIP_full_path_to("GeoIPOrg.dat"); + GeoIPDBFileName[GEOIP_PROXY_EDITION] = _GeoIP_full_path_to("GeoIPProxy.dat"); + GeoIPDBFileName[GEOIP_ASNUM_EDITION] = _GeoIP_full_path_to("GeoIPASNum.dat"); + GeoIPDBFileName[GEOIP_NETSPEED_EDITION] = _GeoIP_full_path_to("GeoIPNetSpeed.dat"); + GeoIPDBFileName[GEOIP_DOMAIN_EDITION] = _GeoIP_full_path_to("GeoIPDomain.dat"); + } +} +*/ + +static +int _file_exists(const char *file_name) { + struct stat file_stat; + return( (stat(file_name, &file_stat) == 0) ? 1:0); +} +/* +int GeoIP_db_avail(int type) { + const char * filePath; + if (type < 0 || type >= NUM_DB_TYPES) { + return 0; + } + _GeoIP_setup_dbfilename(); + filePath = GeoIPDBFileName[type]; + if (NULL == filePath) { + return 0; + } + return _file_exists(filePath); +} +*/ +static +void _setup_segments(GeoIP * gi) { + int i, j; + unsigned char delim[3]; + unsigned char buf[SEGMENT_RECORD_LENGTH]; + + gi->databaseSegments = NULL; + + /* default to GeoIP Country Edition */ + gi->databaseType = GEOIP_COUNTRY_EDITION; + gi->record_length = STANDARD_RECORD_LENGTH; + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) { + fread(delim, 1, 3, gi->GeoIPDatabase); + if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) { + fread(&gi->databaseType, 1, 1, gi->GeoIPDatabase); + if (gi->databaseType >= 106) { + /* backwards compatibility with databases from April 2003 and earlier */ + gi->databaseType -= 105; + } + + if (gi->databaseType == GEOIP_REGION_EDITION_REV0) { + /* Region Edition, pre June 2003 */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = STATE_BEGIN_REV0; + } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) { + /* Region Edition, post June 2003 */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = STATE_BEGIN_REV1; + } else if (gi->databaseType == GEOIP_CITY_EDITION_REV0 || + gi->databaseType == GEOIP_CITY_EDITION_REV1 || + gi->databaseType == GEOIP_ORG_EDITION || + gi->databaseType == GEOIP_ISP_EDITION || + gi->databaseType == GEOIP_ASNUM_EDITION) { + /* City/Org Editions have two segments, read offset of second segment */ + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = 0; + fread(buf, SEGMENT_RECORD_LENGTH, 1, gi->GeoIPDatabase); + for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) { + gi->databaseSegments[0] += (buf[j] << (j * 8)); + } + if (gi->databaseType == GEOIP_ORG_EDITION || + gi->databaseType == GEOIP_ISP_EDITION) + gi->record_length = ORG_RECORD_LENGTH; + } + break; + } else { + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + } + if (gi->databaseType == GEOIP_COUNTRY_EDITION || + gi->databaseType == GEOIP_PROXY_EDITION || + gi->databaseType == GEOIP_NETSPEED_EDITION) { + gi->databaseSegments = (unsigned int*)malloc(sizeof(unsigned int)); + gi->databaseSegments[0] = COUNTRY_BEGIN; + } +} + +static +int _check_mtime(GeoIP *gi) { + struct stat buf; + if (gi->flags & GEOIP_CHECK_CACHE) { + if (stat(gi->file_path, &buf) != -1) { + if (buf.st_mtime != gi->mtime) { + int name_len; + wchar_t* wfilename; + wchar_t const* dst_start; + char const* src_start; + /* GeoIP Database file updated */ + if (gi->flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE)) { +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + munmap(gi->cache, gi->size); + gi->cache = NULL; + } else +#endif + { + /* reload database into memory cache */ + if ((gi->cache = (unsigned char*) realloc(gi->cache, buf.st_size)) == NULL) { + fprintf(stderr,"Out of memory when reloading %s\n",gi->file_path); + return -1; + } + } + } + /* refresh filehandle */ + fclose(gi->GeoIPDatabase); +#ifdef WIN32 + assert(sizeof(wchar_t) == 2); + name_len = strlen(gi->file_path); + wfilename = malloc((name_len + 1) * sizeof(wchar_t)); + dst_start = wfilename; + src_start = gi->file_path; + ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + name_len+1, (UTF16**)&dst_start, (UTF16*)dst_start + name_len + 1 + , lenientConversion); + gi->GeoIPDatabase = _wfopen(wfilename,L"rb"); + free(wfilename); +#else + gi->GeoIPDatabase = fopen(gi->file_path,"rb"); +#endif + if (gi->GeoIPDatabase == NULL) { + fprintf(stderr,"Error Opening file %s when reloading\n",gi->file_path); + return -1; + } + gi->mtime = buf.st_mtime; + gi->size = buf.st_size; + +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + gi->cache = (unsigned char*)mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fileno(gi->GeoIPDatabase), 0); + if ( gi->cache == MAP_FAILED ) { + + fprintf(stderr,"Error remapping file %s when reloading\n",gi->file_path); + gi->cache = 0; + return -1; + } + } else +#endif + if ( gi->flags & GEOIP_MEMORY_CACHE ) { + if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) { + fprintf(stderr,"Error reading file %s when reloading\n",gi->file_path); + return -1; + } + } + if (gi->databaseSegments != NULL) { + free(gi->databaseSegments); + gi->databaseSegments = NULL; + } + _setup_segments(gi); + if (gi->databaseSegments == NULL) { + fprintf(stderr, "Error reading file %s -- corrupt\n", gi->file_path); + return -1; + } + if (gi->flags & GEOIP_INDEX_CACHE) { + gi->index_cache = (unsigned char *) realloc(gi->index_cache, sizeof(unsigned char) * ((gi->databaseSegments[0] * (long)gi->record_length * 2))); + if (gi->index_cache != NULL) { + fseek(gi->GeoIPDatabase, 0, SEEK_SET); + if (fread(gi->index_cache, sizeof(unsigned char), gi->databaseSegments[0] * (long)gi->record_length * 2, gi->GeoIPDatabase) != (size_t) (gi->databaseSegments[0]*(long)gi->record_length * 2)) { + fprintf(stderr,"Error reading file %s where reloading\n",gi->file_path); + return -1; + } + } + } + } + } + } + return 0; +} + +unsigned int _GeoIP_seek_record (GeoIP *gi, unsigned long ipnum) { + int depth; + unsigned int x; + unsigned char stack_buffer[2 * MAX_RECORD_LENGTH]; + const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL; + unsigned int offset = 0; + + const unsigned char * p; + int j; + + _check_mtime(gi); + for (depth = 31; depth >= 0; depth--) { + if (gi->cache == NULL && gi->index_cache == NULL) { + /* read from disk */ + fseek(gi->GeoIPDatabase, (long)gi->record_length * 2 * offset, SEEK_SET); + fread(stack_buffer,gi->record_length,2,gi->GeoIPDatabase); + } else if (gi->index_cache == NULL) { + /* simply point to record in memory */ + buf = gi->cache + (long)gi->record_length * 2 *offset; + } else { + buf = gi->index_cache + (long)gi->record_length * 2 * offset; + } + + if (ipnum & (1 << depth)) { + /* Take the right-hand branch */ + if ( gi->record_length == 3 ) { + /* Most common case is completely unrolled and uses constants. */ + x = (buf[3*1 + 0] << (0*8)) + + (buf[3*1 + 1] << (1*8)) + + (buf[3*1 + 2] << (2*8)); + + } else { + /* General case */ + j = gi->record_length; + p = &buf[2*j]; + x = 0; + do { + x <<= 8; + x += *(--p); + } while ( --j ); + } + + } else { + /* Take the left-hand branch */ + if ( gi->record_length == 3 ) { + /* Most common case is completely unrolled and uses constants. */ + x = (buf[3*0 + 0] << (0*8)) + + (buf[3*0 + 1] << (1*8)) + + (buf[3*0 + 2] << (2*8)); + } else { + /* General case */ + j = gi->record_length; + p = &buf[1*j]; + x = 0; + do { + x <<= 8; + x += *(--p); + } while ( --j ); + } + } + + if (x >= gi->databaseSegments[0]) { + gi->netmask = 32 - depth; + return x; + } + offset = x; + } + + /* shouldn't reach here */ + fprintf(stderr,"Error Traversing Database for ipnum = %lu - Perhaps database is corrupt?\n",ipnum); + return 0; +} + +unsigned long +_GeoIP_addr_to_num(const char *addr) +{ + unsigned int c, octet, t; + unsigned long ipnum; + int i = 3; + + octet = ipnum = 0; + while ((c = *addr++)) { + if (c == '.') { + if (octet > 255) + return 0; + ipnum <<= 8; + ipnum += octet; + i--; + octet = 0; + } else { + t = octet; + octet <<= 3; + octet += t; + octet += t; + c -= '0'; + if (c > 9) + return 0; + octet += c; + } + } + if ((octet > 255) || (i != 0)) + return 0; + ipnum <<= 8; + return ipnum + octet; +} +/* +GeoIP* GeoIP_open_type (int type, int flags) { + GeoIP * gi; + const char * filePath; + if (type < 0 || type >= NUM_DB_TYPES) { + printf("Invalid database type %d\n", type); + return NULL; + } + _GeoIP_setup_dbfilename(); + filePath = GeoIPDBFileName[type]; + if (filePath == NULL) { + printf("Invalid database type %d\n", type); + return NULL; + } + gi = GeoIP_open (filePath, flags); + return gi; +} + +GeoIP* GeoIP_new (int flags) { + GeoIP * gi; + _GeoIP_setup_dbfilename(); + gi = GeoIP_open (GeoIPDBFileName[GEOIP_COUNTRY_EDITION], flags); + return gi; +} +*/ + +GeoIP* GeoIP_open (const char * filename, int flags) { + struct stat buf; + GeoIP * gi; + size_t len; +#ifdef WIN32 + int name_len; + wchar_t* wfilename; + wchar_t const* dst_start; + char const* src_start; +#endif + + gi = (GeoIP *)malloc(sizeof(GeoIP)); + if (gi == NULL) + return NULL; + len = sizeof(char) * (strlen(filename)+1); + gi->file_path = (char*)malloc(len); + if (gi->file_path == NULL) { + free(gi); + return NULL; + } + strncpy(gi->file_path, filename, len); +#ifdef WIN32 + assert(sizeof(wchar_t) == 2); + name_len = strlen(filename); + wfilename = malloc((name_len + 1) * sizeof(wchar_t)); + dst_start = wfilename; + src_start = filename; + ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + name_len+1, (UTF16**)&dst_start, (UTF16*)dst_start + name_len + 1 + , lenientConversion); + gi->GeoIPDatabase = _wfopen(wfilename,L"rb"); + free(wfilename); +#else + gi->GeoIPDatabase = fopen(filename,"rb"); +#endif + if (gi->GeoIPDatabase == NULL) { + fprintf(stderr,"Error Opening file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } else { + if (flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE) ) { + if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) { + fprintf(stderr,"Error stating file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + gi->mtime = buf.st_mtime; + gi->size = buf.st_size; +#if !defined WIN32 && !defined __OS2__ + /* MMAP added my Peter Shipley */ + if ( flags & GEOIP_MMAP_CACHE) { + gi->cache = (unsigned char*)mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fileno(gi->GeoIPDatabase), 0); + if ( gi->cache == MAP_FAILED ) { + fprintf(stderr,"Error mmaping file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + } else +#endif + { + gi->cache = (unsigned char *) malloc(sizeof(unsigned char) * buf.st_size); + + if (gi->cache != NULL) { + if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) { + fprintf(stderr,"Error reading file %s\n",filename); + free(gi->cache); + free(gi->file_path); + free(gi); + return NULL; + } + } + } + } else { + if (flags & GEOIP_CHECK_CACHE) { + if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) { + fprintf(stderr,"Error stating file %s\n",filename); + free(gi->file_path); + free(gi); + return NULL; + } + gi->mtime = buf.st_mtime; + } + gi->cache = NULL; + } + gi->flags = flags; + gi->charset = GEOIP_CHARSET_ISO_8859_1; + + _setup_segments(gi); + if (flags & GEOIP_INDEX_CACHE) { + gi->index_cache = (unsigned char *) malloc(sizeof(unsigned char) * ((gi->databaseSegments[0] * (long)gi->record_length * 2))); + if (gi->index_cache != NULL) { + fseek(gi->GeoIPDatabase, 0, SEEK_SET); + if (fread(gi->index_cache, sizeof(unsigned char), gi->databaseSegments[0] * (long)gi->record_length * 2, gi->GeoIPDatabase) != (size_t) (gi->databaseSegments[0]*(long)gi->record_length * 2)) { + fprintf(stderr,"Error reading file %s\n",filename); + free(gi->databaseSegments); + free(gi->index_cache); + free(gi); + return NULL; + } + } + } else { + gi->index_cache = NULL; + } + return gi; + } +} + +void GeoIP_delete (GeoIP *gi) { + if (gi == NULL ) + return; + if (gi->GeoIPDatabase != NULL) + fclose(gi->GeoIPDatabase); + if (gi->cache != NULL) { +#if !defined WIN32 && !defined __OS2__ + if ( gi->flags & GEOIP_MMAP_CACHE) { + munmap(gi->cache, gi->size); + } else +#endif + { + free(gi->cache); + } + gi->cache = NULL; + } + if (gi->index_cache != NULL) + free(gi->index_cache); + if (gi->file_path != NULL) + free(gi->file_path); + if (gi->databaseSegments != NULL) + free(gi->databaseSegments); + free(gi); +} + +const char *GeoIP_country_code_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; +} + +const char *GeoIP_country_name_by_name (GeoIP* gi, const char *name) { + int country_id; + country_id = GeoIP_id_by_name(gi, name); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; +} + +unsigned long _GeoIP_lookupaddress (const char *host) { + unsigned long addr = inet_addr(host); + struct hostent phe2; + struct hostent * phe = &phe2; + char *buf = NULL; + int buflength = 16384; + int herr = 0; + int result = 0; +#ifdef HAVE_GETHOSTBYNAME_R + buf = malloc(buflength); +#endif + if (addr == INADDR_NONE) { +#ifdef HAVE_GETHOSTBYNAME_R + while (1) { + /* we use gethostbyname_r here because it is thread-safe and gethostbyname is not */ +#ifdef GETHOSTBYNAME_R_RETURNS_INT + result = gethostbyname_r(host,&phe2,buf,buflength,&phe,&herr); +#else + phe = gethostbyname_r(host,&phe2,buf,buflength,&herr); +#endif + if (herr != ERANGE) + break; + if (result == 0) + break; + /* double the buffer if the buffer is too small */ + buflength = buflength * 2; + buf = realloc(buf,buflength); + } +#endif +#ifndef HAVE_GETHOSTBYNAME_R + /* Some systems do not support gethostbyname_r, such as Mac OS X */ + phe = gethostbyname(host); +#endif + if (!phe || result != 0) { + free(buf); + return 0; + } + addr = *((unsigned long *) phe->h_addr_list[0]); + } +#ifdef HAVE_GETHOSTBYNAME_R + free(buf); +#endif + return ntohl(addr); +} + +int GeoIP_id_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + int ret; + if (name == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && gi->databaseType != GEOIP_PROXY_EDITION && gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; + +} + +const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; + return GeoIP_country_code3[country_id]; +} + +const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr) { + int country_id; + country_id = GeoIP_id_by_addr(gi, addr); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; + return GeoIP_country_name[country_id]; +} + +const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_name[country_id] : NULL; +} + +const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_code[country_id] : NULL; +} + +const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int country_id; + country_id = GeoIP_id_by_ipnum(gi, ipnum); + return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL; +} + +int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr) { + return GeoIP_id_by_addr(gi, addr); +} + +int GeoIP_country_id_by_name (GeoIP* gi, const char *host) { + return GeoIP_id_by_name(gi, host); +} + +int GeoIP_id_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + int ret; + if (addr == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && + gi->databaseType != GEOIP_PROXY_EDITION && + gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; +} + +int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum) { + int ret; + if (ipnum == 0) { + return 0; + } + if (gi->databaseType != GEOIP_COUNTRY_EDITION && + gi->databaseType != GEOIP_PROXY_EDITION && + gi->databaseType != GEOIP_NETSPEED_EDITION) { + printf("Invalid database type %s, expected %s\n", + GeoIPDBDescription[(int)gi->databaseType], + GeoIPDBDescription[GEOIP_COUNTRY_EDITION]); + return 0; + } + ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN; + return ret; +} + +char *GeoIP_database_info (GeoIP* gi) { + int i; + unsigned char buf[3]; + char *retval; + int hasStructureInfo = 0; + + if(gi == NULL) + return NULL; + + _check_mtime(gi); + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + + /* first get past the database structure information */ + for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) { + fread(buf, 1, 3, gi->GeoIPDatabase); + if (buf[0] == 255 && buf[1] == 255 && buf[2] == 255) { + hasStructureInfo = 1; + break; + } + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + if (hasStructureInfo == 1) { + fseek(gi->GeoIPDatabase, -6l, SEEK_CUR); + } else { + /* no structure info, must be pre Sep 2002 database, go back to end */ + fseek(gi->GeoIPDatabase, -3l, SEEK_END); + } + + for (i = 0; i < DATABASE_INFO_MAX_SIZE; i++) { + fread(buf, 1, 3, gi->GeoIPDatabase); + if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) { + retval = (char*)malloc(sizeof(char) * (i+1)); + if (retval == NULL) { + return NULL; + } + fread(retval, 1, i, gi->GeoIPDatabase); + retval[i] = '\0'; + return retval; + } + fseek(gi->GeoIPDatabase, -4l, SEEK_CUR); + } + return NULL; +} + +/* GeoIP Region Edition functions */ + +void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *region) { + unsigned int seek_region; + + /* This also writes in the terminating NULs (if you decide to + * keep them) and clear any fields that are not set. */ + memset(region, 0, sizeof(GeoIPRegion)); + + seek_region = _GeoIP_seek_record(gi, ntohl(inetaddr)); + + if (gi->databaseType == GEOIP_REGION_EDITION_REV0) { + /* Region Edition, pre June 2003 */ + seek_region -= STATE_BEGIN_REV0; + if (seek_region >= 1000) { + region->country_code[0] = 'U'; + region->country_code[1] = 'S'; + region->region[0] = (char) ((seek_region - 1000)/26 + 65); + region->region[1] = (char) ((seek_region - 1000)%26 + 65); + } else { + memcpy(region->country_code, GeoIP_country_code[seek_region], 2); + } + } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) { + /* Region Edition, post June 2003 */ + seek_region -= STATE_BEGIN_REV1; + if (seek_region < US_OFFSET) { + /* Unknown */ + /* we don't need to do anything here b/c we memset region to 0 */ + } else if (seek_region < CANADA_OFFSET) { + /* USA State */ + region->country_code[0] = 'U'; + region->country_code[1] = 'S'; + region->region[0] = (char) ((seek_region - US_OFFSET)/26 + 65); + region->region[1] = (char) ((seek_region - US_OFFSET)%26 + 65); + } else if (seek_region < WORLD_OFFSET) { + /* Canada Province */ + region->country_code[0] = 'C'; + region->country_code[1] = 'A'; + region->region[0] = (char) ((seek_region - CANADA_OFFSET)/26 + 65); + region->region[1] = (char) ((seek_region - CANADA_OFFSET)%26 + 65); + } else { + /* Not US or Canada */ + memcpy(region->country_code, GeoIP_country_code[(seek_region - WORLD_OFFSET) / FIPS_RANGE], 2); + } + } +} + +static +GeoIPRegion * _get_region(GeoIP* gi, unsigned long ipnum) { + GeoIPRegion * region; + + region = (GeoIPRegion*)malloc(sizeof(GeoIPRegion)); + if (region) { + GeoIP_assign_region_by_inetaddr(gi, htonl(ipnum), region); + } + return region; +} + +GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + if (addr == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + return _get_region(gi, ipnum); +} + +GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + if (name == NULL) { + return 0; + } + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + return _get_region(gi, ipnum); +} + +GeoIPRegion * GeoIP_region_by_ipnum (GeoIP* gi, unsigned long ipnum) { + if (gi->databaseType != GEOIP_REGION_EDITION_REV0 && + gi->databaseType != GEOIP_REGION_EDITION_REV1) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]); + return 0; + } + return _get_region(gi, ipnum); +} + +void GeoIPRegion_delete (GeoIPRegion *gir) { + free(gir); +} + +/* GeoIP Organization, ISP and AS Number Edition private method */ +static +char *_get_name (GeoIP* gi, unsigned long ipnum) { + int seek_org; + char buf[MAX_ORG_RECORD_LENGTH]; + char * org_buf, * buf_pointer; + int record_pointer; + size_t len; + + if (gi->databaseType != GEOIP_ORG_EDITION && + gi->databaseType != GEOIP_ISP_EDITION && + gi->databaseType != GEOIP_ASNUM_EDITION) { + printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_ORG_EDITION]); + return 0; + } + + seek_org = _GeoIP_seek_record(gi, ipnum); + if (seek_org == gi->databaseSegments[0]) + return NULL; + + record_pointer = seek_org + (2 * gi->record_length - 1) * gi->databaseSegments[0]; + + if (gi->cache == NULL) { + fseek(gi->GeoIPDatabase, record_pointer, SEEK_SET); + fread(buf, sizeof(char), MAX_ORG_RECORD_LENGTH, gi->GeoIPDatabase); + len = sizeof(char) * (strlen(buf)+1); + org_buf = (char*)malloc(len); + strncpy(org_buf, buf, len); + } else { + buf_pointer = (char*)(gi->cache + (long)record_pointer); + len = sizeof(char) * (strlen(buf_pointer)+1); + org_buf = (char*)malloc(len); + strncpy(org_buf, buf_pointer, len); + } + return org_buf; +} + +char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum) { + return _get_name(gi,ipnum); +} + +char *GeoIP_name_by_addr (GeoIP* gi, const char *addr) { + unsigned long ipnum; + if (addr == NULL) { + return 0; + } + ipnum = _GeoIP_addr_to_num(addr); + return _get_name(gi, ipnum); +} + +char *GeoIP_name_by_name (GeoIP* gi, const char *name) { + unsigned long ipnum; + if (name == NULL) { + return 0; + } + if (!(ipnum = _GeoIP_lookupaddress(name))) + return 0; + return _get_name(gi, ipnum); +} + +char *GeoIP_org_by_ipnum (GeoIP* gi, unsigned long ipnum) { + return GeoIP_name_by_ipnum(gi, ipnum); +} + +char *GeoIP_org_by_addr (GeoIP* gi, const char *addr) { + return GeoIP_name_by_addr(gi, addr); +} + +char *GeoIP_org_by_name (GeoIP* gi, const char *name) { + return GeoIP_name_by_name(gi, name); +} + +unsigned char GeoIP_database_edition (GeoIP* gi) { + return gi->databaseType; +} + +int GeoIP_charset( GeoIP* gi){ + return gi->charset; +} + +int GeoIP_set_charset( GeoIP* gi, int charset ){ + int old_charset = gi->charset; + gi->charset = charset; + return old_charset; +} + +int GeoIP_last_netmask (GeoIP* gi) { + return gi->netmask; +} + diff --git a/apps/Launcher/ext/libtorrent/src/Makefile.am b/apps/Launcher/ext/libtorrent/src/Makefile.am new file mode 100644 index 0000000000..a285470e49 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/Makefile.am @@ -0,0 +1,141 @@ +AUTOMAKE_OPTIONS = subdir-objects + +lib_LTLIBRARIES = libtorrent-rasterbar.la + +if ENABLE_DHT +KADEMLIA_SOURCES = \ + kademlia/dht_tracker.cpp \ + kademlia/find_data.cpp \ + kademlia/node.cpp \ + kademlia/node_id.cpp \ + kademlia/refresh.cpp \ + kademlia/routing_table.cpp \ + kademlia/rpc_manager.cpp \ + kademlia/logging.cpp \ + kademlia/traversal_algorithm.cpp \ + kademlia/get_peers.cpp \ + kademlia/get_item.cpp \ + kademlia/item.cpp \ + ../ed25519/src/add_scalar.cpp \ + ../ed25519/src/fe.cpp \ + ../ed25519/src/ge.cpp \ + ../ed25519/src/key_exchange.cpp \ + ../ed25519/src/keypair.cpp \ + ../ed25519/src/sc.cpp \ + ../ed25519/src/seed.cpp \ + ../ed25519/src/sha512.cpp \ + ../ed25519/src/sign.cpp \ + ../ed25519/src/verify.cpp +endif + +if WITH_SHIPPED_GEOIP +GEOIP_SOURCES = GeoIP.c +endif + +if WITH_OPENSSL +ASIO_OPENSSL_SOURCES = asio_ssl.cpp +endif + +libtorrent_rasterbar_la_SOURCES = \ + web_connection_base.cpp \ + alert.cpp \ + alert_manager.cpp \ + allocator.cpp \ + asio.cpp \ + assert.cpp \ + bandwidth_limit.cpp \ + bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp \ + bloom_filter.cpp \ + broadcast_socket.cpp \ + bt_peer_connection.cpp \ + chained_buffer.cpp \ + connection_queue.cpp \ + ConvertUTF.cpp \ + create_torrent.cpp \ + disk_buffer_holder.cpp \ + disk_buffer_pool.cpp \ + disk_io_thread.cpp \ + entry.cpp \ + enum_net.cpp \ + error_code.cpp \ + escape_string.cpp \ + file.cpp \ + file_pool.cpp \ + file_storage.cpp \ + gzip.cpp \ + hasher.cpp \ + http_connection.cpp \ + http_parser.cpp \ + http_seed_connection.cpp \ + http_stream.cpp \ + http_tracker_connection.cpp \ + i2p_stream.cpp \ + identify_client.cpp \ + instantiate_connection.cpp \ + ip_filter.cpp \ + ip_voter.cpp \ + lazy_bdecode.cpp \ + logger.cpp \ + lsd.cpp \ + lt_trackers.cpp \ + magnet_uri.cpp \ + metadata_transfer.cpp \ + mpi.c \ + natpmp.cpp \ + parse_url.cpp \ + pe_crypto.cpp \ + peer_connection.cpp \ + piece_picker.cpp \ + packet_buffer.cpp \ + policy.cpp \ + puff.cpp \ + random.cpp \ + rss.cpp \ + session.cpp \ + session_impl.cpp \ + settings.cpp \ + sha1.cpp \ + smart_ban.cpp \ + socket_io.cpp \ + socket_type.cpp \ + socks5_stream.cpp \ + stat.cpp \ + storage.cpp \ + string_util.cpp \ + thread.cpp \ + torrent.cpp \ + torrent_handle.cpp \ + torrent_info.cpp \ + time.cpp \ + timestamp_history.cpp \ + tracker_manager.cpp \ + udp_socket.cpp \ + udp_tracker_connection.cpp \ + upnp.cpp \ + ut_metadata.cpp \ + ut_pex.cpp \ + utf8.cpp \ + utp_socket_manager.cpp \ + utp_stream.cpp \ + web_peer_connection.cpp \ + xml_parse.cpp \ + \ + $(KADEMLIA_SOURCES) \ + $(GEOIP_SOURCES) \ + $(ASIO_OPENSSL_SOURCES) + +#libtorrent_rasterbar_la_LDFLAGS = $(LDFLAGS) -version-info $(INTERFACE_VERSION_INFO) +libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) + +#libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LIBS@ +libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ + +#AM_CXXFLAGS= -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 + +#AM_CFLAGS= -I$(top_srcdir)/include @DEBUGFLAGS@ +#AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ +AM_LDFLAGS = @OPENSSL_LDFLAGS@ + diff --git a/apps/Launcher/ext/libtorrent/src/Makefile.in b/apps/Launcher/ext/libtorrent/src/Makefile.in new file mode 100644 index 0000000000..99b32c067c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/Makefile.in @@ -0,0 +1,1116 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_python.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_check_geoip.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/gettext-lib.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkgconfig.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libtorrent_rasterbar_la_DEPENDENCIES = +am__libtorrent_rasterbar_la_SOURCES_DIST = web_connection_base.cpp \ + alert.cpp alert_manager.cpp allocator.cpp asio.cpp assert.cpp \ + bandwidth_limit.cpp bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp bloom_filter.cpp \ + broadcast_socket.cpp bt_peer_connection.cpp chained_buffer.cpp \ + connection_queue.cpp ConvertUTF.cpp create_torrent.cpp \ + disk_buffer_holder.cpp disk_buffer_pool.cpp disk_io_thread.cpp \ + entry.cpp enum_net.cpp error_code.cpp escape_string.cpp \ + file.cpp file_pool.cpp file_storage.cpp gzip.cpp hasher.cpp \ + http_connection.cpp http_parser.cpp http_seed_connection.cpp \ + http_stream.cpp http_tracker_connection.cpp i2p_stream.cpp \ + identify_client.cpp instantiate_connection.cpp ip_filter.cpp \ + ip_voter.cpp lazy_bdecode.cpp logger.cpp lsd.cpp \ + lt_trackers.cpp magnet_uri.cpp metadata_transfer.cpp mpi.c \ + natpmp.cpp parse_url.cpp pe_crypto.cpp peer_connection.cpp \ + piece_picker.cpp packet_buffer.cpp policy.cpp puff.cpp \ + random.cpp rss.cpp session.cpp session_impl.cpp settings.cpp \ + sha1.cpp smart_ban.cpp socket_io.cpp socket_type.cpp \ + socks5_stream.cpp stat.cpp storage.cpp string_util.cpp \ + thread.cpp torrent.cpp torrent_handle.cpp torrent_info.cpp \ + time.cpp timestamp_history.cpp tracker_manager.cpp \ + udp_socket.cpp udp_tracker_connection.cpp upnp.cpp \ + ut_metadata.cpp ut_pex.cpp utf8.cpp utp_socket_manager.cpp \ + utp_stream.cpp web_peer_connection.cpp xml_parse.cpp \ + kademlia/dht_tracker.cpp kademlia/find_data.cpp \ + kademlia/node.cpp kademlia/node_id.cpp kademlia/refresh.cpp \ + kademlia/routing_table.cpp kademlia/rpc_manager.cpp \ + kademlia/logging.cpp kademlia/traversal_algorithm.cpp \ + kademlia/get_peers.cpp kademlia/get_item.cpp kademlia/item.cpp \ + ../ed25519/src/add_scalar.cpp ../ed25519/src/fe.cpp \ + ../ed25519/src/ge.cpp ../ed25519/src/key_exchange.cpp \ + ../ed25519/src/keypair.cpp ../ed25519/src/sc.cpp \ + ../ed25519/src/seed.cpp ../ed25519/src/sha512.cpp \ + ../ed25519/src/sign.cpp ../ed25519/src/verify.cpp GeoIP.c \ + asio_ssl.cpp +am__dirstamp = $(am__leading_dot)dirstamp +@ENABLE_DHT_TRUE@am__objects_1 = kademlia/dht_tracker.lo \ +@ENABLE_DHT_TRUE@ kademlia/find_data.lo kademlia/node.lo \ +@ENABLE_DHT_TRUE@ kademlia/node_id.lo kademlia/refresh.lo \ +@ENABLE_DHT_TRUE@ kademlia/routing_table.lo \ +@ENABLE_DHT_TRUE@ kademlia/rpc_manager.lo kademlia/logging.lo \ +@ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.lo \ +@ENABLE_DHT_TRUE@ kademlia/get_peers.lo kademlia/get_item.lo \ +@ENABLE_DHT_TRUE@ kademlia/item.lo ../ed25519/src/add_scalar.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/fe.lo ../ed25519/src/ge.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/keypair.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sc.lo ../ed25519/src/seed.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sha512.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sign.lo \ +@ENABLE_DHT_TRUE@ ../ed25519/src/verify.lo +@WITH_SHIPPED_GEOIP_TRUE@am__objects_2 = GeoIP.lo +@WITH_OPENSSL_TRUE@am__objects_3 = asio_ssl.lo +am_libtorrent_rasterbar_la_OBJECTS = web_connection_base.lo alert.lo \ + alert_manager.lo allocator.lo asio.lo assert.lo \ + bandwidth_limit.lo bandwidth_manager.lo \ + bandwidth_queue_entry.lo bloom_filter.lo broadcast_socket.lo \ + bt_peer_connection.lo chained_buffer.lo connection_queue.lo \ + ConvertUTF.lo create_torrent.lo disk_buffer_holder.lo \ + disk_buffer_pool.lo disk_io_thread.lo entry.lo enum_net.lo \ + error_code.lo escape_string.lo file.lo file_pool.lo \ + file_storage.lo gzip.lo hasher.lo http_connection.lo \ + http_parser.lo http_seed_connection.lo http_stream.lo \ + http_tracker_connection.lo i2p_stream.lo identify_client.lo \ + instantiate_connection.lo ip_filter.lo ip_voter.lo \ + lazy_bdecode.lo logger.lo lsd.lo lt_trackers.lo magnet_uri.lo \ + metadata_transfer.lo mpi.lo natpmp.lo parse_url.lo \ + pe_crypto.lo peer_connection.lo piece_picker.lo \ + packet_buffer.lo policy.lo puff.lo random.lo rss.lo session.lo \ + session_impl.lo settings.lo sha1.lo smart_ban.lo socket_io.lo \ + socket_type.lo socks5_stream.lo stat.lo storage.lo \ + string_util.lo thread.lo torrent.lo torrent_handle.lo \ + torrent_info.lo time.lo timestamp_history.lo \ + tracker_manager.lo udp_socket.lo udp_tracker_connection.lo \ + upnp.lo ut_metadata.lo ut_pex.lo utf8.lo utp_socket_manager.lo \ + utp_stream.lo web_peer_connection.lo xml_parse.lo \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) +libtorrent_rasterbar_la_OBJECTS = \ + $(am_libtorrent_rasterbar_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libtorrent_rasterbar_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libtorrent_rasterbar_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libtorrent_rasterbar_la_SOURCES) +DIST_SOURCES = $(am__libtorrent_rasterbar_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_PYTHON_LIB = @BOOST_PYTHON_LIB@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPILETIME_OPTIONS = @COMPILETIME_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GEOIP_CFLAGS = @GEOIP_CFLAGS@ +GEOIP_LIBS = @GEOIP_LIBS@ +GREP = @GREP@ +ICONV_LIBS = @ICONV_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTERFACE_VERSION_INFO = @INTERFACE_VERSION_INFO@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_INCLUDES = @OPENSSL_INCLUDES@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_INSTALL_PARAMS = @PYTHON_INSTALL_PARAMS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = subdir-objects +lib_LTLIBRARIES = libtorrent-rasterbar.la +@ENABLE_DHT_TRUE@KADEMLIA_SOURCES = \ +@ENABLE_DHT_TRUE@ kademlia/dht_tracker.cpp \ +@ENABLE_DHT_TRUE@ kademlia/find_data.cpp \ +@ENABLE_DHT_TRUE@ kademlia/node.cpp \ +@ENABLE_DHT_TRUE@ kademlia/node_id.cpp \ +@ENABLE_DHT_TRUE@ kademlia/refresh.cpp \ +@ENABLE_DHT_TRUE@ kademlia/routing_table.cpp \ +@ENABLE_DHT_TRUE@ kademlia/rpc_manager.cpp \ +@ENABLE_DHT_TRUE@ kademlia/logging.cpp \ +@ENABLE_DHT_TRUE@ kademlia/traversal_algorithm.cpp \ +@ENABLE_DHT_TRUE@ kademlia/get_peers.cpp \ +@ENABLE_DHT_TRUE@ kademlia/get_item.cpp \ +@ENABLE_DHT_TRUE@ kademlia/item.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/add_scalar.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/fe.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/ge.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/key_exchange.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/keypair.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sc.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/seed.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sha512.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/sign.cpp \ +@ENABLE_DHT_TRUE@ ../ed25519/src/verify.cpp + +@WITH_SHIPPED_GEOIP_TRUE@GEOIP_SOURCES = GeoIP.c +@WITH_OPENSSL_TRUE@ASIO_OPENSSL_SOURCES = asio_ssl.cpp +libtorrent_rasterbar_la_SOURCES = \ + web_connection_base.cpp \ + alert.cpp \ + alert_manager.cpp \ + allocator.cpp \ + asio.cpp \ + assert.cpp \ + bandwidth_limit.cpp \ + bandwidth_manager.cpp \ + bandwidth_queue_entry.cpp \ + bloom_filter.cpp \ + broadcast_socket.cpp \ + bt_peer_connection.cpp \ + chained_buffer.cpp \ + connection_queue.cpp \ + ConvertUTF.cpp \ + create_torrent.cpp \ + disk_buffer_holder.cpp \ + disk_buffer_pool.cpp \ + disk_io_thread.cpp \ + entry.cpp \ + enum_net.cpp \ + error_code.cpp \ + escape_string.cpp \ + file.cpp \ + file_pool.cpp \ + file_storage.cpp \ + gzip.cpp \ + hasher.cpp \ + http_connection.cpp \ + http_parser.cpp \ + http_seed_connection.cpp \ + http_stream.cpp \ + http_tracker_connection.cpp \ + i2p_stream.cpp \ + identify_client.cpp \ + instantiate_connection.cpp \ + ip_filter.cpp \ + ip_voter.cpp \ + lazy_bdecode.cpp \ + logger.cpp \ + lsd.cpp \ + lt_trackers.cpp \ + magnet_uri.cpp \ + metadata_transfer.cpp \ + mpi.c \ + natpmp.cpp \ + parse_url.cpp \ + pe_crypto.cpp \ + peer_connection.cpp \ + piece_picker.cpp \ + packet_buffer.cpp \ + policy.cpp \ + puff.cpp \ + random.cpp \ + rss.cpp \ + session.cpp \ + session_impl.cpp \ + settings.cpp \ + sha1.cpp \ + smart_ban.cpp \ + socket_io.cpp \ + socket_type.cpp \ + socks5_stream.cpp \ + stat.cpp \ + storage.cpp \ + string_util.cpp \ + thread.cpp \ + torrent.cpp \ + torrent_handle.cpp \ + torrent_info.cpp \ + time.cpp \ + timestamp_history.cpp \ + tracker_manager.cpp \ + udp_socket.cpp \ + udp_tracker_connection.cpp \ + upnp.cpp \ + ut_metadata.cpp \ + ut_pex.cpp \ + utf8.cpp \ + utp_socket_manager.cpp \ + utp_stream.cpp \ + web_peer_connection.cpp \ + xml_parse.cpp \ + \ + $(KADEMLIA_SOURCES) \ + $(GEOIP_SOURCES) \ + $(ASIO_OPENSSL_SOURCES) + + +#libtorrent_rasterbar_la_LDFLAGS = $(LDFLAGS) -version-info $(INTERFACE_VERSION_INFO) +libtorrent_rasterbar_la_LDFLAGS = -version-info $(INTERFACE_VERSION_INFO) + +#libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LIBS@ +libtorrent_rasterbar_la_LIBADD = @BOOST_SYSTEM_LIB@ @OPENSSL_LIBS@ + +#AM_CXXFLAGS= -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/ed25519/src @DEBUGFLAGS@ @OPENSSL_INCLUDES@ +AM_CFLAGS = -I$(top_srcdir)/ed25519/src -std=c99 + +#AM_CFLAGS= -I$(top_srcdir)/include @DEBUGFLAGS@ +#AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ +AM_LDFLAGS = @OPENSSL_LDFLAGS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +kademlia/$(am__dirstamp): + @$(MKDIR_P) kademlia + @: > kademlia/$(am__dirstamp) +kademlia/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) kademlia/$(DEPDIR) + @: > kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/dht_tracker.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/find_data.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/node.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/node_id.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/refresh.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/routing_table.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/rpc_manager.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/logging.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/traversal_algorithm.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/get_peers.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/get_item.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +kademlia/item.lo: kademlia/$(am__dirstamp) \ + kademlia/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/$(am__dirstamp): + @$(MKDIR_P) ../ed25519/src + @: > ../ed25519/src/$(am__dirstamp) +../ed25519/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ../ed25519/src/$(DEPDIR) + @: > ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/add_scalar.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/fe.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/ge.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/key_exchange.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/keypair.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sc.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/seed.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sha512.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/sign.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) +../ed25519/src/verify.lo: ../ed25519/src/$(am__dirstamp) \ + ../ed25519/src/$(DEPDIR)/$(am__dirstamp) + +libtorrent-rasterbar.la: $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_DEPENDENCIES) $(EXTRA_libtorrent_rasterbar_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libtorrent_rasterbar_la_LINK) -rpath $(libdir) $(libtorrent_rasterbar_la_OBJECTS) $(libtorrent_rasterbar_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f ../ed25519/src/*.$(OBJEXT) + -rm -f ../ed25519/src/*.lo + -rm -f kademlia/*.$(OBJEXT) + -rm -f kademlia/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/add_scalar.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/fe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/ge.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/key_exchange.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/keypair.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/seed.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sha512.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@../ed25519/src/$(DEPDIR)/verify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConvertUTF.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GeoIP.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_ssl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_limit.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_queue_entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bloom_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/broadcast_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chained_buffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection_queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_holder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_buffer_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk_io_thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enum_net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error_code.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gzip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_parser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_seed_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i2p_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/instantiate_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_voter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lazy_bdecode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lt_trackers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/magnet_uri.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata_transfer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpi.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/natpmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_buffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_url.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe_crypto.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/puff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rss.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smart_ban.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_io.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_type.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks5_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string_util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timestamp_history.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upnp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_metadata.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_pex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_socket_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utp_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_connection_base.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml_parse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/dht_tracker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/find_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/get_peers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/logging.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/node_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/routing_table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/rpc_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@kademlia/$(DEPDIR)/traversal_algorithm.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf ../ed25519/src/.libs ../ed25519/src/_libs + -rm -rf kademlia/.libs kademlia/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f ../ed25519/src/$(DEPDIR)/$(am__dirstamp) + -rm -f ../ed25519/src/$(am__dirstamp) + -rm -f kademlia/$(DEPDIR)/$(am__dirstamp) + -rm -f kademlia/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ../ed25519/src/$(DEPDIR) ./$(DEPDIR) kademlia/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/apps/Launcher/ext/libtorrent/src/alert.cpp b/apps/Launcher/ext/libtorrent/src/alert.cpp new file mode 100644 index 0000000000..84a8df12b6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/alert.cpp @@ -0,0 +1,1246 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/extensions.hpp" +#include + +namespace libtorrent { + + alert::alert() : m_timestamp(time_now()) {} + alert::~alert() {} + ptime alert::timestamp() const { return m_timestamp; } + + torrent_alert::torrent_alert(torrent_handle const& h) + : handle(h) + {} + + std::string torrent_alert::message() const + { + if (!handle.is_valid()) return " - "; + torrent_status st = handle.status(torrent_handle::query_name); + if (st.name.empty()) + { + char msg[41]; + to_hex((char const*)&st.info_hash[0], 20, msg); + return msg; + } + return st.name; + } + + peer_alert::peer_alert(torrent_handle const& h, tcp::endpoint const& i + , peer_id const& pi) + : torrent_alert(h) + , ip(i) + , pid(pi) + {} + + std::string peer_alert::message() const + { + error_code ec; + return torrent_alert::message() + " peer (" + print_endpoint(ip) + + ", " + identify_client(pid) + ")"; + } + + tracker_alert::tracker_alert(torrent_handle const& h + , std::string const& u) + : torrent_alert(h) + , url(u) + {} + + std::string tracker_alert::message() const + { + return torrent_alert::message() + " (" + url + ")"; + } + + read_piece_alert::read_piece_alert(torrent_handle const& h + , int p, boost::shared_array d, int s) + : torrent_alert(h) + , buffer(d) + , piece(p) + , size(s) + {} + + read_piece_alert::read_piece_alert(torrent_handle h, int p, error_code e) + : torrent_alert(h) + , ec(e) + , piece(p) + , size(0) + {} + + std::string read_piece_alert::message() const + { + char msg[200]; + if (ec) + { + snprintf(msg, sizeof(msg), "%s: read_piece %u failed: %s" + , torrent_alert::message().c_str() , piece, ec.message().c_str()); + } + else + { + snprintf(msg, sizeof(msg), "%s: read_piece %u successful" + , torrent_alert::message().c_str() , piece); + } + return msg; + } + + file_completed_alert::file_completed_alert(torrent_handle const& h + , int idx) + : torrent_alert(h) + , index(idx) + {} + + std::string file_completed_alert::message() const + { + char msg[200 + TORRENT_MAX_PATH]; + snprintf(msg, sizeof(msg), "%s: file %d finished downloading" + , torrent_alert::message().c_str(), index); + return msg; + } + + file_renamed_alert::file_renamed_alert(torrent_handle const& h + , std::string const& n + , int idx) + : torrent_alert(h) + , name(n) + , index(idx) + {} + + std::string file_renamed_alert::message() const + { + char msg[200 + TORRENT_MAX_PATH * 2]; + snprintf(msg, sizeof(msg), "%s: file %d renamed to %s", torrent_alert::message().c_str() + , index, name.c_str()); + return msg; + } + + file_rename_failed_alert::file_rename_failed_alert(torrent_handle const& h + , int idx + , error_code ec) + : torrent_alert(h) + , index(idx) + , error(ec) + {} + + std::string file_rename_failed_alert::message() const + { + char ret[200 + TORRENT_MAX_PATH * 2]; + snprintf(ret, sizeof(ret), "%s: failed to rename file %d: %s" + , torrent_alert::message().c_str(), index, convert_from_native(error.message()).c_str()); + return ret; + } + + performance_alert::performance_alert(torrent_handle const& h + , performance_warning_t w) + : torrent_alert(h) + , warning_code(w) + {} + + std::string performance_alert::message() const + { + static char const* warning_str[] = + { + "max outstanding disk writes reached", + "max outstanding piece requests reached", + "upload limit too low (download rate will suffer)", + "download limit too low (upload rate will suffer)", + "send buffer watermark too low (upload rate will suffer)", + "too many optimistic unchoke slots", + "using bittyrant unchoker with no upload rate limit set", + "the disk queue limit is too high compared to the cache size. The disk queue eats into the cache size", + "too few ports allowed for outgoing connections", + "too few file descriptors are allowed for this process. connection limit lowered" + }; + + return torrent_alert::message() + ": performance warning: " + + warning_str[warning_code]; + } + + state_changed_alert::state_changed_alert(torrent_handle const& h + , torrent_status::state_t st + , torrent_status::state_t prev_st) + : torrent_alert(h) + , state(st) + , prev_state(prev_st) + {} + + std::string state_changed_alert::message() const + { + static char const* state_str[] = + {"checking (q)", "checking", "dl metadata" + , "downloading", "finished", "seeding", "allocating" + , "checking (r)"}; + + return torrent_alert::message() + ": state changed to: " + + state_str[state]; + } + + tracker_error_alert::tracker_error_alert(torrent_handle const& h + , int times + , int status + , std::string const& u + , error_code const& e + , std::string const& m) + : tracker_alert(h, u) + , times_in_row(times) + , status_code(status) + , error(e) + , msg(m) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_error_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s (%d) %s \"%s\" (%d)" + , tracker_alert::message().c_str(), status_code + , error.message().c_str(), msg.c_str(), times_in_row); + return ret; + } + + tracker_warning_alert::tracker_warning_alert(torrent_handle const& h + , std::string const& u + , std::string const& m) + : tracker_alert(h, u) + , msg(m) + { TORRENT_ASSERT(!url.empty()); } + + std::string tracker_warning_alert::message() const + { + return tracker_alert::message() + " warning: " + msg; + } + + scrape_reply_alert::scrape_reply_alert(torrent_handle const& h + , int incomp + , int comp + , std::string const& u) + : tracker_alert(h, u) + , incomplete(incomp) + , complete(comp) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string scrape_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s scrape reply: %u %u" + , tracker_alert::message().c_str(), incomplete, complete); + return ret; + } + + scrape_failed_alert::scrape_failed_alert(torrent_handle const& h + , std::string const& u + , error_code const& e) + : tracker_alert(h, u) + , msg(convert_from_native(e.message())) + { + TORRENT_ASSERT(!url.empty()); + } + + scrape_failed_alert::scrape_failed_alert(torrent_handle const& h + , std::string const& u + , std::string const& m) + : tracker_alert(h, u) + , msg(m) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string scrape_failed_alert::message() const + { + return tracker_alert::message() + " scrape failed: " + msg; + } + + tracker_reply_alert::tracker_reply_alert(torrent_handle const& h + , int np + , std::string const& u) + : tracker_alert(h, u) + , num_peers(np) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s received peers: %u" + , tracker_alert::message().c_str(), num_peers); + return ret; + } + + dht_reply_alert::dht_reply_alert(torrent_handle const& h + , int np) + : tracker_alert(h, "") + , num_peers(np) + {} + + std::string dht_reply_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s received DHT peers: %u" + , tracker_alert::message().c_str(), num_peers); + return ret; + } + + tracker_announce_alert::tracker_announce_alert(torrent_handle const& h + , std::string const& u, int e) + : tracker_alert(h, u) + , event(e) + { + TORRENT_ASSERT(!url.empty()); + } + + std::string tracker_announce_alert::message() const + { + const static char* event_str[] = {"none", "completed", "started", "stopped", "paused"}; + TORRENT_ASSERT_VAL(event < int(sizeof(event_str)/sizeof(event_str[0])), event); + return tracker_alert::message() + " sending announce (" + event_str[event] + ")"; + } + + hash_failed_alert::hash_failed_alert( + torrent_handle const& h + , int index) + : torrent_alert(h) + , piece_index(index) + + { + TORRENT_ASSERT(index >= 0); + } + + std::string hash_failed_alert::message() const + { + char ret[400]; + snprintf(ret, sizeof(ret), "%s hash for piece %u failed" + , torrent_alert::message().c_str(), piece_index); + return ret; + } + + peer_ban_alert::peer_ban_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_ban_alert::message() const + { + return peer_alert::message() + " banned peer"; + } + + peer_unsnubbed_alert::peer_unsnubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_unsnubbed_alert::message() const + { + return peer_alert::message() + " peer unsnubbed"; + } + + peer_snubbed_alert::peer_snubbed_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id) + : peer_alert(h, ep, peer_id) + {} + + std::string peer_snubbed_alert::message() const + { + return peer_alert::message() + " peer snubbed"; + } + + peer_error_alert::peer_error_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e) + : peer_alert(h, ep, peer_id) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string peer_error_alert::message() const + { + return peer_alert::message() + " peer error: " + convert_from_native(error.message()); + } + + invalid_request_alert::invalid_request_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, peer_request const& r) + : peer_alert(h, ep, peer_id) + , request(r) + {} + + std::string invalid_request_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer sent an invalid piece request (piece: %u start: %u len: %u)" + , peer_alert::message().c_str(), request.piece, request.start, request.length); + return ret; + } + + torrent_finished_alert::torrent_finished_alert( + const torrent_handle& h) + : torrent_alert(h) + {} + + std::string torrent_finished_alert::message() const + { + return torrent_alert::message() + " torrent finished downloading"; + } + + piece_finished_alert::piece_finished_alert( + const torrent_handle& h + , int piece_num) + : torrent_alert(h) + , piece_index(piece_num) + { + TORRENT_ASSERT(piece_index >= 0); + } + + std::string piece_finished_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s piece: %u finished downloading" + , torrent_alert::message().c_str(), piece_index); + return ret; + } + + request_dropped_alert::request_dropped_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string request_dropped_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer dropped block ( piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_timeout_alert::block_timeout_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_timeout_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s peer timed out request ( piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_finished_alert::block_finished_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_finished_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s block finished downloading (piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + block_downloading_alert::block_downloading_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, char const* speedmsg, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , peer_speedmsg(speedmsg) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string block_downloading_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s requested block (piece: %u block: %u) %s" + , torrent_alert::message().c_str(), piece_index, block_index, peer_speedmsg); + return ret; + } + + unwanted_block_alert::unwanted_block_alert(const torrent_handle& h, tcp::endpoint const& ep + , peer_id const& peer_id, int block_num, int piece_num) + : peer_alert(h, ep, peer_id) + , block_index(block_num) + , piece_index(piece_num) + { + TORRENT_ASSERT(block_index >= 0 && piece_index >= 0); + } + + std::string unwanted_block_alert::message() const + { + char ret[200]; + snprintf(ret, sizeof(ret), "%s received block not in download queue (piece: %u block: %u)" + , torrent_alert::message().c_str(), piece_index, block_index); + return ret; + } + + storage_moved_alert::storage_moved_alert(torrent_handle const& h, std::string const& p) + : torrent_alert(h) + , path(p) + {} + + std::string storage_moved_alert::message() const + { + return torrent_alert::message() + " moved storage to: " + + path; + } + + storage_moved_failed_alert::storage_moved_failed_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + {} + + std::string storage_moved_failed_alert::message() const + { + return torrent_alert::message() + " storage move failed: " + + convert_from_native(error.message()); + } + + torrent_deleted_alert::torrent_deleted_alert(torrent_handle const& h, sha1_hash const& ih) + : torrent_alert(h) + { + info_hash = ih; + } + + std::string torrent_deleted_alert::message() const + { + return torrent_alert::message() + " deleted"; + } + + torrent_delete_failed_alert::torrent_delete_failed_alert(torrent_handle const& h, error_code const& e, sha1_hash const& ih) + : torrent_alert(h) + , error(e) + , info_hash(ih) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string torrent_delete_failed_alert::message() const + { + return torrent_alert::message() + " torrent deletion failed: " + + convert_from_native(error.message()); + } + + save_resume_data_failed_alert::save_resume_data_failed_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string save_resume_data_failed_alert::message() const + { + return torrent_alert::message() + " resume data was not generated: " + + convert_from_native(error.message()); + } + + torrent_paused_alert::torrent_paused_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_paused_alert::message() const + { + return torrent_alert::message() + " paused"; + } + + torrent_resumed_alert::torrent_resumed_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_resumed_alert::message() const + { + return torrent_alert::message() + " resumed"; + } + + torrent_checked_alert::torrent_checked_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_checked_alert::message() const + { + return torrent_alert::message() + " checked"; + } + + url_seed_alert::url_seed_alert( + torrent_handle const& h + , std::string const& u + , error_code const& e) + : torrent_alert(h) + , url(u) + , msg(convert_from_native(e.message())) + {} + + url_seed_alert::url_seed_alert( + torrent_handle const& h + , std::string const& u + , std::string const& m) + : torrent_alert(h) + , url(u) + , msg(m) + {} + + std::string url_seed_alert::message() const + { + return torrent_alert::message() + " url seed (" + + url + ") failed: " + msg; + } + + file_error_alert::file_error_alert( + std::string const& f + , torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , file(f) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string file_error_alert::message() const + { + return torrent_alert::message() + " file (" + file + ") error: " + + convert_from_native(error.message()); + } + + metadata_failed_alert::metadata_failed_alert(const torrent_handle& h, error_code e) + : torrent_alert(h) + , error(e) + {} + + std::string metadata_failed_alert::message() const + { + return torrent_alert::message() + " invalid metadata received"; + } + + metadata_received_alert::metadata_received_alert( + const torrent_handle& h) + : torrent_alert(h) + {} + + std::string metadata_received_alert::message() const + { + return torrent_alert::message() + " metadata successfully received"; + } + + udp_error_alert::udp_error_alert( + udp::endpoint const& ep + , error_code const& ec) + : endpoint(ep) + , error(ec) + {} + + std::string udp_error_alert::message() const + { + error_code ec; + return "UDP error: " + convert_from_native(error.message()) + " from: " + endpoint.address().to_string(ec); + } + + external_ip_alert::external_ip_alert(address const& ip) + : external_address(ip) + {} + + std::string external_ip_alert::message() const + { + error_code ec; + return "external IP received: " + external_address.to_string(ec); + } + + save_resume_data_alert::save_resume_data_alert(boost::shared_ptr const& rd + , torrent_handle const& h) + : torrent_alert(h) + , resume_data(rd) + {} + + std::string save_resume_data_alert::message() const + { + return torrent_alert::message() + " resume data generated"; + } + + listen_failed_alert::listen_failed_alert( + tcp::endpoint const& ep + , int op + , error_code const& ec + , socket_type_t t) + : endpoint(ep) + , error(ec) + , operation(op) + , sock_type(t) + {} + + std::string listen_failed_alert::message() const + { + static char const* op_str[] = + { + "parse_addr", + "open", + "bind", + "listen", + "get_peer_name", + "accept" + }; + static char const* type_str[] = + { + "TCP", "TCP/SSL", "UDP", "I2P", "Socks5" + }; + char ret[250]; + snprintf(ret, sizeof(ret), "listening on %s failed: [%s] [%s] %s" + , print_endpoint(endpoint).c_str() + , op_str[operation] + , type_str[sock_type] + , convert_from_native(error.message()).c_str()); + return ret; + } + + listen_succeeded_alert::listen_succeeded_alert(tcp::endpoint const& ep, socket_type_t t) + : endpoint(ep) + , sock_type(t) + {} + + std::string listen_succeeded_alert::message() const + { + static char const* type_str[] = + { + "TCP", "TCP/SSL", "UDP" + }; + char ret[200]; + snprintf(ret, sizeof(ret), "successfully listening on [%s] %s" + , type_str[sock_type], print_endpoint(endpoint).c_str()); + return ret; + } + + portmap_error_alert::portmap_error_alert(int i, int t, error_code const& e) + : mapping(i), map_type(t), error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string portmap_error_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + return std::string("could not map port using ") + type_str[map_type] + + ": " + convert_from_native(error.message()); + } + + portmap_alert::portmap_alert(int i, int port, int t) + : mapping(i), external_port(port), map_type(t) + {} + + std::string portmap_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + char ret[200]; + snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %u" + , type_str[map_type], external_port); + return ret; + } + + portmap_log_alert::portmap_log_alert(int t, std::string const& m) + : map_type(t), msg(m) + {} + + std::string portmap_log_alert::message() const + { + static char const* type_str[] = {"NAT-PMP", "UPnP"}; + char ret[600]; + snprintf(ret, sizeof(ret), "%s: %s", type_str[map_type], msg.c_str()); + return ret; + } + + fastresume_rejected_alert::fastresume_rejected_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string fastresume_rejected_alert::message() const + { + return torrent_alert::message() + " fast resume rejected: " + + convert_from_native(error.message()); + } + + peer_blocked_alert::peer_blocked_alert(torrent_handle const& h, address const& i + , int r) + : torrent_alert(h) + , ip(i) + , reason(r) + {} + + std::string peer_blocked_alert::message() const + { + error_code ec; + char ret[600]; + char const* reason_str[] = + { + "ip_filter", + "port_filter", + "i2p_mixed", + "privileged_ports", + "utp_disabled", + "tcp_disabled" + }; + + snprintf(ret, sizeof(ret), "%s: blocked peer: %s [%s]" + , torrent_alert::message().c_str(), ip.to_string(ec).c_str() + , reason_str[reason]); + return ret; + } + + dht_announce_alert::dht_announce_alert(address const& i, int p + , sha1_hash const& ih) + : ip(i) + , port(p) + , info_hash(ih) + {} + + std::string dht_announce_alert::message() const + { + error_code ec; + char ih_hex[41]; + to_hex((const char*)&info_hash[0], 20, ih_hex); + char msg[200]; + snprintf(msg, sizeof(msg), "incoming dht announce: %s:%u (%s)" + , ip.to_string(ec).c_str(), port, ih_hex); + return msg; + } + + dht_get_peers_alert::dht_get_peers_alert(sha1_hash const& ih) + : info_hash(ih) + {} + + std::string dht_get_peers_alert::message() const + { + char ih_hex[41]; + to_hex((const char*)&info_hash[0], 20, ih_hex); + char msg[200]; + snprintf(msg, sizeof(msg), "incoming dht get_peers: %s", ih_hex); + return msg; + } + + stats_alert::stats_alert(torrent_handle const& h, int in + , stat const& s) + : torrent_alert(h) + , interval(in) + { + for (int i = 0; i < num_channels; ++i) + transferred[i] = s[i].counter(); + } + + std::string stats_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), "%s: [%d] %d %d %d %d %d %d %d %d %d %d" + , torrent_alert::message().c_str() + , interval + , transferred[0] + , transferred[1] + , transferred[2] + , transferred[3] +#ifndef TORRENT_DISABLE_FULL_STATS + , transferred[4] + , transferred[5] + , transferred[6] + , transferred[7] + , transferred[8] + , transferred[9] +#endif + ); + return msg; + } + + cache_flushed_alert::cache_flushed_alert(torrent_handle const& h): torrent_alert(h) {} + + anonymous_mode_alert::anonymous_mode_alert(torrent_handle const& h + , int k, std::string const& s) + : torrent_alert(h) + , kind(k) + , str(s) + {} + + std::string anonymous_mode_alert::message() const + { + char msg[200]; + char const* msgs[] = { + "tracker is not anonymous, set a proxy" + }; + snprintf(msg, sizeof(msg), "%s: %s: %s" + , torrent_alert::message().c_str() + , msgs[kind], str.c_str()); + return msg; + } + + lsd_peer_alert::lsd_peer_alert(torrent_handle const& h + , tcp::endpoint const& i) + : peer_alert(h, i, peer_id(0)) + {} + + std::string lsd_peer_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), "%s: received peer from local service discovery" + , peer_alert::message().c_str()); + return msg; + } + + trackerid_alert::trackerid_alert(torrent_handle const& h + , std::string const& u + , const std::string& id) + : tracker_alert(h, u) + , trackerid(id) + {} + + std::string trackerid_alert::message() const + { + return "trackerid received: " + trackerid; + } + + dht_bootstrap_alert::dht_bootstrap_alert() + {} + + std::string dht_bootstrap_alert::message() const + { + return "DHT bootstrap complete"; + } + + rss_alert::rss_alert(feed_handle h, std::string const& u, int s, error_code const& ec) + : handle(h), url(u), state(s), error(ec) + {} + + std::string rss_alert::message() const + { + char msg[600]; + char const* state_msg[] = {"updating", "updated", "error"}; + snprintf(msg, sizeof(msg), "RSS feed %s: %s (%s)" + , url.c_str(), state_msg[state], convert_from_native(error.message()).c_str()); + return msg; + } + + torrent_error_alert::torrent_error_alert(torrent_handle const& h + , error_code const& e) + : torrent_alert(h) + , error(e) + {} + + std::string torrent_error_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), " ERROR: %s", convert_from_native(error.message()).c_str()); + return torrent_alert::message() + msg; + } + + torrent_added_alert::torrent_added_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_added_alert::message() const + { + return torrent_alert::message() + " added"; + } + + torrent_removed_alert::torrent_removed_alert(torrent_handle const& h, sha1_hash const& ih) + : torrent_alert(h) + , info_hash(ih) + {} + + std::string torrent_removed_alert::message() const + { + return torrent_alert::message() + " removed"; + } + + torrent_need_cert_alert::torrent_need_cert_alert(torrent_handle const& h) + : torrent_alert(h) + {} + + std::string torrent_need_cert_alert::message() const + { + return torrent_alert::message() + " needs SSL certificate"; + } + + static char const* type_str[] = { + "null", + "TCP", + "Socks5/TCP", + "HTTP", + "uTP", + "i2p", + "SSL/TCP", + "SSL/Socks5", + "HTTPS", + "SSL/uTP" + }; + + incoming_connection_alert::incoming_connection_alert(int t, tcp::endpoint const& i) + : socket_type(t) + , ip(i) + {} + + std::string incoming_connection_alert::message() const + { + char msg[600]; + error_code ec; + snprintf(msg, sizeof(msg), "incoming connection from %s (%s)" + , print_endpoint(ip).c_str(), type_str[socket_type]); + return msg; + } + + peer_connect_alert::peer_connect_alert(torrent_handle h, tcp::endpoint const& ep + , peer_id const& peer_id, int type) + : peer_alert(h, ep, peer_id) + , socket_type(type) + {} + + std::string peer_connect_alert::message() const + { + char msg[600]; + error_code ec; + snprintf(msg, sizeof(msg), "%s connecting to peer (%s)" + , peer_alert::message().c_str(), type_str[socket_type]); + return msg; + } + + add_torrent_alert::add_torrent_alert(torrent_handle h, add_torrent_params const& p, error_code ec) + : torrent_alert(h) + , params(p) + , error(ec) + {} + + std::string add_torrent_alert::message() const + { + char msg[600]; + char info_hash[41]; + char const* torrent_name = info_hash; + if (params.ti) torrent_name = params.ti->name().c_str(); + else if (!params.name.empty()) torrent_name = params.name.c_str(); + else if (!params.url.empty()) torrent_name = params.url.c_str(); + else to_hex((const char*)¶ms.info_hash[0], 20, info_hash); + + if (error) + { + snprintf(msg, sizeof(msg), "failed to add torrent \"%s\": [%s] %s" + , torrent_name, error.category().name() + , convert_from_native(error.message()).c_str()); + } + else + { + snprintf(msg, sizeof(msg), "added torrent: %s", torrent_name); + } + return msg; + } + + std::string state_update_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "state updates for %d torrents", int(status.size())); + return msg; + } + + torrent_update_alert::torrent_update_alert(torrent_handle h, sha1_hash const& old_hash, sha1_hash const& new_hash) + : torrent_alert(h) + , old_ih(old_hash) + , new_ih(new_hash) + {} + + std::string torrent_update_alert::message() const + { + char msg[200]; + snprintf(msg, sizeof(msg), " torrent changed info-hash from: %s to %s" + , to_hex(old_ih.to_string()).c_str() + , to_hex(new_ih.to_string()).c_str()); + return torrent_alert::message() + msg; + } + + rss_item_alert::rss_item_alert(feed_handle h, feed_item const& item) + : handle(h) + , item(item) + {} + + std::string rss_item_alert::message() const + { + char msg[500]; + snprintf(msg, sizeof(msg), "feed [%s] has new RSS item %s" + , handle.get_feed_status().title.c_str() + , item.title.empty() ? item.url.c_str() : item.title.c_str()); + return msg; + } + + peer_disconnected_alert::peer_disconnected_alert(torrent_handle const& h, tcp::endpoint const& ep + , peer_id const& peer_id, error_code const& e) + : peer_alert(h, ep, peer_id) + , error(e) + { +#ifndef TORRENT_NO_DEPRECATE + msg = convert_from_native(error.message()); +#endif + } + + std::string peer_disconnected_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "%s disconnecting: [%s] %s", peer_alert::message().c_str() + , error.category().name(), convert_from_native(error.message()).c_str()); + return msg; + } + + dht_error_alert::dht_error_alert(int op, error_code const& ec) + : error(ec), operation(op_t(op)) + {} + + std::string dht_error_alert::message() const + { + const static char* const operation_names[] = + { + "unknown", + "hostname lookup" + }; + + int op = operation; + if (op < 0 || op >= int(sizeof(operation_names)/sizeof(operation_names[0]))) + op = 0; + + char msg[600]; + snprintf(msg, sizeof(msg), "DHT error [%s] (%d) %s" + , operation_names[op] + , error.value() + , convert_from_native(error.message()).c_str()); + return msg; + } + + dht_immutable_item_alert::dht_immutable_item_alert(sha1_hash const& t, entry const& i) + : target(t), item(i) + {} + + std::string dht_immutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT immutable item %s [ %s ]" + , to_hex(target.to_string()).c_str() + , item.to_string().c_str()); + return msg; + } + + dht_mutable_item_alert::dht_mutable_item_alert(boost::array k + , boost::array sig + , boost::uint64_t sequence + , std::string const& s + , entry const& i) + : key(k), signature(sig), seq(sequence), salt(s), item(i) + {} + + std::string dht_mutable_item_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT mutable item (key=%s salt=%s seq=%" PRId64 ") [ %s ]" + , to_hex(std::string(&key[0], 32)).c_str() + , salt.c_str() + , seq + , item.to_string().c_str()); + return msg; + } + + dht_put_alert::dht_put_alert(sha1_hash const& t) + : target(t) + , seq(0) + {} + + dht_put_alert::dht_put_alert(boost::array key + , boost::array sig + , std::string s + , boost::uint64_t sequence_number) + : target(0) + , public_key(key) + , signature(sig) + , salt(s) + , seq(sequence_number) + {} + + std::string dht_put_alert::message() const + { + char msg[1050]; + snprintf(msg, sizeof(msg), "DHT put complete (key=%s sig=%s salt=%s seq=%" PRId64 ")" + , to_hex(std::string(&public_key[0], 32)).c_str() + , to_hex(std::string(&signature[0], 64)).c_str() + , salt.c_str() + , seq); + return msg; + } + + i2p_alert::i2p_alert(error_code const& ec) + : error(ec) + {} + + std::string i2p_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "i2p_error: [%s] %s" + , error.category().name(), convert_from_native(error.message()).c_str()); + return msg; + } + +} // namespace libtorrent + diff --git a/apps/Launcher/ext/libtorrent/src/alert_manager.cpp b/apps/Launcher/ext/libtorrent/src/alert_manager.cpp new file mode 100644 index 0000000000..4f6dd60050 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/alert_manager.cpp @@ -0,0 +1,196 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/alert_manager.hpp" +#include "libtorrent/alert_types.hpp" + +#ifndef TORRENT_DISABLE_EXTENSIONS +#include "libtorrent/extensions.hpp" +#endif + +namespace libtorrent +{ + + alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask) + : m_alert_mask(alert_mask) + , m_queue_size_limit(queue_limit) + {} + + alert_manager::~alert_manager() + { + while (!m_alerts.empty()) + { + TORRENT_ASSERT(alert_cast(m_alerts.front()) == 0 + && "shutting down session with remaining resume data alerts in the alert queue. " + "You proabably wany to make sure you always wait for all resume data " + "alerts before shutting down"); + delete m_alerts.front(); + m_alerts.pop_front(); + } + } + + alert const* alert_manager::wait_for_alert(time_duration max_wait) + { + mutex::scoped_lock lock(m_mutex); + + if (!m_alerts.empty()) return m_alerts.front(); + + // this call can be interrupted prematurely by other signals + m_condition.wait_for(lock, max_wait); + if (!m_alerts.empty()) return m_alerts.front(); + + return NULL; + } + + void alert_manager::set_dispatch_function(boost::function)> const& fun) + { + mutex::scoped_lock lock(m_mutex); + + m_dispatch = fun; + + std::deque alerts; + m_alerts.swap(alerts); + lock.unlock(); + + while (!alerts.empty()) + { + TORRENT_TRY { + m_dispatch(std::auto_ptr(alerts.front())); + } TORRENT_CATCH(std::exception&) {} + alerts.pop_front(); + } + } + + void dispatch_alert(boost::function dispatcher + , alert* alert_) + { + std::auto_ptr holder(alert_); + dispatcher(*alert_); + } + + void alert_manager::post_alert_ptr(alert* alert_) + { + std::auto_ptr a(alert_); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_alert(alert_); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + mutex::scoped_lock lock(m_mutex); + post_impl(a, lock); + } + + void alert_manager::post_alert(const alert& alert_) + { + std::auto_ptr a(alert_.clone()); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_alert(&alert_); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + mutex::scoped_lock lock(m_mutex); + post_impl(a, lock); + } + + void alert_manager::post_impl(std::auto_ptr& alert_, mutex::scoped_lock& l) + { + if (m_dispatch) + { + TORRENT_ASSERT(m_alerts.empty()); + TORRENT_TRY { + m_dispatch(alert_); + } TORRENT_CATCH(std::exception&) {} + } + else if (m_alerts.size() < m_queue_size_limit || !alert_->discardable()) + { + m_alerts.push_back(alert_.release()); + if (m_alerts.size() == 1) + m_condition.notify_all(); + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void alert_manager::add_extension(boost::shared_ptr ext) + { + m_ses_extensions.push_back(ext); + } +#endif + + std::auto_ptr alert_manager::get() + { + mutex::scoped_lock lock(m_mutex); + + if (m_alerts.empty()) + return std::auto_ptr(0); + + alert* result = m_alerts.front(); + m_alerts.pop_front(); + return std::auto_ptr(result); + } + + void alert_manager::get_all(std::deque* alerts) + { + mutex::scoped_lock lock(m_mutex); + if (m_alerts.empty()) return; + m_alerts.swap(*alerts); + } + + bool alert_manager::pending() const + { + mutex::scoped_lock lock(m_mutex); + + return !m_alerts.empty(); + } + + size_t alert_manager::set_alert_queue_size_limit(size_t queue_size_limit_) + { + mutex::scoped_lock lock(m_mutex); + + std::swap(m_queue_size_limit, queue_size_limit_); + return queue_size_limit_; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/allocator.cpp b/apps/Launcher/ext/libtorrent/src/allocator.cpp new file mode 100644 index 0000000000..310cf0b312 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/allocator.cpp @@ -0,0 +1,201 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/allocator.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" + +#if defined TORRENT_BEOS +#include +#include // malloc/free +#elif !defined TORRENT_WINDOWS +#include // valloc/free +#include // _SC_PAGESIZE +#endif + +#if TORRENT_USE_MEMALIGN || TORRENT_USE_POSIX_MEMALIGN || defined TORRENT_WINDOWS +#include // memalign and _aligned_malloc +#include // _aligned_malloc on mingw +#endif + +#ifdef TORRENT_WINDOWS +// windows.h must be included after stdlib.h under mingw +#include +#endif + +#ifdef TORRENT_MINGW +#define _aligned_malloc __mingw_aligned_malloc +#define _aligned_free __mingw_aligned_free +#endif + +#ifdef TORRENT_DEBUG_BUFFERS +#ifndef TORRENT_WINDOWS +#include +#endif +#include "libtorrent/size_type.hpp" + +struct alloc_header +{ + libtorrent::size_type size; + int magic; + char stack[3072]; +}; + +#endif + +namespace libtorrent +{ + + int page_size() + { + static int s = 0; + if (s != 0) return s; + +#ifdef TORRENT_WINDOWS + SYSTEM_INFO si; + GetSystemInfo(&si); + s = si.dwPageSize; +#elif defined TORRENT_BEOS + s = B_PAGE_SIZE; +#else + s = sysconf(_SC_PAGESIZE); +#endif + // assume the page size is 4 kiB if we + // fail to query it + if (s <= 0) s = 4096; + return s; + } + + char* page_aligned_allocator::malloc(size_type bytes) + { + TORRENT_ASSERT(bytes > 0); + // just sanity check (this needs to be pretty high + // for cases where the cache size is several gigabytes) + TORRENT_ASSERT(bytes < 0x30000000); + + TORRENT_ASSERT(int(bytes) >= page_size()); +#ifdef TORRENT_DEBUG_BUFFERS + int page = page_size(); + int num_pages = (bytes + (page-1)) / page + 2; + int orig_bytes = bytes; + bytes = num_pages * page; +#endif + + char* ret; +#if TORRENT_USE_POSIX_MEMALIGN + if (posix_memalign((void**)&ret, page_size(), bytes) != 0) ret = NULL; +#elif TORRENT_USE_MEMALIGN + ret = (char*)memalign(page_size(), bytes); +#elif defined TORRENT_WINDOWS + ret = (char*)_aligned_malloc(bytes, page_size()); +#elif defined TORRENT_BEOS + area_id id = create_area("", &ret, B_ANY_ADDRESS + , (bytes + page_size() - 1) & (page_size()-1), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (id < B_OK) return NULL; + ret = (char*)ret; +#else + ret = (char*)valloc(bytes); +#endif + if (ret == NULL) return NULL; + +#ifdef TORRENT_DEBUG_BUFFERS + // make the two surrounding pages non-readable and -writable + alloc_header* h = (alloc_header*)ret; + h->size = orig_bytes; + h->magic = 0x1337; + print_backtrace(h->stack, sizeof(h->stack)); + +#ifdef TORRENT_WINDOWS +#define mprotect(buf, size, prot) VirtualProtect(buf, size, prot, NULL) +#define PROT_READ PAGE_READONLY +#endif + mprotect(ret, page, PROT_READ); + mprotect(ret + (num_pages-1) * page, page, PROT_READ); + +#ifdef TORRENT_WINDOWS +#undef mprotect +#undef PROT_READ +#endif +// fprintf(stderr, "malloc: %p head: %p tail: %p size: %d\n", ret + page, ret, ret + page + bytes, int(bytes)); + + return ret + page; +#endif // TORRENT_DEBUG_BUFFERS + + return ret; + } + + void page_aligned_allocator::free(char* block) + { + if (block == 0) return; + +#ifdef TORRENT_DEBUG_BUFFERS + +#ifdef TORRENT_WINDOWS +#define mprotect(buf, size, prot) VirtualProtect(buf, size, prot, NULL) +#define PROT_READ PAGE_READONLY +#define PROT_WRITE PAGE_READWRITE +#endif + int page = page_size(); + // make the two surrounding pages non-readable and -writable + mprotect(block - page, page, PROT_READ | PROT_WRITE); + alloc_header* h = (alloc_header*)(block - page); + int num_pages = (h->size + (page-1)) / page + 2; + TORRENT_ASSERT(h->magic == 0x1337); + mprotect(block + (num_pages-2) * page, page, PROT_READ | PROT_WRITE); +// fprintf(stderr, "free: %p head: %p tail: %p size: %d\n", block, block - page, block + h->size, int(h->size)); + h->magic = 0; + block -= page; + +#ifdef TORRENT_WINDOWS +#undef mprotect +#undef PROT_READ +#undef PROT_WRITE +#endif + + print_backtrace(h->stack, sizeof(h->stack)); + +#endif // TORRENT_DEBUG_BUFFERS + +#ifdef TORRENT_WINDOWS + _aligned_free(block); +#elif defined TORRENT_BEOS + area_id id = area_for(block); + if (id < B_OK) return; + delete_area(id); +#else + ::free(block); +#endif // TORRENT_WINDOWS + } + + +} + diff --git a/apps/Launcher/ext/libtorrent/src/asio.cpp b/apps/Launcher/ext/libtorrent/src/asio.cpp new file mode 100644 index 0000000000..bde4c1ff33 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/asio.cpp @@ -0,0 +1,23 @@ +// builds all boost.asio source as a separate compilation unit +#include + +#ifndef BOOST_ASIO_SOURCE +#define BOOST_ASIO_SOURCE +#endif + +#include "libtorrent/config.hpp" + +#define TORRENT_HAS_ASIO_DECL x ## BOOST_ASIO_DECL + +// only define BOOST_ASIO_DECL if it hasn't already been defined +// or if it has been defined to an empty string +#if TORRENT_HAS_ASIO_DECL == x +#define BOOST_ASIO_DECL BOOST_SYMBOL_EXPORT +#endif + +#if BOOST_VERSION >= 104500 +#include +#elif BOOST_VERSION >= 104400 +#include +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp b/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp new file mode 100644 index 0000000000..f762048e35 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/asio_ssl.cpp @@ -0,0 +1,22 @@ +// builds all boost.asio SSL source as a separate compilation unit +#include +#include + +#ifndef BOOST_ASIO_SOURCE +#define BOOST_ASIO_SOURCE +#endif + +#include "libtorrent/config.hpp" + +#define TORRENT_HAS_ASIO_DECL x ## BOOST_ASIO_DECL + +// only define BOOST_ASIO_DECL if it hasn't already been defined +// or if it has been defined to an empty string +#if TORRENT_HAS_ASIO_DECL == x +#define BOOST_ASIO_DECL BOOST_SYMBOL_EXPORT +#endif + +#if BOOST_VERSION >= 104610 +#include +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/assert.cpp b/apps/Launcher/ext/libtorrent/src/assert.cpp new file mode 100644 index 0000000000..47d996938d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/assert.cpp @@ -0,0 +1,277 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" + +#if TORRENT_PRODUCTION_ASSERTS +#include +#endif + +#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING \ + || TORRENT_RELEASE_ASSERTS || defined TORRENT_DEBUG_BUFFERS + +#ifdef __APPLE__ +#include +#endif + +#include +#include +#include + +// uClibc++ doesn't have cxxabi.h +#if defined __GNUC__ && __GNUC__ >= 3 \ + && !defined __UCLIBCXX_MAJOR__ + +#include + +std::string demangle(char const* name) +{ +// in case this string comes + // this is needed on linux + char const* start = strchr(name, '('); + if (start != 0) + { + ++start; + } + else + { + // this is needed on macos x + start = strstr(name, "0x"); + if (start != 0) + { + start = strchr(start, ' '); + if (start != 0) ++start; + else start = name; + } + else start = name; + } + + char const* end = strchr(start, '+'); + if (end) while (*(end-1) == ' ') --end; + + std::string in; + if (end == 0) in.assign(start); + else in.assign(start, end); + + size_t len; + int status; + char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status); + if (unmangled == 0) return in; + std::string ret(unmangled); + free(unmangled); + return ret; +} +#elif defined WIN32 + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // XP + +#include "windows.h" +#include "dbghelp.h" + +std::string demangle(char const* name) +{ + char demangled_name[256]; + if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0) + demangled_name[0] = 0; + return demangled_name; +} + +#else +std::string demangle(char const* name) { return name; } +#endif + +#include +#include +#include +#include "libtorrent/version.hpp" + +#if TORRENT_USE_EXECINFO +#include + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) +{ + void* stack[50]; + int size = backtrace(stack, 50); + char** symbols = backtrace_symbols(stack, size); + + for (int i = 1; i < size && len > 0; ++i) + { + int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str()); + out += ret; + len -= ret; + if (i - 1 == max_depth && max_depth > 0) break; + } + + free(symbols); +} + +// visual studio 9 and up appears to support this +#elif defined WIN32 && _MSC_VER >= 1500 + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // XP + +#include "windows.h" +#include "libtorrent/utf8.hpp" + +#include "winbase.h" +#include "dbghelp.h" + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) +{ + typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out PVOID *BackTrace, + __out_opt PULONG BackTraceHash); + + static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace = 0; + + if (RtlCaptureStackBackTrace == 0) + { + // we don't actually have to free this library, everyone has it loaded + HMODULE lib = LoadLibrary(TEXT("kernel32.dll")); + RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t)GetProcAddress(lib, "RtlCaptureStackBackTrace"); + if (RtlCaptureStackBackTrace == 0) + { + out[0] = 0; + return; + } + } + + int i; + void* stack[50]; + int size = CaptureStackBackTrace(0, 50, stack, 0); + + SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR), 1); + symbol->MaxNameLen = MAX_SYM_NAME; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + HANDLE p = GetCurrentProcess(); + static bool sym_initialized = false; + if (!sym_initialized) + { + sym_initialized = true; + SymInitialize(p, NULL, true); + } + for (i = 0; i < size && len > 0; ++i) + { + int ret; + if (SymFromAddr(p, uintptr_t(stack[i]), 0, symbol)) + ret = snprintf(out, len, "%d: %s\n", i, symbol->Name); + else + ret = snprintf(out, len, "%d: \n", i); + + out += ret; + len -= ret; + if (i == max_depth && max_depth > 0) break; + } + free(symbol); +} + +#else + +TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth) {} + +#endif + +#endif + +#if TORRENT_USE_ASSERTS || defined TORRENT_ASIO_DEBUGGING + +#if TORRENT_PRODUCTION_ASSERTS +char const* libtorrent_assert_log = "asserts.log"; +// the number of asserts we've printed to the log +boost::detail::atomic_count assert_counter(0); +#endif + +TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file + , char const* function, char const* value, int kind) +{ +#if TORRENT_PRODUCTION_ASSERTS + // no need to flood the assert log with infinite number of asserts + if (++assert_counter > 500) return; + + FILE* out = fopen(libtorrent_assert_log, "a+"); + if (out == 0) out = stderr; +#else + FILE* out = stderr; +#endif + + char stack[8192]; + stack[0] = '\0'; + print_backtrace(stack, sizeof(stack), 0); + + char const* message = "assertion failed. Please file a bugreport at " + "http://code.google.com/p/libtorrent/issues\n" + "Please include the following information:\n\n" + "version: " LIBTORRENT_VERSION "\n" + LIBTORRENT_REVISION "\n"; + + switch (kind) + { + case 1: + message = "A precondition of a libtorrent function has been violated.\n" + "This indicates a bug in the client application using libtorrent\n"; + } + + fprintf(out, "%s\n" + "file: '%s'\n" + "line: %d\n" + "function: %s\n" + "expression: %s\n" + "%s%s\n" + "stack:\n" + "%s\n" + , message, file, line, function, expr + , value ? value : "", value ? "\n" : "" + , stack); + + // if production asserts are defined, don't abort, just print the error +#if TORRENT_PRODUCTION_ASSERTS + if (out != stderr) fclose(out); +#else + // send SIGINT to the current process + // to break into the debugger + raise(SIGINT); + abort(); +#endif +} + +#else + +TORRENT_EXPORT void assert_fail(char const* expr, int line, char const* file + , char const* function, char const* value, int kind) {} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp new file mode 100644 index 0000000000..155251fa97 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_limit.cpp @@ -0,0 +1,94 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bandwidth_limit.hpp" +#include + +namespace libtorrent +{ + bandwidth_channel::bandwidth_channel() + : tmp(0) + , distribute_quota(0) + , m_quota_left(0) + , m_limit(0) + {} + + // 0 means infinite + void bandwidth_channel::throttle(int limit) + { + TORRENT_ASSERT(limit >= 0); + // if the throttle is more than this, we might overflow + TORRENT_ASSERT(limit < INT_MAX); + m_limit = limit; + } + + int bandwidth_channel::quota_left() const + { + if (m_limit == 0) return inf; + return (std::max)(int(m_quota_left), 0); + } + + void bandwidth_channel::update_quota(int dt_milliseconds) + { + if (m_limit == 0) return; + m_quota_left += (m_limit * dt_milliseconds + 500) / 1000; + if (m_quota_left > m_limit * 3) m_quota_left = m_limit * 3; + distribute_quota = int((std::max)(m_quota_left, boost::int64_t(0))); +// fprintf(stderr, "%p: [%d]: + %"PRId64" limit: %"PRId64" quota_left: %"PRId64"\n", this +// , dt_milliseconds, (m_limit * dt_milliseconds + 500) / 1000, m_limit +// , m_quota_left); + } + + // this is used when connections disconnect with + // some quota left. It's returned to its bandwidth + // channels. + void bandwidth_channel::return_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + if (m_limit == 0) return; + TORRENT_ASSERT(m_quota_left <= m_quota_left + amount); + m_quota_left += amount; + } + + void bandwidth_channel::use_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + TORRENT_ASSERT(m_limit >= 0); + if (m_limit == 0) return; + +// fprintf(stderr, "%p: - %"PRId64" limit: %"PRId64" quota_left: %"PRId64"\n", this +// , amount, m_limit, m_quota_left); + m_quota_left -= amount; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp new file mode 100644 index 0000000000..8ad2a7e3cd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_manager.cpp @@ -0,0 +1,241 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + + bandwidth_manager::bandwidth_manager(int channel +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , bool log +#endif + ) + : m_queued_bytes(0) + , m_channel(channel) + , m_abort(false) + { +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + if (log) + m_log.open("bandwidth_limiter.log", std::ios::trunc); + m_start = time_now(); +#endif + } + + void bandwidth_manager::close() + { + m_abort = true; + + queue_t tm; + tm.swap(m_queue); + m_queued_bytes = 0; + + while (!tm.empty()) + { + bw_request& bwr = tm.back(); + bwr.peer->assign_bandwidth(m_channel, bwr.assigned); + tm.pop_back(); + } + } + +#if TORRENT_USE_ASSERTS + bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const + { + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (i->peer.get() == peer) return true; + } + return false; + } +#endif + + int bandwidth_manager::queue_size() const + { + return m_queue.size(); + } + + boost::int64_t bandwidth_manager::queued_bytes() const + { + return m_queued_bytes; + } + + // non prioritized means that, if there's a line for bandwidth, + // others will cut in front of the non-prioritized peers. + // this is used by web seeds + int bandwidth_manager::request_bandwidth(boost::intrusive_ptr const& peer + , int blk, int priority + , bandwidth_channel* chan1 + , bandwidth_channel* chan2 + , bandwidth_channel* chan3 + , bandwidth_channel* chan4 + , bandwidth_channel* chan5 + ) + { + INVARIANT_CHECK; + if (m_abort) return 0; + + TORRENT_ASSERT(blk > 0); + TORRENT_ASSERT(priority > 0); + TORRENT_ASSERT(!is_queued(peer.get())); + + bw_request bwr(peer, blk, priority); + int i = 0; + if (chan1 && chan1->throttle() > 0 && chan1->need_queueing(blk)) + bwr.channel[i++] = chan1; + if (chan2 && chan2->throttle() > 0 && chan2->need_queueing(blk)) + bwr.channel[i++] = chan2; + if (chan3 && chan3->throttle() > 0 && chan3->need_queueing(blk)) + bwr.channel[i++] = chan3; + if (chan4 && chan4->throttle() > 0 && chan4->need_queueing(blk)) + bwr.channel[i++] = chan4; + if (chan5 && chan5->throttle() > 0 && chan5->need_queueing(blk)) + bwr.channel[i++] = chan5; + + if (i == 0) + { + // the connection is not rate limited by any of its + // bandwidth channels, or it doesn't belong to any + // channels. There's no point in adding it to + // the queue, just satisfy the request immediately + return blk; + } + + m_queued_bytes += blk; + m_queue.push_back(bwr); + return 0; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void bandwidth_manager::check_invariant() const + { + boost::int64_t queued = 0; + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + queued += i->request_size - i->assigned; + } + TORRENT_ASSERT(queued == m_queued_bytes); + } +#endif + + void bandwidth_manager::update_quotas(time_duration const& dt) + { + if (m_abort) return; + if (m_queue.empty()) return; + + INVARIANT_CHECK; + + boost::int64_t dt_milliseconds = total_milliseconds(dt); + if (dt_milliseconds > 3000) dt_milliseconds = 3000; + + // for each bandwidth channel, call update_quota(dt) + + std::vector channels; + + queue_t tm; + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + if (i->peer->is_disconnecting()) + { + m_queued_bytes -= i->request_size - i->assigned; + + // return all assigned quota to all the + // bandwidth channels this peer belongs to + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->return_quota(i->assigned); + } + + i->assigned = 0; + tm.push_back(*i); + i = m_queue.erase(i); + continue; + } + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->tmp = 0; + } + ++i; + } + + for (queue_t::iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + if (bwc->tmp == 0) channels.push_back(bwc); + TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority); + bwc->tmp += i->priority; + } + } + + for (std::vector::iterator i = channels.begin() + , end(channels.end()); i != end; ++i) + { + (*i)->update_quota(int(dt_milliseconds)); + } + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + int a = i->assign_bandwidth(); + if (i->assigned == i->request_size + || (i->ttl <= 0 && i->assigned > 0)) + { + a += i->request_size - i->assigned; + TORRENT_ASSERT(i->assigned <= i->request_size); + tm.push_back(*i); + i = m_queue.erase(i); + } + else + { + ++i; + } + m_queued_bytes -= a; + } + + while (!tm.empty()) + { + bw_request& bwr = tm.back(); + bwr.peer->assign_bandwidth(m_channel, bwr.assigned); + tm.pop_back(); + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp b/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp new file mode 100644 index 0000000000..0be7e3c0fa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bandwidth_queue_entry.cpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "libtorrent/bandwidth_queue_entry.hpp" +#include +#include + +namespace libtorrent +{ + bw_request::bw_request(boost::intrusive_ptr const& pe + , int blk, int prio) + : peer(pe) + , priority(prio) + , assigned(0) + , request_size(blk) + , ttl(20) + { + TORRENT_ASSERT(priority > 0); + std::memset(channel, 0, sizeof(channel)); + } + + int bw_request::assign_bandwidth() + { + TORRENT_ASSERT(assigned < request_size); + int quota = request_size - assigned; + TORRENT_ASSERT(quota >= 0); + --ttl; + if (quota == 0) return quota; + + for (int j = 0; j < 5 && channel[j]; ++j) + { + if (channel[j]->throttle() == 0) continue; + if (channel[j]->tmp == 0) continue; + quota = (std::min)(int(boost::int64_t(channel[j]->distribute_quota) + * priority / channel[j]->tmp), quota); + } + assigned += quota; + for (int j = 0; j < 5 && channel[j]; ++j) + channel[j]->use_quota(quota); + TORRENT_ASSERT(assigned <= request_size); + return quota; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp b/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp new file mode 100644 index 0000000000..72c4317f46 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bloom_filter.cpp @@ -0,0 +1,76 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bloom_filter.hpp" + +namespace libtorrent +{ + bool has_bits(boost::uint8_t const* k, boost::uint8_t const* bits, int len) + { + boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); + boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); + idx1 %= len * 8; + idx2 %= len * 8; + return (bits[idx1/8] & (1 << (idx1 & 7))) != 0 + && (bits[idx2/8] & (1 << (idx2 & 7))) != 0; + } + + void set_bits(boost::uint8_t const* k, boost::uint8_t* bits, int len) + { + boost::uint32_t idx1 = boost::uint32_t(k[0]) | (boost::uint32_t(k[1]) << 8); + boost::uint32_t idx2 = boost::uint32_t(k[2]) | (boost::uint32_t(k[3]) << 8); + idx1 %= len * 8; + idx2 %= len * 8; + bits[idx1/8] |= (1 << (idx1 & 7)); + bits[idx2/8] |= (1 << (idx2 & 7)); + } + + int count_zero_bits(boost::uint8_t const* bits, int len) + { + // number of bits _not_ set in a nibble + boost::uint8_t bitcount[16] = + { + // 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, + // 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111 + 4, 3, 3, 2, 3, 2, 2, 1, + 3, 2, 2, 1, 2, 1, 1, 0 + }; + int ret = 0; + for (int i = 0; i < len; ++i) + { + ret += bitcount[bits[i] & 0xf]; + ret += bitcount[(bits[i] >> 4) & 0xf]; + } + return ret; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp b/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp new file mode 100644 index 0000000000..e44dcb722e --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/broadcast_socket.cpp @@ -0,0 +1,450 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#if defined TORRENT_OS2 +#include +#endif + +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/assert.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#ifdef TORRENT_DEBUG +#include "libtorrent/socket_io.hpp" +#endif + +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif + +#ifdef TORRENT_WINDOWS +#include // for if_nametoindex +#endif + +namespace libtorrent +{ + bool is_local(address const& a) + { + TORRENT_TRY { +#if TORRENT_USE_IPV6 + if (a.is_v6()) + { + return a.to_v6().is_loopback() + || a.to_v6().is_link_local() + || a.to_v6().is_multicast_link_local(); + } +#endif + address_v4 a4 = a.to_v4(); + unsigned long ip = a4.to_ulong(); + return ((ip & 0xff000000) == 0x0a000000 // 10.x.x.x + || (ip & 0xfff00000) == 0xac100000 // 172.16.x.x + || (ip & 0xffff0000) == 0xc0a80000 // 192.168.x.x + || (ip & 0xffff0000) == 0xa9fe0000 // 169.254.x.x + || (ip & 0xff000000) == 0x7f000000); // 127.x.x.x + } TORRENT_CATCH(std::exception&) { return false; } + } + + bool is_loopback(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (addr.is_v4()) + return addr.to_v4() == address_v4::loopback(); + else + return addr.to_v6() == address_v6::loopback(); + } TORRENT_CATCH(std::exception&) { return false; } +#else + return addr.to_v4() == address_v4::loopback(); +#endif + } + + bool is_multicast(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (addr.is_v4()) + return addr.to_v4().is_multicast(); + else + return addr.to_v6().is_multicast(); + } TORRENT_CATCH(std::exception&) { return false; } +#else + return addr.to_v4().is_multicast(); +#endif + } + + bool is_any(address const& addr) + { + TORRENT_TRY { +#if TORRENT_USE_IPV6 + if (addr.is_v4()) + return addr.to_v4() == address_v4::any(); + else if (addr.to_v6().is_v4_mapped()) + return (addr.to_v6().to_v4() == address_v4::any()); + else + return addr.to_v6() == address_v6::any(); +#else + return addr.to_v4() == address_v4::any(); +#endif + } TORRENT_CATCH(std::exception&) { return false; } + } + + bool is_teredo(address const& addr) + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + if (!addr.is_v6()) return false; + boost::uint8_t teredo_prefix[] = {0x20, 0x01, 0, 0}; + address_v6::bytes_type b = addr.to_v6().to_bytes(); + return memcmp(&b[0], teredo_prefix, 4) == 0; + } TORRENT_CATCH(std::exception&) { return false; } +#else + return false; +#endif + } + + bool supports_ipv6() + { +#if TORRENT_USE_IPV6 + TORRENT_TRY { + error_code ec; + address::from_string("::1", ec); + return !ec; + } TORRENT_CATCH(std::exception&) { return false; } +#else + return false; +#endif + } + + address guess_local_address(io_service& ios) + { + // make a best guess of the interface we're using and its IP + error_code ec; + std::vector const& interfaces = enum_net_interfaces(ios, ec); + address ret = address_v4::any(); + for (std::vector::const_iterator i = interfaces.begin() + , end(interfaces.end()); i != end; ++i) + { + address const& a = i->interface_address; + if (is_loopback(a) + || is_multicast(a) + || is_any(a)) continue; + + // prefer a v4 address, but return a v6 if + // there are no v4 + if (a.is_v4()) return a; + + if (ret != address_v4::any()) + ret = a; + } + return ret; + } + + // count the length of the common bit prefix + int common_bits(unsigned char const* b1 + , unsigned char const* b2, int n) + { + for (int i = 0; i < n; ++i, ++b1, ++b2) + { + unsigned char a = *b1 ^ *b2; + if (a == 0) continue; + int ret = i * 8 + 8; + for (; a > 0; a >>= 1) --ret; + return ret; + } + return n * 8; + } + + // returns the number of bits in that differ from the right + // between the addresses. The larger number, the further apart + // the IPs are + int cidr_distance(address const& a1, address const& a2) + { +#if TORRENT_USE_IPV6 + if (a1.is_v4() && a2.is_v4()) + { +#endif + // both are v4 + address_v4::bytes_type b1 = a1.to_v4().to_bytes(); + address_v4::bytes_type b2 = a2.to_v4().to_bytes(); + return address_v4::bytes_type().size() * 8 + - common_bits(b1.data(), b2.data(), b1.size()); +#if TORRENT_USE_IPV6 + } + + address_v6::bytes_type b1; + address_v6::bytes_type b2; + if (a1.is_v4()) b1 = address_v6::v4_mapped(a1.to_v4()).to_bytes(); + else b1 = a1.to_v6().to_bytes(); + if (a2.is_v4()) b2 = address_v6::v4_mapped(a2.to_v4()).to_bytes(); + else b2 = a2.to_v6().to_bytes(); + return address_v6::bytes_type().size() * 8 + - common_bits(b1.data(), b2.data(), b1.size()); +#endif + } + + broadcast_socket::broadcast_socket( + udp::endpoint const& multicast_endpoint + , receive_handler_t const& handler) + : m_multicast_endpoint(multicast_endpoint) + , m_on_receive(handler) + , m_outstanding_operations(0) + , m_abort(false) + { + TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address())); + + using namespace asio::ip::multicast; + } + + void broadcast_socket::open(io_service& ios, error_code& ec, bool loopback) + { + std::vector interfaces = enum_net_interfaces(ios, ec); + +#if TORRENT_USE_IPV6 + if (m_multicast_endpoint.address().is_v6()) + open_multicast_socket(ios, address_v6::any(), loopback, ec); + else +#endif + open_multicast_socket(ios, address_v4::any(), loopback, ec); + + for (std::vector::const_iterator i = interfaces.begin() + , end(interfaces.end()); i != end; ++i) + { + // only multicast on compatible networks + if (i->interface_address.is_v4() != m_multicast_endpoint.address().is_v4()) continue; + // ignore any loopback interface + if (!loopback && is_loopback(i->interface_address)) continue; + + ec = error_code(); + + // if_nametoindex was introduced in vista +#if TORRENT_USE_IPV6 \ + && (!defined TORRENT_WINDOWS || _WIN32_WINNT >= 0x0600) \ + && !defined TORRENT_MINGW + + if (i->interface_address.is_v6() && + i->interface_address.to_v6().is_link_local()) { + address_v6 addr6 = i->interface_address.to_v6(); + addr6.scope_id(if_nametoindex(i->name)); + open_multicast_socket(ios, addr6, loopback, ec); + + address_v4 const& mask = i->netmask.is_v4() ? + i->netmask.to_v4() : address_v4(); + open_unicast_socket(ios, addr6, mask); + continue; + } + +#endif + open_multicast_socket(ios, i->interface_address, loopback, ec); +#ifdef TORRENT_DEBUG +// fprintf(stderr, "broadcast socket [ if: %s group: %s mask: %s ] %s\n" +// , i->interface_address.to_string().c_str() +// , m_multicast_endpoint.address().to_string().c_str() +// , i->netmask.to_string().c_str() +// , ec.message().c_str()); +#endif + open_unicast_socket(ios, i->interface_address + , i->netmask.is_v4() ? i->netmask.to_v4() : address_v4()); + } + } + + void broadcast_socket::open_multicast_socket(io_service& ios + , address const& addr, bool loopback, error_code& ec) + { + using namespace asio::ip::multicast; + + boost::shared_ptr s(new datagram_socket(ios)); + s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); + if (ec) return; + s->set_option(datagram_socket::reuse_address(true), ec); + if (ec) return; + s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec); + if (ec) return; + s->set_option(join_group(m_multicast_endpoint.address()), ec); + if (ec) return; + s->set_option(hops(255), ec); + if (ec) return; + s->set_option(enable_loopback(loopback), ec); + if (ec) return; + m_sockets.push_back(socket_entry(s)); + socket_entry& se = m_sockets.back(); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) + , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; + } + + void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr + , address_v4 const& mask) + { + using namespace asio::ip::multicast; + error_code ec; + boost::shared_ptr s(new datagram_socket(ios)); + s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec); + if (ec) return; + s->bind(udp::endpoint(addr, 0), ec); + if (ec) return; + + m_unicast_sockets.push_back(socket_entry(s, mask)); + socket_entry& se = m_unicast_sockets.back(); + + // allow sending broadcast messages + asio::socket_base::broadcast option(true); + s->set_option(option, ec); + if (!ec) se.broadcast = true; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) + , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; + } + + void broadcast_socket::send(char const* buffer, int size, error_code& ec, int flags) + { + for (std::list::iterator i = m_unicast_sockets.begin() + , end(m_unicast_sockets.end()); i != end; ++i) + { + if (!i->socket) continue; + error_code e; + i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e); + + // if the user specified the broadcast flag, send one to the broadcast + // address as well + if ((flags & broadcast_socket::broadcast) && i->can_broadcast()) + i->socket->send_to(asio::buffer(buffer, size) + , udp::endpoint(i->broadcast_address(), m_multicast_endpoint.port()), 0, e); + +#ifdef TORRENT_DEBUG +// fprintf(stderr, " sending on unicast %s to: %s\n", print_address(i->socket->local_endpoint().address()).c_str() +// , print_endpoint(m_multicast_endpoint).c_str()); +#endif + if (e) + { +#ifdef TORRENT_DEBUG +// fprintf(stderr, " ERROR: %s\n", e.message().c_str()); +#endif + i->socket->close(e); + i->socket.reset(); + } + } + + for (std::list::iterator i = m_sockets.begin() + , end(m_sockets.end()); i != end; ++i) + { + if (!i->socket) continue; + error_code e; + i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e); +#ifdef TORRENT_DEBUG +// extern std::string print_address(address const& addr); +// extern std::string print_endpoint(udp::endpoint const& ep); +// fprintf(stderr, " sending on multicast %s to: %s\n", print_address(i->socket->local_endpoint().address()).c_str() +// , print_endpoint(m_multicast_endpoint).c_str()); +#endif + if (e) + { +#ifdef TORRENT_DEBUG +// fprintf(stderr, " ERROR: %s\n", e.message().c_str()); +#endif + i->socket->close(e); + i->socket.reset(); + } + } + } + + void broadcast_socket::on_receive(socket_entry* s, error_code const& ec + , std::size_t bytes_transferred) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("broadcast_socket::on_receive"); +#endif + TORRENT_ASSERT(m_outstanding_operations > 0); + --m_outstanding_operations; + + if (ec || bytes_transferred == 0 || !m_on_receive) + { + maybe_abort(); + return; + } + m_on_receive(s->remote, s->buffer, bytes_transferred); + + if (maybe_abort()) return; + if (!s->socket) return; +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("broadcast_socket::on_receive"); +#endif + s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer)) + , s->remote, boost::bind(&broadcast_socket::on_receive, this, s, _1, _2)); + ++m_outstanding_operations; + } + + bool broadcast_socket::maybe_abort() + { + bool ret = m_abort; + if (m_abort && m_outstanding_operations == 0) + { + // it's important that m_on_receive is cleared + // before the object is destructed, since it may + // hold a reference to ourself, which would otherwise + // cause an infinite recursion destructing the objects + receive_handler_t().swap(m_on_receive); + } + return ret; + } + + void broadcast_socket::close() + { + std::for_each(m_sockets.begin(), m_sockets.end(), boost::bind(&socket_entry::close, _1)); + std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), boost::bind(&socket_entry::close, _1)); + + m_abort = true; + maybe_abort(); + } +} + + diff --git a/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp new file mode 100644 index 0000000000..2d0befd259 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/bt_peer_connection.cpp @@ -0,0 +1,3408 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +Copyright (c) 2007-2014, Arvid Norberg, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#ifdef TORRENT_USE_OPENSSL +#include // autp_ptr +#endif + +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/alloca.hpp" + +#ifndef TORRENT_DISABLE_ENCRYPTION +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#endif + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + const bt_peer_connection::message_handler + bt_peer_connection::m_message_handler[] = + { + &bt_peer_connection::on_choke, + &bt_peer_connection::on_unchoke, + &bt_peer_connection::on_interested, + &bt_peer_connection::on_not_interested, + &bt_peer_connection::on_have, + &bt_peer_connection::on_bitfield, + &bt_peer_connection::on_request, + &bt_peer_connection::on_piece, + &bt_peer_connection::on_cancel, + &bt_peer_connection::on_dht_port, + 0, 0, 0, + // FAST extension messages + &bt_peer_connection::on_suggest_piece, + &bt_peer_connection::on_have_all, + &bt_peer_connection::on_have_none, + &bt_peer_connection::on_reject_request, + &bt_peer_connection::on_allowed_fast, +#ifndef TORRENT_DISABLE_EXTENSIONS + 0, 0, + &bt_peer_connection::on_extended +#endif + }; + + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo + , peer_id const& pid + , boost::weak_ptr tor + , bool outgoing) + : peer_connection(ses, tor, s, remote + , peerinfo, outgoing) + , m_state(read_protocol_identifier) + , m_supports_extensions(false) + , m_supports_dht_port(false) + , m_supports_fast(false) +#if TORRENT_USE_ASSERTS + , m_sent_bitfield(false) + , m_in_constructor(true) + , m_sent_handshake(false) +#endif +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_upload_only_id(0) + , m_holepunch_id(0) +#endif + , m_our_peer_id(pid) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_sync_bytes_read(0) +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_dont_have_id(0) + , m_share_mode_id(0) +#endif + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** bt_peer_connection"); +#endif + +#if TORRENT_USE_ASSERTS + m_in_constructor = false; +#endif +#ifndef TORRENT_DISABLE_EXTENSIONS + memset(m_reserved_bits, 0, sizeof(m_reserved_bits)); +#endif + } + + void bt_peer_connection::start() + { + peer_connection::start(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + + bt_peer_connection::~bt_peer_connection() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + } + + void bt_peer_connection::on_connected() + { + if (is_disconnecting()) return; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (t->graceful_pause()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ON_CONNECTED [ graceful-paused ]"); +#endif + disconnect(error_code(errors::torrent_paused), 0); + return; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + boost::uint8_t out_enc_policy = m_ses.get_pe_settings().out_enc_policy; + +#ifdef TORRENT_USE_OPENSSL + // never try an encrypted connection when already using SSL + if (is_ssl(*get_socket())) + out_enc_policy = pe_settings::disabled; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + char const* policy_name[] = {"forced", "enabled", "disabled"}; + peer_log("*** outgoing encryption policy: %s", policy_name[out_enc_policy]); +#endif + + if (out_enc_policy == pe_settings::forced) + { + write_pe1_2_dhkey(); + if (is_disconnecting()) return; + + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else if (out_enc_policy == pe_settings::enabled) + { + TORRENT_ASSERT(peer_info_struct()); + + policy::peer* pi = peer_info_struct(); + if (pi->pe_support == true) + { + // toggle encryption support flag, toggled back to + // true if encrypted portion of the handshake + // completes correctly + pi->pe_support = false; + + // if this fails, we need to reconnect + // fast. + fast_reconnect(true); + + write_pe1_2_dhkey(); + if (is_disconnecting()) return; + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else // pi->pe_support == false + { + // toggled back to false if standard handshake + // completes correctly (without encryption) + pi->pe_support = true; + + write_handshake(); + reset_recv_buffer(20); + setup_receive(); + } + } + else if (out_enc_policy == pe_settings::disabled) +#endif + { + write_handshake(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + } + + void bt_peer_connection::on_metadata() + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ON_METADATA"); +#endif + // connections that are still in the handshake + // will send their bitfield when the handshake + // is done + if (m_state < read_packet_size) return; + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + write_bitfield(); +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + + void bt_peer_connection::write_dht_port(int listen_port) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> DHT_PORT [ %d ]", listen_port); +#endif + char msg[] = {0,0,0,3, msg_dht_port, 0, 0}; + char* ptr = msg + 5; + detail::write_uint16(listen_port, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have_all() + { + INVARIANT_CHECK; + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE_ALL"); +#endif + char msg[] = {0,0,0,1, msg_have_all}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have_none() + { + INVARIANT_CHECK; + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE_NONE"); +#endif + char msg[] = {0,0,0,1, msg_have_none}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_reject_request(peer_request const& r) + { + INVARIANT_CHECK; + +#ifdef TORRENT_STATS + ++m_ses.m_piece_rejects; +#endif + + if (!m_supports_fast) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[] = {0,0,0,13, msg_reject_request,0,0,0,0, 0,0,0,0, 0,0,0,0}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_allow_fast(int piece) + { + INVARIANT_CHECK; + + if (!m_supports_fast) return; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0}; + char* ptr = msg + 5; + detail::write_int32(piece, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_suggest(int piece) + { + INVARIANT_CHECK; + + if (!m_supports_fast) return; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (m_sent_suggested_pieces.empty()) + m_sent_suggested_pieces.resize(t->torrent_file().num_pieces(), false); + + if (m_sent_suggested_pieces[piece]) return; + m_sent_suggested_pieces.set_bit(piece); + + char msg[] = {0,0,0,5, msg_suggest_piece, 0, 0, 0, 0}; + char* ptr = msg + 5; + detail::write_int32(piece, ptr); + send_buffer(msg, sizeof(msg)); + } + + char random_byte() + { return random() & 0xff; } + + void bt_peer_connection::get_specific_peer_info(peer_info& p) const + { + TORRENT_ASSERT(!associated_torrent().expired()); + + if (is_interesting()) p.flags |= peer_info::interesting; + if (is_choked()) p.flags |= peer_info::choked; + if (is_peer_interested()) p.flags |= peer_info::remote_interested; + if (has_peer_choked()) p.flags |= peer_info::remote_choked; + if (support_extensions()) p.flags |= peer_info::supports_extensions; + if (is_outgoing()) p.flags |= peer_info::local_connection; +#if TORRENT_USE_I2P + if (is_i2p(*get_socket())) p.flags |= peer_info::i2p_socket; +#endif + if (is_utp(*get_socket())) p.flags |= peer_info::utp_socket; + if (is_ssl(*get_socket())) p.flags |= peer_info::ssl_socket; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted) + { + p.flags |= m_rc4_encrypted + ? peer_info::rc4_encrypted + : peer_info::plaintext_encrypted; + } +#endif + + if (!is_connecting() && in_handshake()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_client_version; + p.connection_type = peer_info::standard_bittorrent; + } + + bool bt_peer_connection::in_handshake() const + { + return m_state < read_packet_size; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::write_pe1_2_dhkey() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!m_dh_key_exchange.get()); + TORRENT_ASSERT(!m_sent_handshake); + +#ifdef TORRENT_VERBOSE_LOGGING + if (is_outgoing()) + peer_log("*** initiating encrypted handshake"); +#endif + + m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange); + if (!m_dh_key_exchange || !m_dh_key_exchange->good()) + { + disconnect(errors::no_memory); + return; + } + + int pad_size = random() % 512; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" pad size: %d", pad_size); +#endif + + char msg[dh_key_len + 512]; + char* ptr = msg; + int buf_size = dh_key_len + pad_size; + + memcpy(ptr, m_dh_key_exchange->get_local_key(), dh_key_len); + ptr += dh_key_len; + + std::generate(ptr, ptr + pad_size, random_byte); + send_buffer(msg, buf_size); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" sent DH key"); +#endif + } + + void bt_peer_connection::write_pe3_sync() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(is_outgoing()); + TORRENT_ASSERT(!m_sent_handshake); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + hasher h; + sha1_hash const& info_hash = t->torrent_file().info_hash(); + char const* const secret = m_dh_key_exchange->get_secret(); + + int pad_size = random() % 512; + + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) + char msg[20 + 20 + 8 + 4 + 2 + 512 + 2]; + char* ptr = msg; + + // sync hash (hash('req1',S)) + h.reset(); + h.update("req1",4); + h.update(secret, dh_key_len); + sha1_hash sync_hash = h.final(); + + memcpy(ptr, &sync_hash[0], 20); + ptr += 20; + + // stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ] + h.reset(); + h.update("req2",4); + h.update((const char*)info_hash.begin(), 20); + sha1_hash streamkey_hash = h.final(); + + h.reset(); + h.update("req3",4); + h.update(secret, dh_key_len); + sha1_hash obfsc_hash = h.final(); + obfsc_hash ^= streamkey_hash; + + memcpy(ptr, &obfsc_hash[0], 20); + ptr += 20; + + // Discard DH key exchange data, setup RC4 keys + init_pe_rc4_handler(secret, info_hash); + m_dh_key_exchange.reset(); // secret should be invalid at this point + + // write the verification constant and crypto field + int encrypt_size = sizeof(msg) - 512 + pad_size - 40; + + boost::uint8_t crypto_provide = m_ses.get_pe_settings().allowed_enc_level; + + // this is an invalid setting, but let's just make the best of the situation + if ((crypto_provide & pe_settings::both) == 0) crypto_provide = pe_settings::both; + +#ifdef TORRENT_VERBOSE_LOGGING + char const* level[] = {"plaintext", "rc4", "plaintext rc4"}; + peer_log(" crypto provide : [ %s ]" + , level[crypto_provide-1]); +#endif + + write_pe_vc_cryptofield(ptr, encrypt_size, crypto_provide, pad_size); + m_enc_handler->encrypt(ptr, encrypt_size); + send_buffer(msg, sizeof(msg) - 512 + pad_size); + } + + void bt_peer_connection::write_pe4_sync(int crypto_select) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(crypto_select == 0x02 || crypto_select == 0x01); + TORRENT_ASSERT(!m_sent_handshake); + + int pad_size = random() % 512; + + const int buf_size = 8 + 4 + 2 + pad_size; + char msg[512 + 8 + 4 + 2]; + write_pe_vc_cryptofield(msg, sizeof(msg), crypto_select, pad_size); + + m_enc_handler->encrypt(msg, buf_size); + send_buffer(msg, buf_size); + + // encryption method has been negotiated + if (crypto_select == 0x02) + m_rc4_encrypted = true; + else // 0x01 + m_rc4_encrypted = false; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" crypto select : [ %s ]" + , (crypto_select == 0x01) ? "plaintext" : "rc4"); +#endif + } + + void bt_peer_connection::write_pe_vc_cryptofield(char* write_buf, int len + , int crypto_field, int pad_size) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(crypto_field <= 0x03 && crypto_field > 0); + // vc,crypto_field,len(pad),pad, (len(ia)) + TORRENT_ASSERT((len >= 8+4+2+pad_size+2 && is_outgoing()) + || (len >= 8+4+2+pad_size && !is_outgoing())); + TORRENT_ASSERT(!m_sent_handshake); + + // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) + // len(pad) is zero for now, len(IA) only for outgoing connections + + // vc + memset(write_buf, 0, 8); + write_buf += 8; + + detail::write_uint32(crypto_field, write_buf); + detail::write_uint16(pad_size, write_buf); // len (pad) + + // fill pad with zeroes + std::generate(write_buf, write_buf + pad_size, random_byte); + write_buf += pad_size; + + // append len(ia) if we are initiating + if (is_outgoing()) + detail::write_uint16(handshake_len, write_buf); // len(IA) + } + + void bt_peer_connection::init_pe_rc4_handler(char const* secret, sha1_hash const& stream_key) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(secret); + + hasher h; + static const char keyA[] = "keyA"; + static const char keyB[] = "keyB"; + + // encryption rc4 longkeys + // outgoing connection : hash ('keyA',S,SKEY) + // incoming connection : hash ('keyB',S,SKEY) + + if (is_outgoing()) h.update(keyA, 4); else h.update(keyB, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash local_key = h.final(); + + h.reset(); + + // decryption rc4 longkeys + // outgoing connection : hash ('keyB',S,SKEY) + // incoming connection : hash ('keyA',S,SKEY) + + if (is_outgoing()) h.update(keyB, 4); else h.update(keyA, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash remote_key = h.final(); + + TORRENT_ASSERT(!m_enc_handler.get()); + m_enc_handler.reset(new (std::nothrow) rc4_handler); + m_enc_handler->set_incoming_key(&remote_key[0], 20); + m_enc_handler->set_outgoing_key(&local_key[0], 20); + + if (!m_enc_handler) + { + disconnect(errors::no_memory); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" computed RC4 keys"); +#endif + } + + int bt_peer_connection::get_syncoffset(char const* src, int src_size, + char const* target, int target_size) const + { + TORRENT_ASSERT(target_size >= src_size); + TORRENT_ASSERT(src_size > 0); + TORRENT_ASSERT(src); + TORRENT_ASSERT(target); + + int traverse_limit = target_size - src_size; + + // TODO: this could be optimized using knuth morris pratt + for (int i = 0; i < traverse_limit; ++i) + { + char const* target_ptr = target + i; + if (std::equal(src, src+src_size, target_ptr)) + return i; + } + +// // Partial sync +// for (int i = 0; i < target_size; ++i) +// { +// // first is iterator in src[] at which mismatch occurs +// // second is iterator in target[] at which mismatch occurs +// std::pair ret; +// int src_sync_size; +// if (i > traverse_limit) // partial sync test +// { +// ret = std::mismatch(src, src + src_size - (i - traverse_limit), &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == (src_size - (i - traverse_limit))) +// return i; +// } +// else // complete sync test +// { +// ret = std::mismatch(src, src + src_size, &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == src_size) +// return i; +// } +// } + + // no complete sync + return -1; + } +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::append_const_send_buffer(char const* buffer, int size) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted && m_rc4_encrypted) + { + // if we're encrypting this buffer, we need to make a copy + // since we'll mutate it + char* buf = (char*)malloc(size); + memcpy(buf, buffer, size); + bt_append_send_buffer(buf, size, boost::bind(&::free, _1)); + } + else +#endif + { + peer_connection::append_const_send_buffer(buffer, size); + } + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + void encrypt(char* buf, int len, void* userdata) + { + rc4_handler* rc4 = (rc4_handler*)userdata; + rc4->encrypt(buf, len); + } +#endif + + void bt_peer_connection::send_buffer(char const* buf, int size, int flags + , void (*f)(char*, int, void*), void* ud) + { + TORRENT_ASSERT(f == 0); + TORRENT_ASSERT(ud == 0); + TORRENT_ASSERT(buf); + TORRENT_ASSERT(size > 0); + + void* userdata = 0; + void (*fun)(char*, int, void*) = 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted && m_rc4_encrypted) + { + fun = encrypt; + userdata = m_enc_handler.get(); + } +#endif + + peer_connection::send_buffer(buf, size, flags, fun, userdata); + } + + void bt_peer_connection::write_handshake() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_sent_handshake); +#if TORRENT_USE_ASSERTS + m_sent_handshake = true; +#endif + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // add handshake to the send buffer + const char version_string[] = "BitTorrent protocol"; + const int string_len = sizeof(version_string)-1; + + char handshake[1 + string_len + 8 + 20 + 20]; + char* ptr = handshake; + // length of version string + detail::write_uint8(string_len, ptr); + // protocol identifier + memcpy(ptr, version_string, string_len); + ptr += string_len; + // 8 zeroes + memset(ptr, 0, 8); + +#ifndef TORRENT_DISABLE_DHT + // indicate that we support the DHT messages + *(ptr + 7) |= 0x01; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + // we support extensions + *(ptr + 5) |= 0x10; +#endif + + if (m_ses.m_settings.support_merkle_torrents) + { + // we support merkle torrents + *(ptr + 5) |= 0x08; + } + + // we support FAST extension + *(ptr + 7) |= 0x04; + +#ifdef TORRENT_VERBOSE_LOGGING + std::string bitmask; + for (int k = 0; k < 8; ++k) + { + for (int j = 0; j < 8; ++j) + { + if (ptr[k] & (0x80 >> j)) bitmask += '1'; + else bitmask += '0'; + } + } + peer_log("==> EXTENSION [ %s ]", bitmask.c_str()); +#endif + ptr += 8; + + // info hash + sha1_hash const& ih = t->torrent_file().info_hash(); + memcpy(ptr, &ih[0], 20); + ptr += 20; + + // peer id + if (m_ses.m_settings.anonymous_mode) + { + // in anonymous mode, every peer connection + // has a unique peer-id + for (int i = 0; i < 20; ++i) + m_our_peer_id[i] = random() & 0xff; + } + + memcpy(ptr, &m_our_peer_id[0], 20); + ptr += 20; + +#ifdef TORRENT_VERBOSE_LOGGING + { + char hex_pid[41]; + to_hex((char const*)&m_our_peer_id[0], 20, hex_pid); + hex_pid[40] = 0; + peer_log(">>> sent peer_id: %s client: %s" + , hex_pid, identify_client(m_our_peer_id).c_str()); + } + peer_log("==> HANDSHAKE [ ih: %s ]", to_hex(ih.to_string()).c_str()); +#endif + send_buffer(handshake, sizeof(handshake)); + } + + boost::optional bt_peer_connection::downloading_piece_progress() const + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + buffer::const_interval recv_buffer = receive_buffer(); + // are we currently receiving a 'piece' message? + if (m_state != read_packet + || recv_buffer.left() <= 9 + || recv_buffer[0] != msg_piece) + return boost::optional(); + + const char* ptr = recv_buffer.begin + 1; + peer_request r; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = packet_size() - 9; + + // is any of the piece message header data invalid? + if (!verify_piece(r)) + return boost::optional(); + + piece_block_progress p; + + p.piece_index = r.piece; + p.block_index = r.start / t->block_size(); + p.bytes_downloaded = recv_buffer.left() - 9; + p.full_block_bytes = r.length; + + return boost::optional(p); + } + + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void bt_peer_connection::on_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== KEEPALIVE"); +#endif + incoming_keepalive(); + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void bt_peer_connection::on_choke(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_choke, 2); + return; + } + if (!packet_finished()) return; + + incoming_choke(); + if (is_disconnecting()) return; + if (!m_supports_fast) + { + // we just got choked, and the peer that choked use + // doesn't support fast extensions, so we have to + // assume that the choke message implies that all + // of our requests are rejected. Go through them and + // pretend that we received reject request messages + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + while (!download_queue().empty()) + { + piece_block const& b = download_queue().front().block; + peer_request r; + r.piece = b.piece_index; + r.start = b.block_index * t->block_size(); + r.length = t->block_size(); + // if it's the last piece, make sure to + // set the length of the request to not + // exceed the end of the torrent. This is + // necessary in order to maintain a correct + // m_outsanding_bytes + if (r.piece == t->torrent_file().num_pieces() - 1) + { + r.length = (std::min)(t->torrent_file().piece_size( + r.piece) - r.start, r.length); + } + incoming_reject_request(r); + } + } + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void bt_peer_connection::on_unchoke(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_unchoke, 2); + return; + } + if (!packet_finished()) return; + + incoming_unchoke(); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void bt_peer_connection::on_interested(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_interested, 2); + return; + } + if (!packet_finished()) return; + + incoming_interested(); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void bt_peer_connection::on_not_interested(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 1) + { + disconnect(errors::invalid_not_interested, 2); + return; + } + if (!packet_finished()) return; + + incoming_not_interested(); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void bt_peer_connection::on_have(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 5) + { + disconnect(errors::invalid_have, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int index = detail::read_int32(ptr); + + incoming_have(index); + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void bt_peer_connection::on_bitfield(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + m_statistics.received_bytes(0, received); + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && packet_size() - 1 != (t->torrent_file().num_pieces() + 7) / 8) + { + disconnect(errors::invalid_bitfield_size, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + bitfield bits; + bits.borrow_bytes((char*)recv_buffer.begin + 1 + , t->valid_metadata()?get_bitfield().size():(packet_size()-1)*8); + + incoming_bitfield(bits); + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void bt_peer_connection::on_request(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 13) + { + disconnect(errors::invalid_request, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_request(r); + } + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void bt_peer_connection::on_piece(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = receive_pos(); // recv_buffer.end - recv_buffer.begin; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + bool merkle = (unsigned char)recv_buffer.begin[0] == 250; + if (merkle) + { + if (recv_pos == 1) + { + set_soft_packet_size(13); + m_statistics.received_bytes(0, received); + return; + } + if (recv_pos < 13) + { + m_statistics.received_bytes(0, received); + return; + } + if (recv_pos == 13) + { + const char* ptr = recv_buffer.begin + 9; + int list_size = detail::read_int32(ptr); + // now we know how long the bencoded hash list is + // and we can allocate the disk buffer and receive + // into it + + if (list_size > packet_size() - 13) + { + disconnect(errors::invalid_hash_list, 2); + return; + } + + if (packet_size() - 13 - list_size > t->block_size()) + { + disconnect(errors::packet_too_large, 2); + return; + } + + TORRENT_ASSERT(!has_disk_receive_buffer()); + if (!allocate_disk_receive_buffer(packet_size() - 13 - list_size)) + { + m_statistics.received_bytes(0, received); + return; + } + } + } + else + { + if (recv_pos == 1) + { + TORRENT_ASSERT(!has_disk_receive_buffer()); + + if (packet_size() - 9 > t->block_size()) + { + disconnect(errors::packet_too_large, 2); + return; + } + + if (!allocate_disk_receive_buffer(packet_size() - 9)) + { + m_statistics.received_bytes(0, received); + return; + } + } + } + TORRENT_ASSERT(has_disk_receive_buffer() || packet_size() == 9); + // classify the received data as protocol chatter + // or data payload for the statistics + int piece_bytes = 0; + + int header_size = merkle?13:9; + + peer_request p; + int list_size = 0; + + if (recv_pos >= header_size) + { + const char* ptr = recv_buffer.begin + 1; + p.piece = detail::read_int32(ptr); + p.start = detail::read_int32(ptr); + + if (merkle) + { + list_size = detail::read_int32(ptr); + p.length = packet_size() - list_size - header_size; + header_size += list_size; + } + else + { + p.length = packet_size() - header_size; + } + } + + if (recv_pos <= header_size) + { + // only received protocol data + m_statistics.received_bytes(0, received); + } + else if (recv_pos - received >= header_size) + { + // only received payload data + m_statistics.received_bytes(received, 0); + piece_bytes = received; + } + else + { + // received a bit of both + TORRENT_ASSERT(recv_pos - received < header_size); + TORRENT_ASSERT(recv_pos > header_size); + TORRENT_ASSERT(header_size - (recv_pos - received) <= header_size); + m_statistics.received_bytes( + recv_pos - header_size + , header_size - (recv_pos - received)); + piece_bytes = recv_pos - header_size; + } + + if (recv_pos < header_size) return; + +#ifdef TORRENT_VERBOSE_LOGGING +// peer_log("<== PIECE_FRAGMENT p: %d start: %d length: %d" +// , p.piece, p.start, p.length); +#endif + + if (recv_pos - received < header_size && recv_pos >= header_size) + { + // call this once, the first time the entire header + // has been received + start_receive_piece(p); + if (is_disconnecting()) return; + } + + TORRENT_ASSERT(has_disk_receive_buffer() || packet_size() == header_size); + + incoming_piece_fragment(piece_bytes); + if (!packet_finished()) return; + + if (merkle && list_size > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HASHPIECE [ piece: %d list: %d ]", p.piece, list_size); +#endif + lazy_entry hash_list; + error_code ec; + if (lazy_bdecode(recv_buffer.begin + 13, recv_buffer.begin+ 13 + list_size + , hash_list, ec) != 0) + { + disconnect(errors::invalid_hash_piece, 2); + return; + } + + // the list has this format: + // [ [node-index, hash], [node-index, hash], ... ] + if (hash_list.type() != lazy_entry::list_t) + { + disconnect(errors::invalid_hash_list, 2); + return; + } + + std::map nodes; + for (int i = 0; i < hash_list.list_size(); ++i) + { + lazy_entry const* e = hash_list.list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::string_t + || e->list_at(1)->string_length() != 20) continue; + + nodes.insert(std::make_pair(int(e->list_int_value_at(0)) + , sha1_hash(e->list_at(1)->string_ptr()))); + } + if (!nodes.empty() && !t->add_merkle_nodes(nodes, p.piece)) + { + disconnect(errors::invalid_hash_piece, 2); + return; + } + } + + disk_buffer_holder holder(m_ses, release_disk_receive_buffer()); + incoming_piece(p, holder); + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void bt_peer_connection::on_cancel(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 13) + { + disconnect(errors::invalid_cancel, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_cancel(r); + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void bt_peer_connection::on_dht_port(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() != 3) + { + disconnect(errors::invalid_dht_port, 2); + return; + } + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int listen_port = detail::read_uint16(ptr); + + incoming_dht_port(listen_port); + + if (!m_supports_dht_port) + { + m_supports_dht_port = true; +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + } + + void bt_peer_connection::on_suggest_piece(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_suggest, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int piece = detail::read_uint32(ptr); + incoming_suggest(piece); + } + + void bt_peer_connection::on_have_all(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_have_all, 2); + return; + } + incoming_have_all(); + } + + void bt_peer_connection::on_have_none(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_have_none, 2); + return; + } + incoming_have_none(); + } + + void bt_peer_connection::on_reject_request(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_reject, 2); + return; + } + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_reject_request(r); + } + + void bt_peer_connection::on_allowed_fast(int received) + { + INVARIANT_CHECK; + + m_statistics.received_bytes(0, received); + if (!m_supports_fast) + { + disconnect(errors::invalid_allow_fast, 2); + return; + } + + if (!packet_finished()) return; + buffer::const_interval recv_buffer = receive_buffer(); + const char* ptr = recv_buffer.begin + 1; + int index = detail::read_int32(ptr); + + incoming_allowed_fast(index); + } + + // ----------------------------- + // -------- RENDEZVOUS --------- + // ----------------------------- + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::on_holepunch() + { + INVARIANT_CHECK; + + if (!packet_finished()) return; + + // we can't accept holepunch messages from peers + // that don't support the holepunch extension + // because we wouldn't be able to respond + if (m_holepunch_id == 0) return; + + buffer::const_interval recv_buffer = receive_buffer(); + TORRENT_ASSERT(*recv_buffer.begin == msg_extended); + ++recv_buffer.begin; + TORRENT_ASSERT(*recv_buffer.begin == holepunch_msg); + ++recv_buffer.begin; + + const char* ptr = recv_buffer.begin; + + // ignore invalid messages + if (recv_buffer.left() < 2) return; + + int msg_type = detail::read_uint8(ptr); + int addr_type = detail::read_uint8(ptr); + + tcp::endpoint ep; + + if (addr_type == 0) + { + if (recv_buffer.left() < 2 + 4 + 2) return; + // IPv4 address + ep = detail::read_v4_endpoint(ptr); + } +#if TORRENT_USE_IPV6 + else if (addr_type == 1) + { + // IPv6 address + if (recv_buffer.left() < 2 + 18 + 2) return; + ep = detail::read_v6_endpoint(ptr); + } +#endif + else + { +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; + peer_log("<== HOLEPUNCH [ msg: %s from %s to: unknown address type ]" + , (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type") + , print_address(remote().address()).c_str()); +#endif + + return; // unknown address type + } + + boost::shared_ptr t = associated_torrent().lock(); + if (!t) return; + + switch (msg_type) + { + case hp_rendezvous: // rendezvous + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg: rendezvous to: %s ]" + , print_address(ep.address()).c_str()); +#endif + // this peer is asking us to introduce it to + // the peer at 'ep'. We need to find which of + // our connections points to that endpoint + bt_peer_connection* p = t->find_peer(ep); + if (p == 0) + { + // we're not connected to this peer + write_holepunch_msg(hp_failed, ep, hp_not_connected); + break; + } + if (!p->supports_holepunch()) + { + write_holepunch_msg(hp_failed, ep, hp_no_support); + break; + } + if (p == this) + { + write_holepunch_msg(hp_failed, ep, hp_no_self); + break; + } + + write_holepunch_msg(hp_connect, ep, 0); + p->write_holepunch_msg(hp_connect, remote(), 0); + } break; + case hp_connect: + { + // add or find the peer with this endpoint + policy::peer* p = t->get_policy().add_peer(ep, peer_id(0), peer_info::pex, 0); + if (p == 0 || p->connection) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s error: failed to add peer ]" + , print_address(ep.address()).c_str()); +#endif + // we either couldn't add this peer, or it's + // already connected. Just ignore the connect message + break; + } + if (p->banned) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s error: peer banned ]" + , print_address(ep.address()).c_str()); +#endif + // this peer is banned, don't connect to it + break; + + } + // to make sure we use the uTP protocol + p->supports_utp = true; + // #error make sure we make this a connection candidate + // in case it has too many failures for instance + t->connect_to_peer(p, true); + // mark this connection to be in holepunch mode + // so that it will retry faster and stick to uTP while it's + // retrying + if (p->connection) + p->connection->set_holepunch_mode(); +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH [ msg:connect to: %s ]" + , print_address(ep.address()).c_str()); +#endif + } break; + case hp_failed: + { + boost::uint32_t error = detail::read_uint32(ptr); +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"}; + peer_log("<== HOLEPUNCH [ msg:failed error: %d msg: %s ]", error + , ((error > 0 && error < 5)?err_msg[error-1]:"unknown message id")); +#endif + // #error deal with holepunch errors + (void)error; + } break; +#if defined TORRENT_VERBOSE_LOGGING + default: + { + error_code ec; + peer_log("<== HOLEPUNCH [ msg: unknown message type (%d) to: %s ]" + , msg_type, print_address(ep.address()).c_str()); + } +#endif + } + } + + void bt_peer_connection::write_holepunch_msg(int type, tcp::endpoint const& ep, int error) + { + char buf[35]; + char* ptr = buf + 6; + detail::write_uint8(type, ptr); + if (ep.address().is_v4()) detail::write_uint8(0, ptr); + else detail::write_uint8(1, ptr); + detail::write_endpoint(ep, ptr); + +#if defined TORRENT_VERBOSE_LOGGING + error_code ec; + static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; + static const char* hp_error_string[] = {"", "no such peer", "not connected", "no support", "no self"}; + peer_log("==> HOLEPUNCH [ msg: %s to: %s error: %s ]" + , (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type") + , print_address(ep.address()).c_str() + , hp_error_string[error]); +#endif + if (type == hp_failed) + { + detail::write_uint32(error, ptr); + } + + // write the packet length and type + char* hdr = buf; + detail::write_uint32(ptr - buf - 4, hdr); + detail::write_uint8(msg_extended, hdr); + detail::write_uint8(m_holepunch_id, hdr); + + TORRENT_ASSERT(ptr <= buf + sizeof(buf)); + + send_buffer(buf, ptr - buf); + } +#endif // TORRENT_DISABLE_EXTENSIONS + + // ----------------------------- + // --------- EXTENDED ---------- + // ----------------------------- + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::on_extended(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() < 2) + { + disconnect(errors::invalid_extended, 2); + return; + } + + if (associated_torrent().expired()) + { + disconnect(errors::invalid_extended, 2); + return; + } + + buffer::const_interval recv_buffer = receive_buffer(); + if (recv_buffer.left() < 2) return; + + TORRENT_ASSERT(*recv_buffer.begin == msg_extended); + ++recv_buffer.begin; + + int extended_id = detail::read_uint8(recv_buffer.begin); + + if (extended_id == 0) + { + on_extended_handshake(); + disconnect_if_redundant(); + return; + } + + if (extended_id == upload_only_msg) + { + if (!packet_finished()) return; + if (packet_size() != 3) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UPLOAD_ONLY [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + bool ul = detail::read_uint8(recv_buffer.begin) != 0; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UPLOAD_ONLY [ %s ]", (ul?"true":"false")); +#endif + set_upload_only(ul); + return; + } + + if (extended_id == share_mode_msg) + { + if (!packet_finished()) return; + if (packet_size() != 3) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SHARE_MODE [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + bool sm = detail::read_uint8(recv_buffer.begin) != 0; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SHARE_MODE [ %s ]", (sm?"true":"false")); +#endif + set_share_mode(sm); + return; + } + + if (extended_id == holepunch_msg) + { + if (!packet_finished()) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HOLEPUNCH"); +#endif + on_holepunch(); + return; + } + + if (extended_id == dont_have_msg) + { + if (!packet_finished()) return; + if (packet_size() != 6) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DONT_HAVE [ ERROR: unexpected packet size: %d ]", packet_size()); +#endif + return; + } + int piece = detail::read_uint32(recv_buffer.begin); + incoming_dont_have(piece); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + if (packet_finished()) + peer_log("<== EXTENSION MESSAGE [ msg: %d size: %d ]" + , extended_id, packet_size()); +#endif + + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_extended(packet_size() - 2, extended_id + , recv_buffer)) + return; + } + + disconnect(errors::invalid_message, 2); + return; + } + + void bt_peer_connection::on_extended_handshake() + { + if (!packet_finished()) return; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + buffer::const_interval recv_buffer = receive_buffer(); + + lazy_entry root; + error_code ec; + int pos; + int ret = lazy_bdecode(recv_buffer.begin + 2, recv_buffer.end, root, ec, &pos); + if (ret != 0 || ec || root.type() != lazy_entry::dict_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** invalid extended handshake: %s pos: %d" + , ec.message().c_str(), pos); +#endif + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== EXTENDED HANDSHAKE: %s", print_entry(root).c_str()); +#endif + + for (extension_list_t::iterator i = m_extensions.begin(); + !m_extensions.empty() && i != m_extensions.end();) + { + // a false return value means that the extension + // isn't supported by the other end. So, it is removed. + if (!(*i)->on_extension_handshake(root)) + i = m_extensions.erase(i); + else + ++i; + } + if (is_disconnecting()) return; + + // upload_only + if (lazy_entry const* m = root.dict_find_dict("m")) + { + m_upload_only_id = boost::uint8_t(m->dict_find_int_value("upload_only", 0)); + m_holepunch_id = boost::uint8_t(m->dict_find_int_value("ut_holepunch", 0)); + m_dont_have_id = boost::uint8_t(m->dict_find_int_value("lt_donthave", 0)); + } + + // there is supposed to be a remote listen port + int listen_port = int(root.dict_find_int_value("p")); + if (listen_port > 0 && peer_info_struct() != 0) + { + t->get_policy().update_peer_port(listen_port + , peer_info_struct(), peer_info::incoming); + received_listen_port(); + if (is_disconnecting()) return; + } + + // there should be a version too + // but where do we put that info? + + int last_seen_complete = boost::uint8_t(root.dict_find_int_value("complete_ago", -1)); + if (last_seen_complete >= 0) set_last_seen_complete(last_seen_complete); + + std::string client_info = root.dict_find_string_value("v"); + if (!client_info.empty()) m_client_version = client_info; + + int reqq = int(root.dict_find_int_value("reqq")); + if (reqq > 0) + { + max_out_request_queue(reqq); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MAX OUT REQUEST QUEUE [ %d ]", reqq); +#endif + } + + if (root.dict_find_int_value("upload_only", 0)) + set_upload_only(true); + + if (m_ses.m_settings.support_share_mode + && root.dict_find_int_value("share_mode", 0)) + set_share_mode(true); + + std::string myip = root.dict_find_string_value("yourip"); + if (!myip.empty()) + { + if (myip.size() == address_v4::bytes_type().size()) + { + address_v4::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + m_ses.set_external_address(address_v4(bytes) + , aux::session_impl::source_peer, remote().address()); + } +#if TORRENT_USE_IPV6 + else if (myip.size() == address_v6::bytes_type().size()) + { + address_v6::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + address_v6 ipv6_address(bytes); + if (ipv6_address.is_v4_mapped()) + m_ses.set_external_address(ipv6_address.to_v4() + , aux::session_impl::source_peer, remote().address()); + else + m_ses.set_external_address(ipv6_address + , aux::session_impl::source_peer, remote().address()); + } +#endif + } + + // if we're finished and this peer is uploading only + // disconnect it + if (t->is_finished() && upload_only() + && t->settings().close_redundant_connections + && !t->share_mode()) + disconnect(errors::upload_upload_connection); + } +#endif // TORRENT_DISABLE_EXTENSIONS + + bool bt_peer_connection::dispatch_message(int received) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(received > 0); + + // this means the connection has been closed already + if (associated_torrent().expired()) + { + m_statistics.received_bytes(0, received); + return false; + } + + buffer::const_interval recv_buffer = receive_buffer(); + + TORRENT_ASSERT(recv_buffer.left() >= 1); + int packet_type = (unsigned char)recv_buffer[0]; + + if (m_ses.m_settings.support_merkle_torrents + && packet_type == 250) packet_type = msg_piece; + + if (packet_type < 0 + || packet_type >= num_supported_messages + || m_message_handler[packet_type] == 0) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_unknown_message(packet_size(), packet_type + , buffer::const_interval(recv_buffer.begin+1 + , recv_buffer.end))) + return packet_finished(); + } +#endif + + m_statistics.received_bytes(0, received); + // What's going on here?! + // break in debug builds to allow investigation +// TORRENT_ASSERT(false); + disconnect(errors::invalid_message); + return packet_finished(); + } + + TORRENT_ASSERT(m_message_handler[packet_type] != 0); + +#ifdef TORRENT_DEBUG + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + // call the correct handler for this packet type + (this->*m_message_handler[packet_type])(received); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == received); +#endif + + return packet_finished(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::write_upload_only() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + if (m_upload_only_id == 0) return; + if (t->share_mode()) return; + + // if we send upload-only, the other end is very likely to disconnect + // us, at least if it's a seed. If we don't want to close redundant + // connections, don't sent upload-only + if (!m_ses.settings().close_redundant_connections) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> UPLOAD_ONLY [ %d ]" + , int(t->is_upload_only() && !t->super_seeding())); +#endif + + char msg[7] = {0, 0, 0, 3, msg_extended}; + char* ptr = msg + 5; + detail::write_uint8(m_upload_only_id, ptr); + // if we're super seeding, we don't want to make peers + // think that we only have a single piece and is upload + // only, since they might disconnect immediately when + // they have downloaded a single piece, although we'll + // make another piece available + detail::write_uint8(t->is_upload_only() && !t->super_seeding(), ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_share_mode() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + if (m_share_mode_id == 0) return; + + char msg[7] = {0, 0, 0, 3, msg_extended}; + char* ptr = msg + 5; + detail::write_uint8(m_share_mode_id, ptr); + detail::write_uint8(t->share_mode(), ptr); + send_buffer(msg, sizeof(msg)); + } +#endif + + void bt_peer_connection::write_keepalive() + { + INVARIANT_CHECK; + + // Don't require the bitfield to have been sent at this point + // the case where m_sent_bitfield may not be true is if the + // torrent doesn't have any metadata, and a peer is timimg out. + // then the keep-alive message will be sent before the bitfield + // this is a violation to the original protocol, but necessary + // for the metadata extension. + TORRENT_ASSERT(m_sent_handshake); + + char msg[] = {0,0,0,0}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_cancel(peer_request const& r) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[17] = {0,0,0,13, msg_cancel}; + char* ptr = msg + 5; + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg)); + + if (!m_supports_fast) + incoming_reject_request(r); + } + + void bt_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + + char msg[17] = {0,0,0,13, msg_request}; + char* ptr = msg + 5; + + detail::write_int32(r.piece, ptr); // index + detail::write_int32(r.start, ptr); // begin + detail::write_int32(r.length, ptr); // length + send_buffer(msg, sizeof(msg), message_type_request); + } + + void bt_peer_connection::write_bitfield() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(m_sent_handshake && !m_sent_bitfield); + TORRENT_ASSERT(t->valid_metadata()); + + // in this case, have_all or have_none should be sent instead + TORRENT_ASSERT(!m_supports_fast || !t->is_seed() || t->num_have() != 0); + + if (t->super_seeding()) + { + if (m_supports_fast) write_have_none(); + + // if we are super seeding, pretend to not have any piece + // and don't send a bitfield +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + + // bootstrap superseeding by sending two have message + int piece = t->get_piece_to_super_seed(get_bitfield()); + if (piece >= 0) superseed_piece(-1, piece); + piece = t->get_piece_to_super_seed(get_bitfield()); + if (piece >= 0) superseed_piece(-1, piece); + return; + } + else if (m_supports_fast && t->is_seed() && !m_ses.settings().lazy_bitfields) + { + write_have_all(); + send_allowed_set(); + return; + } + else if (m_supports_fast && t->num_have() == 0) + { + write_have_none(); + send_allowed_set(); + return; + } + else if (t->num_have() == 0) + { + // don't send a bitfield if we don't have any pieces +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(" *** NOT SENDING BITFIELD"); +#endif +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + return; + } + + int num_pieces = t->torrent_file().num_pieces(); + + int lazy_pieces[50]; + int num_lazy_pieces = 0; + int lazy_piece = 0; + + if (t->is_seed() && m_ses.settings().lazy_bitfields +#ifndef TORRENT_DISABLE_ENCRYPTION + && !m_encrypted +#endif + ) + { + num_lazy_pieces = (std::min)(50, num_pieces / 10); + if (num_lazy_pieces < 1) num_lazy_pieces = 1; + for (int i = 0; i < num_pieces; ++i) + { + if (int(random() % (num_pieces - i)) >= num_lazy_pieces - lazy_piece) continue; + lazy_pieces[lazy_piece++] = i; + } + TORRENT_ASSERT(lazy_piece == num_lazy_pieces); + } + + const int packet_size = (num_pieces + 7) / 8 + 5; + + char* msg = TORRENT_ALLOCA(char, packet_size); + if (msg == 0) return; // out of memory + unsigned char* ptr = (unsigned char*)msg; + + detail::write_int32(packet_size - 4, ptr); + detail::write_uint8(msg_bitfield, ptr); + + if (t->is_seed()) + { + memset(ptr, 0xff, packet_size - 5); + + // Clear trailing bits + unsigned char *p = ((unsigned char *)msg) + packet_size - 1; + *p = (0xff << ((8 - (num_pieces & 7)) & 7)) & 0xff; + } + else + { + memset(ptr, 0, packet_size - 5); + piece_picker const& p = t->picker(); + int mask = 0x80; + for (int i = 0; i < num_pieces; ++i) + { + if (p.have_piece(i)) *ptr |= mask; + mask >>= 1; + if (mask == 0) + { + mask = 0x80; + ++ptr; + } + } + } + for (int c = 0; c < num_lazy_pieces; ++c) + msg[5 + lazy_pieces[c] / 8] &= ~(0x80 >> (lazy_pieces[c] & 7)); + +#ifdef TORRENT_VERBOSE_LOGGING + + std::string bitfield_string; + bitfield_string.resize(num_pieces); + for (int k = 0; k < num_pieces; ++k) + { + if (msg[5 + k / 8] & (0x80 >> (k % 8))) bitfield_string[k] = '1'; + else bitfield_string[k] = '0'; + } + peer_log("==> BITFIELD [ %s ]", bitfield_string.c_str()); +#endif +#if TORRENT_USE_ASSERTS + m_sent_bitfield = true; +#endif + + send_buffer(msg, packet_size); + + if (num_lazy_pieces > 0) + { + for (int i = 0; i < num_lazy_pieces; ++i) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ]", lazy_pieces[i]); +#endif + write_have(lazy_pieces[i]); + } + // TODO: if we're finished, send upload_only message + } + + if (m_supports_fast) + send_allowed_set(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::write_extensions() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_supports_extensions); + TORRENT_ASSERT(m_sent_handshake); + + entry handshake; + entry::dictionary_type& m = handshake["m"].dict(); + + // if we're using a proxy, our listen port won't be useful + // anyway. + if (!m_ses.settings().force_proxy && is_outgoing()) + handshake["p"] = m_ses.listen_port(); + + // only send the port in case we bade the connection + // on incoming connections the other end already knows + // our listen port + if (!m_ses.m_settings.anonymous_mode) + { + handshake["v"] = m_ses.settings().handshake_client_version.empty() + ? m_ses.settings().user_agent : m_ses.settings().handshake_client_version; + } + + std::string remote_address; + std::back_insert_iterator out(remote_address); + detail::write_address(remote().address(), out); + handshake["yourip"] = remote_address; + handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + m["upload_only"] = upload_only_msg; + m["ut_holepunch"] = holepunch_msg; + if (m_ses.m_settings.support_share_mode) + m["share_mode"] = share_mode_msg; + m["lt_donthave"] = dont_have_msg; + + int complete_ago = -1; + if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete(); + handshake["complete_ago"] = complete_ago; + + // if we're using lazy bitfields or if we're super seeding, don't say + // we're upload only, since it might make peers disconnect + // don't tell anyone we're upload only when in share mode + // we want to stay connected to seeds + // if we're super seeding, we don't want to make peers + // think that we only have a single piece and is upload + // only, since they might disconnect immediately when + // they have downloaded a single piece, although we'll + // make another piece available + if (t->is_upload_only() + && !t->share_mode() + && !t->super_seeding() + && (!m_ses.settings().lazy_bitfields +#ifndef TORRENT_DISABLE_ENCRYPTION + || m_encrypted +#endif + )) + handshake["upload_only"] = 1; + + if (m_ses.m_settings.support_share_mode + && t->share_mode()) + handshake["share_mode"] = 1; + + // loop backwards, to make the first extension be the last + // to fill in the handshake (i.e. give the first extensions priority) + for (extension_list_t::reverse_iterator i = m_extensions.rbegin() + , end(m_extensions.rend()); i != end; ++i) + { + (*i)->add_handshake(handshake); + } + +#ifndef NDEBUG + // make sure there are not conflicting extensions + std::set ext; + for (entry::dictionary_type::const_iterator i = m.begin() + , end(m.end()); i != end; ++i) + { + if (i->second.type() != entry::int_t) continue; + int val = int(i->second.integer()); + TORRENT_ASSERT(ext.find(val) == ext.end()); + ext.insert(val); + } +#endif + + std::vector dict_msg; + bencode(std::back_inserter(dict_msg), handshake); + + char msg[6]; + char* ptr = msg; + + // write the length of the message + detail::write_int32((int)dict_msg.size() + 2, ptr); + detail::write_uint8(msg_extended, ptr); + // signal handshake message + detail::write_uint8(0, ptr); + send_buffer(msg, sizeof(msg)); + send_buffer(&dict_msg[0], dict_msg.size()); + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> EXTENDED HANDSHAKE: %s", handshake.to_string().c_str()); +#endif + } +#endif + + void bt_peer_connection::write_choke() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + if (is_choked()) return; + char msg[] = {0,0,0,1,msg_choke}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_unchoke() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_unchoke}; + send_buffer(msg, sizeof(msg)); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->sent_unchoke(); + } +#endif + } + + void bt_peer_connection::write_interested() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_interested}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_not_interested() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,1,msg_not_interested}; + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_have(int index) + { + INVARIANT_CHECK; + TORRENT_ASSERT(associated_torrent().lock()->valid_metadata()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().num_pieces()); + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + char msg[] = {0,0,0,5,msg_have,0,0,0,0}; + char* ptr = msg + 5; + detail::write_int32(index, ptr); + send_buffer(msg, sizeof(msg)); + } + + void bt_peer_connection::write_piece(peer_request const& r, disk_buffer_holder& buffer) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_sent_handshake && m_sent_bitfield); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + bool merkle = t->torrent_file().is_merkle_torrent() && r.start == 0; + // the hash piece looks like this: + // uint8_t msg + // uint32_t piece index + // uint32_t start + // uint32_t list len + // var bencoded list + // var piece data + char msg[4 + 1 + 4 + 4 + 4]; + char* ptr = msg; + TORRENT_ASSERT(r.length <= 16 * 1024); + detail::write_int32(r.length + 1 + 4 + 4, ptr); + if (m_ses.m_settings.support_merkle_torrents && merkle) + detail::write_uint8(250, ptr); + else + detail::write_uint8(msg_piece, ptr); + detail::write_int32(r.piece, ptr); + detail::write_int32(r.start, ptr); + + // if this is a merkle torrent and the start offset + // is 0, we need to include the merkle node hashes + if (merkle) + { + std::vector piece_list_buf; + entry piece_list; + entry::list_type& l = piece_list.list(); + std::map merkle_node_list = t->torrent_file().build_merkle_list(r.piece); + for (std::map::iterator i = merkle_node_list.begin() + , end(merkle_node_list.end()); i != end; ++i) + { + l.push_back(entry(entry::list_t)); + l.back().list().push_back(i->first); + l.back().list().push_back(i->second.to_string()); + } + bencode(std::back_inserter(piece_list_buf), piece_list); + detail::write_int32(piece_list_buf.size(), ptr); + + char* ptr = msg; + detail::write_int32(r.length + 1 + 4 + 4 + 4 + piece_list_buf.size(), ptr); + + send_buffer(msg, 17); + send_buffer(&piece_list_buf[0], piece_list_buf.size()); + } + else + { + send_buffer(msg, 13); + } + + bt_append_send_buffer(buffer.get(), r.length + , boost::bind(&session_impl::free_disk_buffer + , boost::ref(m_ses), _1)); + buffer.release(); + + m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); + setup_send(); + } + + namespace + { + struct match_peer_id + { + match_peer_id(peer_id const& id, peer_connection const* pc) + : m_id(id), m_pc(pc) + { TORRENT_ASSERT(pc); } + + bool operator()(policy::peer const* p) const + { + return p->connection != m_pc + && p->connection + && p->connection->pid() == m_id + && !p->connection->pid().is_all_zeros() + && p->address() == m_pc->remote().address(); + } + + peer_id const& m_id; + peer_connection const* m_pc; + }; + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void bt_peer_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT(in_handshake() || !m_rc4_encrypted || m_encrypted); + if (m_rc4_encrypted && m_encrypted) + { + std::pair wr_buf = wr_recv_buffers(bytes_transferred); + m_enc_handler->decrypt(wr_buf.first.begin, wr_buf.first.left()); + if (wr_buf.second.left()) m_enc_handler->decrypt(wr_buf.second.begin, wr_buf.second.left()); + } +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // m_state is set to read_pe_dhkey in initial state + // (read_protocol_identifier) for incoming, or in constructor + // for outgoing + if (m_state == read_pe_dhkey) + { + m_statistics.received_bytes(0, bytes_transferred); + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(packet_size() == dh_key_len); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (!packet_finished()) return; + + // write our dh public key. m_dh_key_exchange is + // initialized in write_pe1_2_dhkey() + if (!is_outgoing()) write_pe1_2_dhkey(); + if (is_disconnecting()) return; + + // read dh key, generate shared secret + if (m_dh_key_exchange->compute_secret(recv_buffer.begin) != 0) + { + disconnect(errors::no_memory); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** received DH key"); +#endif + + // PadA/B can be a max of 512 bytes, and 20 bytes more for + // the sync hash (if incoming), or 8 bytes more for the + // encrypted verification constant (if outgoing). Instead + // of requesting the maximum possible, request the maximum + // possible to ensure we do not overshoot the standard + // handshake. + + if (is_outgoing()) + { + m_state = read_pe_syncvc; + write_pe3_sync(); + + // initial payload is the standard handshake, this is + // always rc4 if sent here. m_rc4_encrypted is flagged + // again according to peer selection. + m_rc4_encrypted = true; + m_encrypted = true; + write_handshake(); + m_rc4_encrypted = false; + m_encrypted = false; + + // vc,crypto_select,len(pad),pad, encrypt(handshake) + // 8+4+2+0+handshake_len + reset_recv_buffer(8+4+2+0+handshake_len); + } + else + { + // already written dh key + m_state = read_pe_synchash; + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,encrypt(handshake) + reset_recv_buffer(20+20+8+4+2+0+handshake_len); + } + TORRENT_ASSERT(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_pe_synchash) + { + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 20) + { + m_statistics.received_bytes(0, bytes_transferred); + + if (packet_finished()) + disconnect(errors::sync_hash_not_found, 1); + return; + } + + if (!m_sync_hash.get()) + { + TORRENT_ASSERT(m_sync_bytes_read == 0); + hasher h; + + // compute synchash (hash('req1',S)) + h.update("req1", 4); + h.update(m_dh_key_exchange->get_secret(), dh_key_len); + + m_sync_hash.reset(new (std::nothrow) sha1_hash(h.final())); + if (!m_sync_hash) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::no_memory); + return; + } + } + + int syncoffset = get_syncoffset((char*)m_sync_hash->begin(), 20 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + + std::size_t bytes_processed = recv_buffer.left() - 20; + m_sync_bytes_read += bytes_processed; + if (m_sync_bytes_read >= 512) + { + disconnect(errors::sync_hash_not_found, 1); + return; + } + + cut_receive_buffer(bytes_processed, (std::min)(packet_size() + , (512+20) - m_sync_bytes_read)); + + TORRENT_ASSERT(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 20; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** sync point (hash) found at offset %d" + , m_sync_bytes_read + bytes_processed - 20); +#endif + m_state = read_pe_skey_vc; + // skey,vc - 28 bytes + m_sync_hash.reset(); + int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + cut_receive_buffer(bytes_processed, 28); + } + } + + if (m_state == read_pe_skey_vc) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(packet_size() == 28); + + if (!packet_finished()) return; + + recv_buffer = receive_buffer(); + + aux::session_impl::torrent_map::const_iterator i; + + for (i = m_ses.m_torrents.begin(); i != m_ses.m_torrents.end(); ++i) + { + torrent const& ti = *i->second; + + sha1_hash const& skey_hash = ti.obfuscated_hash(); + sha1_hash obfs_hash = m_dh_key_exchange->get_hash_xor_mask(); + obfs_hash ^= skey_hash; + + if (std::equal(recv_buffer.begin, recv_buffer.begin + 20, + (char*)&obfs_hash[0])) + { + if (!t) + { + attach_to_torrent(ti.info_hash(), false); + if (is_disconnecting()) return; + + t = associated_torrent().lock(); + TORRENT_ASSERT(t); + } + + init_pe_rc4_handler(m_dh_key_exchange->get_secret(), ti.info_hash()); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** stream key found, torrent located"); +#endif + break; + } + } + + if (!m_enc_handler.get()) + { + disconnect(errors::invalid_info_hash, 1); + return; + } + + // verify constant + buffer::interval wr_recv_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_recv_buf.begin + 20, 8); + wr_recv_buf.begin += 28; + + const char sh_vc[] = {0,0,0,0, 0,0,0,0}; + if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) + { + disconnect(errors::invalid_encryption_constant, 2); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** verification constant found"); +#endif + m_state = read_pe_cryptofield; + reset_recv_buffer(4 + 2); + } + + // cannot fall through into + if (m_state == read_pe_syncvc) + { + TORRENT_ASSERT(is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 8) + { + m_statistics.received_bytes(0, bytes_transferred); + if (packet_finished()) + disconnect(errors::invalid_encryption_constant, 2); + return; + } + + // generate the verification constant + if (!m_sync_vc.get()) + { + TORRENT_ASSERT(m_sync_bytes_read == 0); + + m_sync_vc.reset(new (std::nothrow) char[8]); + if (!m_sync_vc) + { + disconnect(errors::no_memory); + return; + } + std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0); + m_enc_handler->decrypt(m_sync_vc.get(), 8); + } + + TORRENT_ASSERT(m_sync_vc.get()); + int syncoffset = get_syncoffset(m_sync_vc.get(), 8 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + std::size_t bytes_processed = recv_buffer.left() - 8; + m_sync_bytes_read += bytes_processed; + m_statistics.received_bytes(0, bytes_transferred); + + if (m_sync_bytes_read >= 512) + { + disconnect(errors::invalid_encryption_constant, 2); + return; + } + + cut_receive_buffer(bytes_processed, (std::min)(packet_size() + , (512+8) - m_sync_bytes_read)); + + TORRENT_ASSERT(!packet_finished()); + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 8; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** sync point (verification constant) found at offset %d" + , m_sync_bytes_read + bytes_processed - 8); +#endif + int transferred_used = bytes_processed - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + + cut_receive_buffer(bytes_processed, 4 + 2); + + // delete verification constant + m_sync_vc.reset(); + m_state = read_pe_cryptofield; + // fall through + } + } + + if (m_state == read_pe_cryptofield) // local/remote + { + TORRENT_ASSERT(!m_encrypted); + TORRENT_ASSERT(!m_rc4_encrypted); + TORRENT_ASSERT(packet_size() == 4+2); + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + + if (!packet_finished()) return; + + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + boost::uint32_t crypto_field = detail::read_uint32(recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** crypto %s : [%s%s ]" + , is_outgoing() ? "select" : "provide" + , (crypto_field & 1) ? " plaintext" : "" + , (crypto_field & 2) ? " rc4" : ""); +#endif + + if (!is_outgoing()) + { + // select a crypto method + int allowed_encryption = m_ses.get_pe_settings().allowed_enc_level; + boost::uint32_t crypto_select = crypto_field & allowed_encryption; + + // when prefer_rc4 is set, keep the most significant bit + // otherwise keep the least significant one + if (m_ses.get_pe_settings().prefer_rc4) + { + boost::uint32_t mask = (std::numeric_limits::max)(); + while (crypto_select & (mask << 1)) + { + mask <<= 1; + crypto_select = crypto_select & mask; + } + } + else + { + boost::uint32_t mask = (std::numeric_limits::max)(); + while (crypto_select & (mask >> 1)) + { + mask >>= 1; + crypto_select = crypto_select & mask; + } + } + + if (crypto_select == 0) + { + disconnect(errors::unsupported_encryption_mode, 1); + return; + } + + // write the pe4 step + write_pe4_sync(crypto_select); + } + else // is_outgoing() + { + // check if crypto select is valid + int allowed_encryption = m_ses.get_pe_settings().allowed_enc_level; + + crypto_field &= allowed_encryption; + if (crypto_field == 0) + { + // we don't allow any of the offered encryption levels + disconnect(errors::unsupported_encryption_mode_selected, 2); + return; + } + + if (crypto_field == pe_settings::plaintext) + m_rc4_encrypted = false; + else if (crypto_field == pe_settings::rc4) + m_rc4_encrypted = true; + } + + int len_pad = detail::read_int16(recv_buffer.begin); + if (len_pad < 0 || len_pad > 512) + { + disconnect(errors::invalid_pad_size, 2); + return; + } + + m_state = read_pe_pad; + if (!is_outgoing()) + reset_recv_buffer(len_pad + 2); // len(IA) at the end of pad + else + { + if (len_pad == 0) + { + m_encrypted = true; + m_state = init_bt_handshake; + } + else + reset_recv_buffer(len_pad); + } + } + + if (m_state == read_pe_pad) + { + TORRENT_ASSERT(!m_encrypted); + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + if (!packet_finished()) return; + + int pad_size = is_outgoing() ? packet_size() : packet_size() - 2; + + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + if (!is_outgoing()) + { + recv_buffer.begin += pad_size; + int len_ia = detail::read_int16(recv_buffer.begin); + + if (len_ia < 0) + { + disconnect(errors::invalid_encrypt_handshake, 2); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** len(IA) : %d", len_ia); +#endif + if (len_ia == 0) + { + // everything after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + else + { + m_state = read_pe_ia; + reset_recv_buffer(len_ia); + } + } + else // is_outgoing() + { + // everything that arrives after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + } + + if (m_state == read_pe_ia) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(!is_outgoing()); + TORRENT_ASSERT(!m_encrypted); + + if (!packet_finished()) return; + + // ia is always rc4, so decrypt it + buffer::interval wr_buf = wr_recv_buffer(); + m_enc_handler->decrypt(wr_buf.begin, packet_size()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** decrypted ia : %d bytes", packet_size()); +#endif + + if (!m_rc4_encrypted) + { + m_enc_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** destroyed rc4 keys"); +#endif + } + + // everything that arrives after this is encrypted + m_encrypted = true; + + m_state = read_protocol_identifier; + cut_receive_buffer(0, 20); + } + + if (m_state == init_bt_handshake) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(m_encrypted); + + // decrypt remaining received bytes + if (m_rc4_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + wr_buf.begin += packet_size(); + m_enc_handler->decrypt(wr_buf.begin, wr_buf.left()); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** decrypted remaining %d bytes", wr_buf.left()); +#endif + } + else // !m_rc4_encrypted + { + m_enc_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** destroyed encryption handler"); +#endif + } + + // payload stream, start with 20 handshake bytes + m_state = read_protocol_identifier; + reset_recv_buffer(20); + + // encrypted portion of handshake completed, toggle + // peer_info pe_support flag back to true + if (is_outgoing() && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi); + + pi->pe_support = true; + } + } + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + if (m_state == read_protocol_identifier) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + int packet_size = recv_buffer[0]; + const char protocol_string[] = "\x13" "BitTorrent protocol"; + + if (packet_size != 19 || + memcmp(recv_buffer.begin, protocol_string, 20) != 0) + { +#ifndef TORRENT_DISABLE_ENCRYPTION +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** unrecognized protocol header"); +#endif + +#ifdef TORRENT_USE_OPENSSL + if (is_ssl(*get_socket())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SSL peers are not allowed to use any other encryption"); +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } +#endif // TORRENT_USE_OPENSSL + + if (!is_outgoing() + && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) + { + disconnect(errors::no_incoming_encrypted); + return; + } + + // Don't attempt to perform an encrypted handshake + // within an encrypted connection. For local connections, + // we're expected to already have passed the encrypted + // handshake by this point + if (m_encrypted || is_outgoing()) + { + disconnect(errors::invalid_info_hash, 1); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** attempting encrypted connection"); +#endif + m_state = read_pe_dhkey; + cut_receive_buffer(0, dh_key_len); + TORRENT_ASSERT(!packet_finished()); + return; +#else + disconnect(errors::invalid_info_hash, 1); + return; +#endif // TORRENT_DISABLE_ENCRYPTION + } + else + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT(m_state != read_pe_dhkey); + + if (!is_outgoing() + && m_ses.get_pe_settings().in_enc_policy == pe_settings::forced + && !m_encrypted + && !is_ssl(*get_socket())) + { + disconnect(errors::no_incoming_regular); + return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== BitTorrent protocol"); +#endif + } + + m_state = read_info_hash; + reset_recv_buffer(28); + } + + // fall through + if (m_state == read_info_hash) + { + m_statistics.received_bytes(0, bytes_transferred); + bytes_transferred = 0; + TORRENT_ASSERT(packet_size() == 28); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + +#ifdef TORRENT_VERBOSE_LOGGING + std::string extensions; + extensions.resize(8 * 8); + for (int i=0; i < 8; ++i) + { + for (int j=0; j < 8; ++j) + { + if (recv_buffer[i] & (0x80 >> j)) extensions[i*8+j] = '1'; + else extensions[i*8+j] = '0'; + } + } + peer_log("<== EXTENSIONS [ %s ext: %s%s%s]" + , extensions.c_str() + , (recv_buffer[7] & 0x01) ? "DHT " : "" + , (recv_buffer[7] & 0x04) ? "FAST " : "" + , (recv_buffer[5] & 0x10) ? "extension " : ""); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + std::memcpy(m_reserved_bits, recv_buffer.begin, 8); + if ((recv_buffer[5] & 0x10)) + m_supports_extensions = true; +#endif + if (recv_buffer[7] & 0x01) + m_supports_dht_port = true; + + if (recv_buffer[7] & 0x04) + m_supports_fast = true; + + // ok, now we have got enough of the handshake. Is this connection + // attached to a torrent? + if (!t) + { + // now, we have to see if there's a torrent with the + // info_hash we got from the peer + sha1_hash info_hash; + std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (char*)info_hash.begin()); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool allow_encrypted = m_encrypted && m_rc4_encrypted; +#else + bool allow_encrypted = true; +#endif + + attach_to_torrent(info_hash, allow_encrypted); + if (is_disconnecting()) return; + } + else + { + // verify info hash + if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (const char*)t->torrent_file().info_hash().begin())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** received invalid info_hash"); +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< info_hash received"); +#endif + } + + t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // if this is a local connection, we have already + // sent the handshake + if (!is_outgoing()) write_handshake(); +// if (t->valid_metadata()) +// write_bitfield(); + TORRENT_ASSERT(m_sent_handshake); + + if (is_disconnecting()) return; + + TORRENT_ASSERT(t->get_policy().has_connection(this)); + + m_state = read_peer_id; + reset_recv_buffer(20); + } + + // fall through + if (m_state == read_peer_id) + { + TORRENT_ASSERT(m_sent_handshake); + m_statistics.received_bytes(0, bytes_transferred); +// bytes_transferred = 0; + if (!t) + { + TORRENT_ASSERT(!packet_finished()); // TODO + return; + } + TORRENT_ASSERT(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + +#ifdef TORRENT_VERBOSE_LOGGING + { + char hex_pid[41]; + to_hex(recv_buffer.begin, 20, hex_pid); + hex_pid[40] = 0; + char ascii_pid[21]; + ascii_pid[20] = 0; + for (int i = 0; i != 20; ++i) + { + if (is_print(recv_buffer.begin[i])) ascii_pid[i] = recv_buffer.begin[i]; + else ascii_pid[i] = '.'; + } + peer_log("<<< received peer_id: %s client: %s ascii: \"%s\"" + , hex_pid, identify_client(peer_id(recv_buffer.begin)).c_str(), ascii_pid); + } +#endif + peer_id pid; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin()); + set_pid(pid); + + if (t->settings().allow_multiple_connections_per_ip) + { + // now, let's see if this connection should be closed + policy& p = t->get_policy(); + policy::iterator i = std::find_if(p.begin_peer(), p.end_peer() + , match_peer_id(pid, this)); + if (i != p.end_peer()) + { + TORRENT_ASSERT((*i)->connection->pid() == pid); + // we found another connection with the same peer-id + // which connection should be closed in order to be + // sure that the other end closes the same connection? + // the peer with greatest peer-id is the one allowed to + // initiate connections. So, if our peer-id is greater than + // the others, we should close the incoming connection, + // if not, we should close the outgoing one. + if (pid < m_our_peer_id && is_outgoing()) + { + (*i)->connection->disconnect(errors::duplicate_peer_id); + } + else + { + disconnect(errors::duplicate_peer_id); + return; + } + } + } + + // disconnect if the peer has the same peer-id as ourself + // since it most likely is ourself then + if (pid == m_our_peer_id) + { + if (peer_info_struct()) t->get_policy().ban_peer(peer_info_struct()); + disconnect(errors::self_connection, 1); + return; + } + + m_client_version = identify_client(pid); + boost::optional f = client_fingerprint(pid); + if (f && std::equal(f->name, f->name + 2, "BC")) + { + // if this is a bitcomet client, lower the request queue size limit + if (max_out_request_queue() > 50) max_out_request_queue(50); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end;) + { + if (!(*i)->on_handshake(m_reserved_bits)) + { + i = m_extensions.erase(i); + } + else + { + ++i; + } + } + if (is_disconnecting()) return; + + if (m_supports_extensions) write_extensions(); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HANDSHAKE"); +#endif + // consider this a successful connection, reset the failcount + if (peer_info_struct()) t->get_policy().set_failcount(peer_info_struct(), 0); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Toggle pe_support back to false if this is a + // standard successful connection + if (is_outgoing() && !m_encrypted && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi); + + pi->pe_support = false; + } +#endif + + m_state = read_packet_size; + reset_recv_buffer(5); + if (t->ready_for_connections()) + { + write_bitfield(); +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.m_external_udp_port); +#endif + } + + TORRENT_ASSERT(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_packet_size) + { + // Make sure this is not fallen though into + TORRENT_ASSERT(recv_buffer == receive_buffer()); + TORRENT_ASSERT(packet_size() == 5); + + if (!t) return; + + if (recv_buffer.left() < 4) + { + m_statistics.received_bytes(0, bytes_transferred); + return; + } + int transferred_used = 4 - recv_buffer.left() + bytes_transferred; + TORRENT_ASSERT(transferred_used <= int(bytes_transferred)); + m_statistics.received_bytes(0, transferred_used); + bytes_transferred -= transferred_used; + + const char* ptr = recv_buffer.begin; + int packet_size = detail::read_int32(ptr); + + // don't accept packets larger than 1 MB + if (packet_size > 1024*1024 || packet_size < 0) + { + m_statistics.received_bytes(0, bytes_transferred); + // packet too large + disconnect(errors::packet_too_large, 2); + return; + } + + if (packet_size == 0) + { + m_statistics.received_bytes(0, bytes_transferred); + incoming_keepalive(); + if (is_disconnecting()) return; + // keepalive message + m_state = read_packet_size; + cut_receive_buffer(4, 5); + return; + } + else + { + if (recv_buffer.left() < 5) return; + + m_state = read_packet; + cut_receive_buffer(4, packet_size); + TORRENT_ASSERT(bytes_transferred == 1); + recv_buffer = receive_buffer(); + TORRENT_ASSERT(recv_buffer.left() == 1); + } + } + + if (m_state == read_packet) + { + TORRENT_ASSERT(recv_buffer == receive_buffer()); + if (!t) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::torrent_removed, 1); + return; + } +#ifdef TORRENT_DEBUG + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + if (dispatch_message(bytes_transferred)) + { + m_state = read_packet_size; + reset_recv_buffer(5); + } +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == size_type(bytes_transferred)); +#endif + TORRENT_ASSERT(!packet_finished()); + return; + } + + TORRENT_ASSERT(!packet_finished()); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + void bt_peer_connection::on_sent(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.sent_bytes(0, bytes_transferred); + return; + } + + // manage the payload markers + int amount_payload = 0; + if (!m_payloads.empty()) + { + for (std::vector::iterator i = m_payloads.begin(); + i != m_payloads.end(); ++i) + { + i->start -= bytes_transferred; + if (i->start < 0) + { + if (i->start + i->length <= 0) + { + amount_payload += i->length; + } + else + { + amount_payload += -i->start; + i->length -= -i->start; + i->start = 0; + } + } + } + } + + // TODO: move the erasing into the loop above + // remove all payload ranges that has been sent + m_payloads.erase( + std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero) + , m_payloads.end()); + + TORRENT_ASSERT(amount_payload <= (int)bytes_transferred); + m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload); + + if (amount_payload > 0) + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + if (t) t->update_last_upload(); + } + } + +#if TORRENT_USE_INVARIANT_CHECKS + void bt_peer_connection::check_invariant() const + { + boost::shared_ptr t = associated_torrent().lock(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_dh_key_exchange.get()) + || !is_outgoing()); + + TORRENT_ASSERT(!m_rc4_encrypted || m_enc_handler.get()); +#endif + if (!in_handshake()) + { + TORRENT_ASSERT(m_sent_handshake); + } + + if (!m_payloads.empty()) + { + for (std::vector::const_iterator i = m_payloads.begin(); + i != m_payloads.end() - 1; ++i) + { + TORRENT_ASSERT(i->start + i->length <= (i+1)->start); + } + } + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp b/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp new file mode 100644 index 0000000000..5fb8eb33d0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/chained_buffer.cpp @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/chained_buffer.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + void chained_buffer::pop_front(int bytes_to_pop) + { + TORRENT_ASSERT(bytes_to_pop <= m_bytes); + while (bytes_to_pop > 0 && !m_vec.empty()) + { + buffer_t& b = m_vec.front(); + if (b.used_size > bytes_to_pop) + { + b.start += bytes_to_pop; + b.used_size -= bytes_to_pop; + m_bytes -= bytes_to_pop; + TORRENT_ASSERT(m_bytes <= m_capacity); + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + break; + } + + b.free(b.buf); + m_bytes -= b.used_size; + m_capacity -= b.size; + bytes_to_pop -= b.used_size; + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + TORRENT_ASSERT(m_bytes <= m_capacity); + m_vec.pop_front(); + } + } + + void chained_buffer::append_buffer(char* buffer, int s, int used_size + , boost::function const& destructor) + { + TORRENT_ASSERT(s >= used_size); + buffer_t b; + b.buf = buffer; + b.size = s; + b.start = buffer; + b.used_size = used_size; + b.free = destructor; + m_vec.push_back(b); + + m_bytes += used_size; + m_capacity += s; + TORRENT_ASSERT(m_bytes <= m_capacity); + } + + // returns the number of bytes available at the + // end of the last chained buffer. + int chained_buffer::space_in_last_buffer() + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + return b.size - b.used_size - (b.start - b.buf); + } + + // tries to copy the given buffer to the end of the + // last chained buffer. If there's not enough room + // it returns false + char* chained_buffer::append(char const* buf, int s) + { + char* insert = allocate_appendix(s); + if (insert == 0) return 0; + memcpy(insert, buf, s); + return insert; + } + + // tries to allocate memory from the end + // of the last buffer. If there isn't + // enough room, returns 0 + char* chained_buffer::allocate_appendix(int s) + { + if (m_vec.empty()) return 0; + buffer_t& b = m_vec.back(); + char* insert = b.start + b.used_size; + if (insert + s > b.buf + b.size) return 0; + b.used_size += s; + m_bytes += s; + TORRENT_ASSERT(m_bytes <= m_capacity); + return insert; + } + + std::list const& chained_buffer::build_iovec(int to_send) + { + m_tmp_vec.clear(); + + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); to_send > 0 && i != end; ++i) + { + if (i->used_size > to_send) + { + TORRENT_ASSERT(to_send > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, to_send)); + break; + } + TORRENT_ASSERT(i->used_size > 0); + m_tmp_vec.push_back(asio::const_buffer(i->start, i->used_size)); + to_send -= i->used_size; + } + return m_tmp_vec; + } + + chained_buffer::~chained_buffer() + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(!m_destructed); + m_destructed = true; +#endif + TORRENT_ASSERT(m_bytes >= 0); + TORRENT_ASSERT(m_capacity >= 0); + for (std::list::iterator i = m_vec.begin() + , end(m_vec.end()); i != end; ++i) + { + i->free(i->buf); + } +#ifdef TORRENT_DEBUG + m_bytes = -1; + m_capacity = -1; + m_vec.clear(); +#endif + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/connection_queue.cpp b/apps/Launcher/ext/libtorrent/src/connection_queue.cpp new file mode 100644 index 0000000000..e34d72551a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/connection_queue.cpp @@ -0,0 +1,350 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/error.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent +{ + + connection_queue::connection_queue(io_service& ios): m_next_ticket(0) + , m_num_connecting(0) + , m_half_open_limit(0) + , m_abort(false) + , m_num_timers(0) + , m_timer(ios) +#ifdef TORRENT_DEBUG + , m_in_timeout_function(false) +#endif + { +#ifdef TORRENT_CONNECTION_LOGGING + m_log.open("connection_queue.log"); +#endif + } + + int connection_queue::free_slots() const + { + mutex_t::scoped_lock l(m_mutex); + return m_half_open_limit == 0 ? (std::numeric_limits::max)() + : m_half_open_limit - m_queue.size(); + } + + void connection_queue::enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout, int priority) + { + mutex_t::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(priority < 3); + + entry* e = 0; + + if (priority <= 0) + { + m_queue.push_back(entry()); + e = &m_queue.back(); + } + else // priority > 0 + { + m_queue.push_front(entry()); + e = &m_queue.front(); + } + + e->priority = priority; + e->on_connect = on_connect; + e->on_timeout = on_timeout; + e->ticket = m_next_ticket; + e->timeout = timeout; + ++m_next_ticket; + + if (m_next_ticket >= (1 << 29)) + m_next_ticket = 0; + + if (m_num_connecting < m_half_open_limit + || m_half_open_limit == 0) + m_timer.get_io_service().post(boost::bind( + &connection_queue::on_try_connect, this)); + } + + bool connection_queue::done(int ticket) + { + mutex_t::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::ticket, _1) == ticket); + if (i == m_queue.end()) + { + // this might not be here in case on_timeout calls remove + return false; + } + if (i->connecting) --m_num_connecting; + m_queue.erase(i); + + if (m_num_connecting < m_half_open_limit + || m_half_open_limit == 0) + m_timer.get_io_service().post(boost::bind( + &connection_queue::on_try_connect, this)); + return true; + } + + void connection_queue::close() + { + error_code ec; + mutex_t::scoped_lock l(m_mutex); + if (m_num_connecting == 0) m_timer.cancel(ec); + m_abort = true; + + std::list tmp; + tmp.swap(m_queue); + m_num_connecting = 0; + + // we don't want to call the timeout callback while we're locked + // since that is a recipie for dead-locks + l.unlock(); + + while (!tmp.empty()) + { + entry& e = tmp.front(); + if (e.priority > 1) + { + mutex_t::scoped_lock ll(m_mutex); + if (e.connecting) ++m_num_connecting; + m_queue.push_back(e); + tmp.pop_front(); + continue; + } + TORRENT_TRY { + if (e.connecting) e.on_timeout(); + else e.on_connect(-1); + } TORRENT_CATCH(std::exception&) {} + tmp.pop_front(); + } + } + + void connection_queue::limit(int limit) + { + TORRENT_ASSERT(limit >= 0); + m_half_open_limit = limit; + } + + int connection_queue::limit() const + { return m_half_open_limit; } + +#if TORRENT_USE_INVARIANT_CHECKS + void connection_queue::check_invariant() const + { + int num_connecting = 0; + for (std::list::const_iterator i = m_queue.begin(); + i != m_queue.end(); ++i) + { + if (i->connecting) ++num_connecting; + else TORRENT_ASSERT(i->expires == max_time()); + } + TORRENT_ASSERT(num_connecting == m_num_connecting); + } + +#endif + + void connection_queue::try_connect(connection_queue::mutex_t::scoped_lock& l) + { + INVARIANT_CHECK; + +#ifdef TORRENT_CONNECTION_LOGGING + m_log << log_time() << " " << free_slots() << std::endl; +#endif + // if this is enabled, UPnP connections will be blocked when shutting down +// if (m_abort) return; + + if (m_num_connecting >= m_half_open_limit + && m_half_open_limit > 0) return; + + if (m_queue.empty()) + { + error_code ec; + m_timer.cancel(ec); + return; + } + + // all entries are connecting, no need to look for new ones + if (int(m_queue.size()) == m_num_connecting) + return; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::connecting, _1) == false); + + std::list to_connect; + + while (i != m_queue.end()) + { + TORRENT_ASSERT(i->connecting == false); + ptime expire = time_now_hires() + i->timeout; + if (m_num_connecting == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("connection_queue::on_timeout"); +#endif + error_code ec; + m_timer.expires_at(expire, ec); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; + } + i->connecting = true; + ++m_num_connecting; + i->expires = expire; + + INVARIANT_CHECK; + + to_connect.push_back(*i); + +#ifdef TORRENT_CONNECTION_LOGGING + m_log << log_time() << " " << free_slots() << std::endl; +#endif + + if (m_num_connecting >= m_half_open_limit + && m_half_open_limit > 0) break; + if (m_num_connecting == int(m_queue.size())) break; + i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false); + } + + l.unlock(); + + while (!to_connect.empty()) + { + entry& ent = to_connect.front(); + TORRENT_TRY { + ent.on_connect(ent.ticket); + } TORRENT_CATCH(std::exception&) {} + to_connect.pop_front(); + } + + } + +#ifdef TORRENT_DEBUG + struct function_guard + { + function_guard(bool& v): val(v) { TORRENT_ASSERT(!val); val = true; } + ~function_guard() { val = false; } + + bool& val; + }; +#endif + + void connection_queue::on_timeout(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("connection_queue::on_timeout"); +#endif + mutex_t::scoped_lock l(m_mutex); + --m_num_timers; + + INVARIANT_CHECK; +#ifdef TORRENT_DEBUG + function_guard guard_(m_in_timeout_function); +#endif + + TORRENT_ASSERT(!e || e == error::operation_aborted); + + // if there was an error, it's most likely operation aborted, + // we should just quit. However, in case there are still connections + // in connecting state, and there are no other timer invocations + // we need to stick around still. + if (e && (m_num_connecting == 0 || m_num_timers > 0)) return; + + ptime next_expire = max_time(); + ptime now = time_now_hires() + milliseconds(100); + std::list timed_out; + for (std::list::iterator i = m_queue.begin(); + !m_queue.empty() && i != m_queue.end();) + { + if (i->connecting && i->expires < now) + { + std::list::iterator j = i; + ++i; + timed_out.splice(timed_out.end(), m_queue, j, i); + --m_num_connecting; + continue; + } + if (i->connecting && i->expires < next_expire) + next_expire = i->expires; + ++i; + } + + // we don't want to call the timeout callback while we're locked + // since that is a recepie for dead-locks + l.unlock(); + + for (std::list::iterator i = timed_out.begin() + , end(timed_out.end()); i != end; ++i) + { + TORRENT_ASSERT(i->connecting); + TORRENT_ASSERT(i->ticket != -1); + TORRENT_TRY { + i->on_timeout(); + } TORRENT_CATCH(std::exception&) {} + } + + l.lock(); + + if (next_expire < max_time()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("connection_queue::on_timeout"); +#endif + error_code ec; + m_timer.expires_at(next_expire, ec); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; + } + try_connect(l); + } + + void connection_queue::on_try_connect() + { + mutex_t::scoped_lock l(m_mutex); + try_connect(l); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/create_torrent.cpp b/apps/Launcher/ext/libtorrent/src/create_torrent.cpp new file mode 100644 index 0000000000..c814be8dbb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/create_torrent.cpp @@ -0,0 +1,657 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/create_torrent.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/torrent_info.hpp" // for merkle_*() + +#include +#include + +#include +#include + +#define MAX_SYMLINK_PATH 200 + +namespace libtorrent +{ + + namespace detail + { + int get_file_attributes(std::string const& p) + { +#ifdef TORRENT_WINDOWS + +#if TORRENT_USE_WSTRING + std::wstring path = convert_to_wstring(p); + DWORD attr = GetFileAttributesW(path.c_str()); +#else + std::string path = convert_to_native(p); + DWORD attr = GetFileAttributesA(path.c_str()); +#endif // TORRENT_USE_WSTRING + if (attr == INVALID_FILE_ATTRIBUTES) return 0; + if (attr & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden; + return 0; +#else + struct stat s; + if (lstat(convert_to_native(p).c_str(), &s) < 0) return 0; + int file_attr = 0; + if (s.st_mode & S_IXUSR) + file_attr += file_storage::attribute_executable; + if (S_ISLNK(s.st_mode)) + file_attr += file_storage::attribute_symlink; + return file_attr; +#endif + } + +#ifndef TORRENT_WINDOWS + std::string get_symlink_path_impl(char const* path) + { + char buf[MAX_SYMLINK_PATH]; + std::string f = convert_to_native(path); + int char_read = readlink(f.c_str(),buf,MAX_SYMLINK_PATH); + if (char_read < 0) return ""; + if (char_read < MAX_SYMLINK_PATH) buf[char_read] = 0; + else buf[0] = 0; + return convert_from_native(buf); + } +#endif + + std::string get_symlink_path(std::string const& p) + { +#if defined TORRENT_WINDOWS + return ""; +#else + std::string path = convert_to_native(p); + return get_symlink_path_impl(p.c_str()); +#endif + } + + void add_files_impl(file_storage& fs, std::string const& p + , std::string const& l, boost::function pred, boost::uint32_t flags) + { + std::string f = combine_path(p, l); + if (!pred(f)) return; + error_code ec; + file_status s; + stat_file(f, &s, ec, (flags & create_torrent::symlinks) ? dont_follow_links : 0); + if (ec) return; + + // recurse into directories + bool recurse = (s.mode & file_status::directory) != 0; + + // if the file is not a link or we're following links, and it's a directory + // only then should we recurse +#ifndef TORRENT_WINDOWS + if ((s.mode & file_status::link) && (flags & create_torrent::symlinks)) + recurse = false; +#endif + + if (recurse) + { + for (directory i(f, ec); !i.done(); i.next(ec)) + { + std::string leaf = i.file(); + if (ignore_subdir(leaf)) continue; + add_files_impl(fs, p, combine_path(l, leaf), pred, flags); + } + } + else + { + // #error use the fields from s + int file_flags = get_file_attributes(f); + + // mask all bits to check if the file is a symlink + if ((file_flags & file_storage::attribute_symlink) + && (flags & create_torrent::symlinks)) + { + std::string sym_path = get_symlink_path(f); + fs.add_file(l, 0, file_flags, s.mtime, sym_path); + } + else + { + fs.add_file(l, s.file_size, file_flags, s.mtime); + } + } + } + } + + struct piece_holder + { + piece_holder(int bytes): m_piece(page_aligned_allocator::malloc(bytes)) {} + ~piece_holder() { page_aligned_allocator::free(m_piece); } + char* bytes() { return m_piece; } + private: + char* m_piece; + }; + +#if TORRENT_USE_WSTRING + void set_piece_hashes(create_torrent& t, std::wstring const& p + , boost::function const& f, error_code& ec) + { + file_pool fp; + std::string utf8; + wchar_utf8(p, utf8); +#if TORRENT_USE_UNC_PATHS + utf8 = canonicalize_path(utf8); +#endif + boost::scoped_ptr st( + default_storage_constructor(const_cast(t.files()), 0, utf8, fp + , std::vector())); + + // calculate the hash for all pieces + int num = t.num_pieces(); + std::vector buf(t.piece_length()); + for (int i = 0; i < num; ++i) + { + // read hits the disk and will block. Progress should + // be updated in between reads + st->read(&buf[0], i, 0, t.piece_size(i)); + if (st->error()) + { + ec = st->error(); + return; + } + hasher h(&buf[0], t.piece_size(i)); + t.set_hash(i, h.final()); + f(i); + } + } +#endif + + void set_piece_hashes(create_torrent& t, std::string const& p + , boost::function f, error_code& ec) + { + file_pool fp; +#if TORRENT_USE_UNC_PATHS + std::string path = canonicalize_path(p); +#else + std::string const& path = p; +#endif + + if (t.files().num_files() == 0) + { + ec = error_code(errors::no_files_in_torrent, get_libtorrent_category()); + return; + } + + boost::scoped_ptr st( + default_storage_constructor(const_cast(t.files()), 0, path, fp + , std::vector())); + + // if we're calculating file hashes as well, use this hasher + hasher filehash; + int file_idx = 0; + size_type left_in_file = t.files().at(0).size; + + // calculate the hash for all pieces + int num = t.num_pieces(); + piece_holder buf(t.piece_length()); + for (int i = 0; i < num; ++i) + { + // read hits the disk and will block. Progress should + // be updated in between reads + st->read(buf.bytes(), i, 0, t.piece_size(i)); + if (st->error()) + { + ec = st->error(); + return; + } + + if (t.should_add_file_hashes()) + { + int left_in_piece = t.piece_size(i); + int this_piece_size = left_in_piece; + // the number of bytes from this file we just read + while (left_in_piece > 0) + { + int to_hash_for_file = int((std::min)(size_type(left_in_piece), left_in_file)); + if (to_hash_for_file > 0) + { + int offset = this_piece_size - left_in_piece; + filehash.update(buf.bytes() + offset, to_hash_for_file); + } + left_in_file -= to_hash_for_file; + left_in_piece -= to_hash_for_file; + if (left_in_file == 0) + { + if (!t.files().at(file_idx).pad_file) + t.set_file_hash(file_idx, filehash.final()); + filehash.reset(); + file_idx++; + if (file_idx >= t.files().num_files()) break; + left_in_file = t.files().at(file_idx).size; + } + } + } + + hasher h(buf.bytes(), t.piece_size(i)); + t.set_hash(i, h.final()); + f(i); + } + } + + create_torrent::~create_torrent() {} + + create_torrent::create_torrent(file_storage& fs, int piece_size + , int pad_file_limit, int flags, int alignment) + : m_files(fs) + , m_creation_date(time(0)) + , m_multifile(fs.num_files() > 1) + , m_private(false) + , m_merkle_torrent((flags & merkle) != 0) + , m_include_mtime((flags & modification_time) != 0) + , m_include_symlinks((flags & symlinks) != 0) + , m_calculate_file_hashes((flags & calculate_file_hashes) != 0) + { + TORRENT_ASSERT(fs.num_files() > 0); + + // return instead of crash in release mode + if (fs.num_files() == 0) return; + + if (!m_multifile && has_parent_path(m_files.file_path(0))) m_multifile = true; + + // a piece_size of 0 means automatic + if (piece_size == 0 && !m_merkle_torrent) + { + const int target_size = 40 * 1024; + piece_size = int(fs.total_size() / (target_size / 20)); + + int i = 16*1024; + for (; i < 2*1024*1024; i *= 2) + { + if (piece_size > i) continue; + break; + } + piece_size = i; + } + else if (piece_size == 0 && m_merkle_torrent) + { + piece_size = 64*1024; + } + + // make sure the size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (piece_size & (1 << i)) + { + TORRENT_ASSERT((piece_size & ~(1 << i)) == 0); + break; + } + } +#endif + m_files.set_piece_length(piece_size); + if (flags & optimize) + m_files.optimize(pad_file_limit, alignment); + m_files.set_num_pieces(static_cast( + (m_files.total_size() + m_files.piece_length() - 1) / m_files.piece_length())); + m_piece_hash.resize(m_files.num_pieces()); + } + + create_torrent::create_torrent(torrent_info const& ti) + : m_files(const_cast(ti.files())) + , m_creation_date(time(0)) + , m_multifile(ti.num_files() > 1) + , m_private(ti.priv()) + , m_merkle_torrent(ti.is_merkle_torrent()) + , m_include_mtime(false) + , m_include_symlinks(false) + , m_calculate_file_hashes(false) + { + TORRENT_ASSERT(ti.is_valid()); + if (ti.creation_date()) m_creation_date = *ti.creation_date(); + + if (!ti.creator().empty()) set_creator(ti.creator().c_str()); + if (!ti.comment().empty()) set_comment(ti.comment().c_str()); + + torrent_info::nodes_t const& nodes = ti.nodes(); + for (torrent_info::nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + add_node(*i); + + std::vector const& trackers = ti.trackers(); + for (std::vector::const_iterator i = trackers.begin() + , end(trackers.end()); i != end; ++i) + add_tracker(i->url, i->tier); + + std::vector const& web_seeds = ti.web_seeds(); + for (std::vector::const_iterator i = web_seeds.begin() + , end(web_seeds.end()); i != end; ++i) + { + if (i->type == web_seed_entry::url_seed) + add_url_seed(i->url); + else if (i->type == web_seed_entry::http_seed) + add_http_seed(i->url); + } + + m_piece_hash.resize(m_files.num_pieces()); + for (int i = 0; i < num_pieces(); ++i) set_hash(i, ti.hash_for_piece(i)); + + m_info_dict = bdecode(&ti.metadata()[0], &ti.metadata()[0] + ti.metadata_size()); + m_info_hash = ti.info_hash(); + } + + entry create_torrent::generate() const + { + TORRENT_ASSERT(m_files.piece_length() > 0); + + entry dict; + + if (m_files.num_files() == 0) + return dict; + + if (!m_urls.empty()) dict["announce"] = m_urls.front().first; + + if (!m_nodes.empty()) + { + entry& nodes = dict["nodes"]; + entry::list_type& nodes_list = nodes.list(); + for (nodes_t::const_iterator i = m_nodes.begin() + , end(m_nodes.end()); i != end; ++i) + { + entry::list_type node; + node.push_back(entry(i->first)); + node.push_back(entry(i->second)); + nodes_list.push_back(entry(node)); + } + } + + if (m_urls.size() > 1) + { + entry trackers(entry::list_t); + entry tier(entry::list_t); + int current_tier = m_urls.front().second; + for (std::vector::const_iterator i = m_urls.begin(); + i != m_urls.end(); ++i) + { + if (i->second != current_tier) + { + current_tier = i->second; + trackers.list().push_back(tier); + tier.list().clear(); + } + tier.list().push_back(entry(i->first)); + } + trackers.list().push_back(tier); + dict["announce-list"] = trackers; + } + + if (!m_comment.empty()) + dict["comment"] = m_comment; + + dict["creation date"] = m_creation_date; + + if (!m_created_by.empty()) + dict["created by"] = m_created_by; + + if (!m_url_seeds.empty()) + { + if (m_url_seeds.size() == 1) + { + dict["url-list"] = m_url_seeds.front(); + } + else + { + entry& list = dict["url-list"]; + for (std::vector::const_iterator i + = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + if (!m_http_seeds.empty()) + { + if (m_http_seeds.size() == 1) + { + dict["httpseeds"] = m_http_seeds.front(); + } + else + { + entry& list = dict["httpseeds"]; + for (std::vector::const_iterator i + = m_http_seeds.begin(); i != m_http_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + entry& info = dict["info"]; + if (m_info_dict.type() == entry::dictionary_t) + { + info = m_info_dict; + return dict; + } + + info["name"] = m_files.name(); + + if (!m_root_cert.empty()) + info["ssl-cert"] = m_root_cert; + + if (m_private) info["private"] = 1; + + if (!m_multifile) + { + file_entry e = m_files.at(0); + if (m_include_mtime) info["mtime"] = e.mtime; + info["length"] = e.size; + if (e.pad_file + || e.hidden_attribute + || e.executable_attribute + || e.symlink_attribute) + { + std::string& attr = info["attr"].string(); + if (e.pad_file) attr += 'p'; + if (e.hidden_attribute) attr += 'h'; + if (e.executable_attribute) attr += 'x'; + if (m_include_symlinks && e.symlink_attribute) attr += 'l'; + } + if (m_include_symlinks + && e.symlink_attribute) + { + entry& sympath_e = info["symlink path"]; + + std::string split = split_path(e.symlink_path); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + sympath_e.list().push_back(entry(e)); + } + if (!m_filehashes.empty()) + { + info["sha1"] = m_filehashes[0].to_string(); + } + } + else + { + if (!info.find_key("files")) + { + entry& files = info["files"]; + + for (int i = 0; i < m_files.num_files(); ++i) + { + files.list().push_back(entry()); + entry& file_e = files.list().back(); + if (m_include_mtime && m_files.mtime(i)) file_e["mtime"] = m_files.mtime(i); + file_e["length"] = m_files.file_size(i); + entry& path_e = file_e["path"]; + + TORRENT_ASSERT(has_parent_path(m_files.file_path(i))); + + std::string split = split_path(m_files.file_path(i)); + TORRENT_ASSERT(split.c_str() == m_files.name()); + + for (char const* e = next_path_element(split.c_str()); + e != 0; e = next_path_element(e)) + path_e.list().push_back(entry(e)); + + int flags = m_files.file_flags(i); + if (flags != 0) + { + std::string& attr = file_e["attr"].string(); + if (flags & file_storage::flag_pad_file) attr += 'p'; + if (flags & file_storage::flag_hidden) attr += 'h'; + if (flags & file_storage::flag_executable) attr += 'x'; + if (m_include_symlinks && (flags & file_storage::flag_symlink)) attr += 'l'; + } + + if (m_include_symlinks + && (flags & file_storage::flag_symlink)) + { + entry& sympath_e = file_e["symlink path"]; + + std::string split = split_path(m_files.symlink(i)); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + sympath_e.list().push_back(entry(e)); + } + if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash()) + { + file_e["sha1"] = m_filehashes[i].to_string(); + } + } + } + } + + info["piece length"] = m_files.piece_length(); + if (m_merkle_torrent) + { + int num_leafs = merkle_num_leafs(m_files.num_pieces()); + int num_nodes = merkle_num_nodes(num_leafs); + int first_leaf = num_nodes - num_leafs; + m_merkle_tree.resize(num_nodes); + int num_pieces = m_piece_hash.size(); + for (int i = 0; i < num_pieces; ++i) + m_merkle_tree[first_leaf + i] = m_piece_hash[i]; + sha1_hash filler(0); + for (int i = num_pieces; i < num_leafs; ++i) + m_merkle_tree[first_leaf + i] = filler; + + // now that we have initialized all leaves, build + // each level bottom-up + int level_start = first_leaf; + int level_size = num_leafs; + while (level_start > 0) + { + int parent = merkle_get_parent(level_start); + for (int i = level_start; i < level_start + level_size; i += 2, ++parent) + { + hasher h; + h.update((char const*)&m_merkle_tree[i][0], 20); + h.update((char const*)&m_merkle_tree[i+1][0], 20); + m_merkle_tree[parent] = h.final(); + } + level_start = merkle_get_parent(level_start); + level_size /= 2; + } + TORRENT_ASSERT(level_size == 1); + std::string& p = info["root hash"].string(); + p.assign((char const*)&m_merkle_tree[0][0], 20); + } + else + { + std::string& p = info["pieces"].string(); + + for (std::vector::const_iterator i = m_piece_hash.begin(); + i != m_piece_hash.end(); ++i) + { + p.append((char*)i->begin(), sha1_hash::size); + } + } + + std::vector buf; + bencode(std::back_inserter(buf), info); + m_info_hash = hasher(&buf[0], buf.size()).final(); + + return dict; + + } + + void create_torrent::add_tracker(std::string const& url, int tier) + { + m_urls.push_back(announce_entry(url, tier)); + + std::sort(m_urls.begin(), m_urls.end() + , boost::bind(&announce_entry::second, _1) < boost::bind(&announce_entry::second, _2)); + } + + void create_torrent::set_root_cert(std::string const& cert) + { + m_root_cert = cert; + } + + void create_torrent::set_hash(int index, sha1_hash const& h) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_hash.size()); + m_piece_hash[index] = h; + } + + void create_torrent::set_file_hash(int index, sha1_hash const& h) + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_files.num_files()); + if (m_filehashes.empty()) m_filehashes.resize(m_files.num_files()); + m_filehashes[index] = h; + } + + void create_torrent::add_node(std::pair const& node) + { + m_nodes.push_back(node); + } + + void create_torrent::add_url_seed(std::string const& url) + { + m_url_seeds.push_back(url); + } + + void create_torrent::add_http_seed(std::string const& url) + { + m_http_seeds.push_back(url); + } + + void create_torrent::set_comment(char const* str) + { + if (str == 0) m_comment.clear(); + else m_comment = str; + } + + void create_torrent::set_creator(char const* str) + { + if (str == 0) m_created_by.clear(); + else m_created_by = str; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp b/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp new file mode 100644 index 0000000000..02ac19d511 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_buffer_holder.cpp @@ -0,0 +1,70 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/disk_io_thread.hpp" + +namespace libtorrent +{ + + disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf) + : m_disk_pool(ses.m_disk_thread), m_buf(buf) + { + TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); + } + + disk_buffer_holder::disk_buffer_holder(disk_buffer_pool& iothread, char* buf) + : m_disk_pool(iothread), m_buf(buf) + { + TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); + } + + void disk_buffer_holder::reset(char* buf) + { + if (m_buf) m_disk_pool.free_buffer(m_buf); + m_buf = buf; + } + + char* disk_buffer_holder::release() + { + char* ret = m_buf; + m_buf = 0; + return ret; + } + + disk_buffer_holder::~disk_buffer_holder() + { + if (m_buf) m_disk_pool.free_buffer(m_buf); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp b/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp new file mode 100644 index 0000000000..0f3efb7121 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_buffer_pool.cpp @@ -0,0 +1,242 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/disk_buffer_pool.hpp" +#include "libtorrent/assert.hpp" +#include + +#if TORRENT_USE_MLOCK && !defined TORRENT_WINDOWS +#include +#endif + +#ifdef TORRENT_DISK_STATS +#include "libtorrent/time.hpp" +#endif + +namespace libtorrent +{ + disk_buffer_pool::disk_buffer_pool(int block_size) + : m_block_size(block_size) + , m_in_use(0) +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + , m_using_pool_allocator(false) + , m_pool(block_size, m_settings.cache_buffer_chunk_size) +#endif + { +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + m_allocations = 0; +#endif +#ifdef TORRENT_DISK_STATS + m_log.open("disk_buffers.log", std::ios::trunc); + m_categories["read cache"] = 0; + m_categories["write cache"] = 0; + + m_disk_access_log.open("disk_access.log", std::ios::trunc); +#endif +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; +#endif + } + +#if TORRENT_USE_ASSERTS + disk_buffer_pool::~disk_buffer_pool() + { + TORRENT_ASSERT(m_magic == 0x1337); + m_magic = 0; + } +#endif + +#if TORRENT_USE_ASSERTS || defined TORRENT_DISK_STATS + bool disk_buffer_pool::is_disk_buffer(char* buffer + , mutex::scoped_lock& l) const + { + TORRENT_ASSERT(m_magic == 0x1337); +#ifdef TORRENT_DISK_STATS + if (m_buf_to_category.find(buffer) + == m_buf_to_category.end()) return false; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + return true; +#else + if (m_using_pool_allocator) + return m_pool.is_from(buffer); + else + return true; +#endif + } + + bool disk_buffer_pool::is_disk_buffer(char* buffer) const + { + mutex::scoped_lock l(m_pool_mutex); + return is_disk_buffer(buffer, l); + } +#endif + + char* disk_buffer_pool::allocate_buffer(char const* category) + { + mutex::scoped_lock l(m_pool_mutex); + TORRENT_ASSERT(m_magic == 0x1337); + char* ret; +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + ret = page_aligned_allocator::malloc(m_block_size); +#else + if (m_using_pool_allocator) + { + ret = (char*)m_pool.malloc(); + m_pool.set_next_size(m_settings.cache_buffer_chunk_size); + } + else + { + ret = page_aligned_allocator::malloc(m_block_size); + } +#endif + ++m_in_use; +#if TORRENT_USE_MLOCK + if (m_settings.lock_disk_cache) + { +#ifdef TORRENT_WINDOWS + VirtualLock(ret, m_block_size); +#else + mlock(ret, m_block_size); +#endif + } +#endif + +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + ++m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + ++m_categories[category]; + m_buf_to_category[ret] = category; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; +#endif + TORRENT_ASSERT(ret == 0 || is_disk_buffer(ret, l)); + return ret; + } + +#ifdef TORRENT_DISK_STATS + void disk_buffer_pool::rename_buffer(char* buf, char const* category) + { + mutex::scoped_lock l(m_pool_mutex); + TORRENT_ASSERT(is_disk_buffer(buf, l)); + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + std::string const& prev_category = m_buf_to_category[buf]; + --m_categories[prev_category]; + m_log << log_time() << " " << prev_category << ": " << m_categories[prev_category] << "\n"; + + ++m_categories[category]; + m_buf_to_category[buf] = category; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + } +#endif + + void disk_buffer_pool::free_multiple_buffers(char** bufvec, int numbufs) + { + char** end = bufvec + numbufs; + // sort the pointers in order to maximize cache hits + std::sort(bufvec, end); + + mutex::scoped_lock l(m_pool_mutex); + for (; bufvec != end; ++bufvec) + { + char* buf = *bufvec; + TORRENT_ASSERT(buf); + free_buffer_impl(buf, l);; + } + } + + void disk_buffer_pool::free_buffer(char* buf) + { + mutex::scoped_lock l(m_pool_mutex); + free_buffer_impl(buf, l); + } + + void disk_buffer_pool::free_buffer_impl(char* buf, mutex::scoped_lock& l) + { + TORRENT_ASSERT(buf); + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_disk_buffer(buf, l)); +#if defined TORRENT_DISK_STATS || defined TORRENT_STATS + --m_allocations; +#endif +#ifdef TORRENT_DISK_STATS + TORRENT_ASSERT(m_categories.find(m_buf_to_category[buf]) + != m_categories.end()); + std::string const& category = m_buf_to_category[buf]; + --m_categories[category]; + m_log << log_time() << " " << category << ": " << m_categories[category] << "\n"; + m_buf_to_category.erase(buf); +#endif +#if TORRENT_USE_MLOCK + if (m_settings.lock_disk_cache) + { +#ifdef TORRENT_WINDOWS + VirtualUnlock(buf, m_block_size); +#else + munlock(buf, m_block_size); +#endif + } +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + page_aligned_allocator::free(buf); +#else + if (m_using_pool_allocator) + m_pool.free(buf); + else + page_aligned_allocator::free(buf); +#endif + --m_in_use; + +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + // should we switch which allocator to use? + if (m_in_use == 0 && m_settings.use_disk_cache_pool != m_using_pool_allocator) + { + m_pool.release_memory(); + m_using_pool_allocator = m_settings.use_disk_cache_pool; + } +#endif + } + + void disk_buffer_pool::release_memory() + { + TORRENT_ASSERT(m_magic == 0x1337); +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + mutex::scoped_lock l(m_pool_mutex); + if (m_using_pool_allocator) + m_pool.release_memory(); +#endif + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp b/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp new file mode 100644 index 0000000000..6120a8a1fa --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/disk_io_thread.cpp @@ -0,0 +1,2537 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + Disk queue elevator patch by Morten Husveit +*/ + +#include "libtorrent/storage.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/file_pool.hpp" +#include +#include + +#include "libtorrent/time.hpp" + +#if TORRENT_USE_MLOCK && !defined TORRENT_WINDOWS +#include +#endif + +#ifdef TORRENT_BSD +#include +#endif + +#if TORRENT_USE_RLIMIT +#include +#endif + +#ifdef TORRENT_LINUX +#include +#endif + +namespace libtorrent +{ + bool should_cancel_on_abort(disk_io_job const& j); + bool is_read_operation(disk_io_job const& j); + bool operation_has_buffer(disk_io_job const& j); + +// ------- disk_io_thread ------ + + disk_io_thread::disk_io_thread(io_service& ios + , boost::function const& queue_callback + , file_pool& fp + , int block_size) + : disk_buffer_pool(block_size) + , m_abort(false) + , m_waiting_to_shutdown(false) + , m_queue_buffer_size(0) + , m_last_file_check(time_now_hires()) + , m_last_stats_flip(time_now()) + , m_physical_ram(0) + , m_exceeded_write_queue(false) + , m_ios(ios) + , m_queue_callback(queue_callback) + , m_work(io_service::work(m_ios)) + , m_file_pool(fp) +#if TORRENT_USE_ASSERTS + , m_magic(0x1337) +#endif + , m_disk_io_thread(boost::bind(&disk_io_thread::thread_fun, this)) + { + // don't do anything in here. Essentially all members + // of this object are owned by the newly created thread. + // initialize stuff in thread_fun(). + } + + disk_io_thread::~disk_io_thread() + { + TORRENT_ASSERT(m_magic == 0x1337); +#if TORRENT_USE_ASSERTS + m_magic = 0xdead; +#endif + TORRENT_ASSERT(m_abort == true); + } + + void disk_io_thread::abort() + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_queue_mutex); + disk_io_job j; + m_waiting_to_shutdown = true; + j.action = disk_io_job::abort_thread; + j.start_time = time_now_hires(); + + TORRENT_ASSERT(l.locked()); + m_jobs.insert(m_jobs.begin(), j); + m_signal.signal(l); + } + + void disk_io_thread::join() + { + TORRENT_ASSERT(m_magic == 0x1337); + + m_disk_io_thread.join(); + mutex::scoped_lock l(m_queue_mutex); + TORRENT_ASSERT(m_abort == true); + m_jobs.clear(); + } + + bool disk_io_thread::can_write() const + { + TORRENT_ASSERT(m_magic == 0x1337); + mutex::scoped_lock l(m_queue_mutex); + return !m_exceeded_write_queue; + } + + void disk_io_thread::flip_stats(ptime now) + { + TORRENT_ASSERT(m_magic == 0x1337); + + // calling mean() will actually reset the accumulators + m_cache_stats.average_queue_time = m_queue_time.mean(); + m_cache_stats.average_read_time = m_read_time.mean(); + m_cache_stats.average_write_time = m_write_time.mean(); + m_cache_stats.average_hash_time = m_hash_time.mean(); + m_cache_stats.average_job_time = m_job_time.mean(); + m_cache_stats.average_sort_time = m_sort_time.mean(); + + m_last_stats_flip = now; + } + + void disk_io_thread::get_cache_info(sha1_hash const& ih, std::vector& ret) const + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_piece_mutex); + ret.clear(); + ret.reserve(m_pieces.size()); + for (cache_t::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i) + { + torrent_info const& ti = *i->storage->info(); + if (ti.info_hash() != ih) continue; + cached_piece_info info; + info.next_to_hash = i->next_block_to_hash; + info.piece = i->piece; + info.last_use = i->expire; + info.kind = cached_piece_info::write_cache; + int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; + info.blocks.resize(blocks_in_piece); + for (int b = 0; b < blocks_in_piece; ++b) + if (i->blocks[b].buf) info.blocks[b] = true; + ret.push_back(info); + } + for (cache_t::const_iterator i = m_read_pieces.begin() + , end(m_read_pieces.end()); i != end; ++i) + { + torrent_info const& ti = *i->storage->info(); + if (ti.info_hash() != ih) continue; + cached_piece_info info; + info.next_to_hash = i->next_block_to_hash; + info.piece = i->piece; + info.last_use = i->expire; + info.kind = cached_piece_info::read_cache; + int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; + info.blocks.resize(blocks_in_piece); + for (int b = 0; b < blocks_in_piece; ++b) + if (i->blocks[b].buf) info.blocks[b] = true; + ret.push_back(info); + } + } + + cache_status disk_io_thread::status() const + { + mutex::scoped_lock l(m_piece_mutex); + m_cache_stats.total_used_buffers = in_use(); + m_cache_stats.queued_bytes = m_queue_buffer_size; + + cache_status ret = m_cache_stats; + + ret.job_queue_length = m_jobs.size() + m_sorted_read_jobs.size(); + ret.read_queue_size = m_sorted_read_jobs.size(); + + return ret; + } + + // aborts read operations + void disk_io_thread::stop(boost::intrusive_ptr s) + { + mutex::scoped_lock l(m_queue_mutex); + // read jobs are aborted, write and move jobs are syncronized + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (i->storage != s) + { + ++i; + continue; + } + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + disk_io_job j; + j.action = disk_io_job::abort_torrent; + j.storage = s; + add_job(j, l); + } + + struct update_last_use + { + update_last_use(int exp): expire(exp) {} + void operator()(disk_io_thread::cached_piece_entry& p) + { + TORRENT_ASSERT(p.storage); + p.expire = time_now() + seconds(expire); + } + int expire; + }; + + disk_io_thread::cache_piece_index_t::iterator disk_io_thread::find_cached_piece( + disk_io_thread::cache_t& cache + , disk_io_job const& j, mutex::scoped_lock& l) + { + cache_piece_index_t& idx = cache.get<0>(); + cache_piece_index_t::iterator i + = idx.find(std::pair(j.storage.get(), j.piece)); + TORRENT_ASSERT(i == idx.end() || (i->storage == j.storage && i->piece == j.piece)); + return i; + } + + void disk_io_thread::flush_expired_pieces() + { + ptime now = time_now(); + + mutex::scoped_lock l(m_piece_mutex); + + INVARIANT_CHECK; + // flush write cache + cache_lru_index_t& widx = m_pieces.get<1>(); + cache_lru_index_t::iterator i = widx.begin(); + time_duration cut_off = seconds(m_settings.cache_expiry); + while (i != widx.end() && now - i->expire > cut_off) + { + TORRENT_ASSERT(i->storage); + flush_range(const_cast(*i), 0, INT_MAX, l); + TORRENT_ASSERT(i->num_blocks == 0); + + // we want to keep the piece in here to have an accurate + // number for next_block_to_hash, if we're in avoid_readback mode + + bool erase = m_settings.disk_cache_algorithm != session_settings::avoid_readback; + if (!erase) + { + // however, if we've already hashed the whole piece, in-order + // there's no need to keep it around + int piece_size = i->storage->info()->piece_size(i->piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + erase = i->next_block_to_hash == blocks_in_piece; + } + + if (erase) widx.erase(i++); + else ++i; + } + + if (m_settings.explicit_read_cache) return; + + // flush read cache + std::vector bufs; + cache_lru_index_t& ridx = m_read_pieces.get<1>(); + i = ridx.begin(); + while (i != ridx.end() && now - i->expire > cut_off) + { + drain_piece_bufs(const_cast(*i), bufs, l); + ridx.erase(i++); + } + if (!bufs.empty()) free_multiple_buffers(&bufs[0], bufs.size()); + } + + int disk_io_thread::drain_piece_bufs(cached_piece_entry& p, std::vector& buf + , mutex::scoped_lock& l) + { + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int ret = 0; + + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf == 0) continue; + buf.push_back(p.blocks[i].buf); + ++ret; + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + return ret; + } + + // returns the number of blocks that were freed + int disk_io_thread::free_piece(cached_piece_entry& p, mutex::scoped_lock& l) + { + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int ret = 0; + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf == 0) continue; + buffers.push_back(p.blocks[i].buf); + ++ret; + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return ret; + } + + // returns the number of blocks that were freed + int disk_io_thread::clear_oldest_read_piece( + int num_blocks, ignore_t ignore, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + cache_lru_index_t& idx = m_read_pieces.get<1>(); + if (idx.empty()) return 0; + + cache_lru_index_t::iterator i = idx.begin(); + if (i->piece == ignore.piece && i->storage == ignore.storage) + { + ++i; + if (i == idx.end()) return 0; + } + + // don't replace an entry that hasn't expired yet + if (time_now() < i->expire) return 0; + int blocks = 0; + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + if (num_blocks >= i->num_blocks) + { + blocks = drain_piece_bufs(const_cast(*i), buffers, l); + } + else + { + // delete blocks from the start and from the end + // until num_blocks have been freed + int end = (i->storage->info()->piece_size(i->piece) + m_block_size - 1) / m_block_size - 1; + int start = 0; + + while (num_blocks) + { + // if we have a volatile read cache, only clear + // from the end, since we're already clearing + // from the start as blocks are read + if (!m_settings.volatile_read_cache) + { + while (i->blocks[start].buf == 0 && start <= end) ++start; + if (start > end) break; + buffers.push_back(i->blocks[start].buf); + i->blocks[start].buf = 0; + ++blocks; + --const_cast(*i).num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + --num_blocks; + if (!num_blocks) break; + } + + while (i->blocks[end].buf == 0 && start <= end) --end; + if (start > end) break; + buffers.push_back(i->blocks[end].buf); + i->blocks[end].buf = 0; + ++blocks; + --const_cast(*i).num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + --num_blocks; + } + } + if (i->num_blocks == 0) idx.erase(i); + + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return blocks; + } + + int contiguous_blocks(disk_io_thread::cached_piece_entry const& b) + { + int ret = 0; + int current = 0; + int blocks_in_piece = (b.storage->info()->piece_size(b.piece) + 16 * 1024 - 1) / (16 * 1024); + for (int i = 0; i < blocks_in_piece; ++i) + { + if (b.blocks[i].buf) ++current; + else + { + if (current > ret) ret = current; + current = 0; + } + } + if (current > ret) ret = current; + return ret; + } + + int disk_io_thread::flush_contiguous_blocks(cached_piece_entry& p + , mutex::scoped_lock& l, int lower_limit, bool avoid_readback) + { + // first find the largest range of contiguous blocks + int len = 0; + int current = 0; + int pos = 0; + int start = 0; + int blocks_in_piece = (p.storage->info()->piece_size(p.piece) + + m_block_size - 1) / m_block_size; + + if (avoid_readback) + { + start = p.next_block_to_hash; + for (int i = p.next_block_to_hash; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf) ++current; + else break; + } + } + else + { + for (int i = 0; i < blocks_in_piece; ++i) + { + if (p.blocks[i].buf) ++current; + else + { + if (current > len) + { + len = current; + pos = start; + } + current = 0; + start = i + 1; + } + } + } + if (current > len) + { + len = current; + pos = start; + } + + if (len < lower_limit || len <= 0) return 0; + len = flush_range(p, pos, pos + len, l); + return len; + } + + bool cmp_contiguous(disk_io_thread::cached_piece_entry const& lhs + , disk_io_thread::cached_piece_entry const& rhs) + { + return lhs.num_contiguous_blocks < rhs.num_contiguous_blocks; + } + + // flushes 'blocks' blocks from the cache + int disk_io_thread::flush_cache_blocks(mutex::scoped_lock& l + , int blocks, ignore_t ignore, int options) + { + // first look if there are any read cache entries that can + // be cleared + int ret = 0; + int tmp = 0; + do { + tmp = clear_oldest_read_piece(blocks, ignore, l); + blocks -= tmp; + ret += tmp; + } while (tmp > 0 && blocks > 0); + + if (blocks == 0) return ret; + + if (options & dont_flush_write_blocks) return ret; + + // if we don't have any blocks in the cache, no need to go look for any + if (m_cache_stats.cache_size == 0) return ret; + + if (m_settings.disk_cache_algorithm == session_settings::lru) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + while (blocks > 0) + { + cache_lru_index_t::iterator i = idx.begin(); + if (i == idx.end()) return ret; + tmp = flush_range(const_cast(*i), 0, INT_MAX, l); + idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + else if (m_settings.disk_cache_algorithm == session_settings::largest_contiguous) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + while (blocks > 0) + { + cache_lru_index_t::iterator i = std::max_element(idx.begin(), idx.end(), &cmp_contiguous); + if (i == idx.end()) return ret; + tmp = flush_contiguous_blocks(const_cast(*i), l); + if (i->num_blocks == 0) idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + else if (m_settings.disk_cache_algorithm == session_settings::avoid_readback) + { + cache_lru_index_t& idx = m_pieces.get<1>(); + for (cache_lru_index_t::iterator i = idx.begin(); i != idx.end();) + { + cached_piece_entry& p = const_cast(*i); + cache_lru_index_t::iterator piece = i; + ++i; + + if (!piece->blocks[p.next_block_to_hash].buf) continue; + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int start = p.next_block_to_hash; + int end = start + 1; + while (end < blocks_in_piece && p.blocks[end].buf) ++end; + tmp = flush_range(p, start, end, l); + p.num_contiguous_blocks = contiguous_blocks(p); + if (p.num_blocks == 0 && p.next_block_to_hash == blocks_in_piece) + idx.erase(piece); + blocks -= tmp; + ret += tmp; + if (blocks <= 0) break; + } + + // if we still need to flush blocks, flush the largest contiguous blocks + // regardless of if we'll have to read them back later + while (blocks > 0) + { + cache_lru_index_t::iterator i = std::max_element(idx.begin(), idx.end(), &cmp_contiguous); + if (i == idx.end() || i->num_blocks == 0) return ret; + tmp = flush_contiguous_blocks(const_cast(*i), l); + // at this point, we will for sure need a read-back for + // this piece anyway. We might as well save some time looping + // over the disk cache by deleting the entry + if (i->num_blocks == 0) idx.erase(i); + blocks -= tmp; + ret += tmp; + } + } + return ret; + } + + int disk_io_thread::flush_range(cached_piece_entry& p + , int start, int end, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(start < end); + + int piece_size = p.storage->info()->piece_size(p.piece); +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " flushing " << piece_size << std::endl; +#endif + TORRENT_ASSERT(piece_size > 0); + + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int buffer_size = 0; + int offset = 0; + + boost::scoped_array buf; + file::iovec_t* iov = 0; + int iov_counter = 0; + if (m_settings.coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]); + else iov = TORRENT_ALLOCA(file::iovec_t, blocks_in_piece); + + end = (std::min)(end, blocks_in_piece); + int num_write_calls = 0; + ptime write_start = time_now_hires(); + for (int i = start; i <= end; ++i) + { + if (i == end || p.blocks[i].buf == 0) + { + if (buffer_size == 0) continue; + + TORRENT_ASSERT(buffer_size <= i * m_block_size); + l.unlock(); + if (iov) + { + int ret = p.storage->write_impl(iov, p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, iov_counter); + iov_counter = 0; + if (ret > 0) ++num_write_calls; + } + else + { + TORRENT_ASSERT(buf); + file::iovec_t b = { buf.get(), size_t(buffer_size) }; + int ret = p.storage->write_impl(&b, p.piece, (std::min)( + i * m_block_size, piece_size) - buffer_size, 1); + if (ret > 0) ++num_write_calls; + } + l.lock(); + ++m_cache_stats.writes; +// std::cerr << " flushing p: " << p.piece << " bytes: " << buffer_size << std::endl; + buffer_size = 0; + offset = 0; + continue; + } + int block_size = (std::min)(piece_size - i * m_block_size, m_block_size); + TORRENT_ASSERT(offset + block_size <= piece_size); + TORRENT_ASSERT(offset + block_size > 0); + if (iov) + { + TORRENT_ASSERT(!buf); + iov[iov_counter].iov_base = p.blocks[i].buf; + iov[iov_counter].iov_len = block_size; + ++iov_counter; + } + else + { + TORRENT_ASSERT(buf); + TORRENT_ASSERT(iov == 0); + std::memcpy(buf.get() + offset, p.blocks[i].buf, block_size); + offset += m_block_size; + } + buffer_size += block_size; + TORRENT_ASSERT(p.num_blocks > 0); + --p.num_blocks; + ++m_cache_stats.blocks_written; + --m_cache_stats.cache_size; + if (i == p.next_block_to_hash) ++p.next_block_to_hash; + } + + ptime done = time_now_hires(); + + int ret = 0; + disk_io_job j; + j.storage = p.storage; + j.action = disk_io_job::write; + j.buffer = 0; + j.piece = p.piece; + test_error(j); + std::vector buffers; + for (int i = start; i < end; ++i) + { + if (p.blocks[i].buf == 0) continue; + j.buffer_size = (std::min)(piece_size - i * m_block_size, m_block_size); + int result = j.error ? -1 : j.buffer_size; + j.offset = i * m_block_size; + j.callback = p.blocks[i].callback; + buffers.push_back(p.blocks[i].buf); + post_callback(j, result); + p.blocks[i].callback.clear(); + p.blocks[i].buf = 0; + ++ret; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + + if (num_write_calls > 0) + { + m_write_time.add_sample(total_microseconds(done - write_start) / num_write_calls); + m_cache_stats.cumulative_write_time += total_milliseconds(done - write_start); + } + if (ret > 0) + p.num_contiguous_blocks = contiguous_blocks(p); + + TORRENT_ASSERT(buffer_size == 0); +// std::cerr << " flushing p: " << p.piece << " cached_blocks: " << m_cache_stats.cache_size << std::endl; +#ifdef TORRENT_DEBUG + for (int i = start; i < end; ++i) + TORRENT_ASSERT(p.blocks[i].buf == 0); +#endif + return ret; + } + + // returns -1 on failure + int disk_io_thread::cache_block(disk_io_job& j + , boost::function& handler + , int cache_expire + , mutex::scoped_lock& l) + { + INVARIANT_CHECK; + TORRENT_ASSERT(find_cached_piece(m_pieces, j, l) == m_pieces.end()); + TORRENT_ASSERT((j.offset & (m_block_size-1)) == 0); + TORRENT_ASSERT(j.cache_min_time >= 0); + cached_piece_entry p; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + // there's no point in caching the piece if + // there's only one block in it + if (blocks_in_piece <= 1) return -1; + +#ifdef TORRENT_DISK_STATS + rename_buffer(j.buffer, "write cache"); +#endif + + p.piece = j.piece; + p.storage = j.storage; + p.expire = time_now() + seconds(j.cache_min_time); + p.num_blocks = 1; + p.num_contiguous_blocks = 1; + p.next_block_to_hash = 0; + p.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!p.blocks) return -1; + int block = j.offset / m_block_size; +// std::cerr << " adding cache entry for p: " << j.piece << " block: " << block << " cached_blocks: " << m_cache_stats.cache_size << std::endl; + p.blocks[block].buf = j.buffer; + p.blocks[block].callback.swap(handler); + ++m_cache_stats.cache_size; + cache_lru_index_t& idx = m_pieces.get<1>(); + TORRENT_ASSERT(p.storage); + idx.insert(p); + return 0; + } + + // fills a piece with data from disk, returns the total number of bytes + // read or -1 if there was an error + int disk_io_thread::read_into_piece(cached_piece_entry& p, int start_block + , int options, int num_blocks, mutex::scoped_lock& l) + { + TORRENT_ASSERT(num_blocks > 0); + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + int end_block = start_block; + int num_read = 0; + + int iov_counter = 0; + file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, (std::min)(blocks_in_piece - start_block, num_blocks)); + + int piece_offset = start_block * m_block_size; + + int ret = 0; + + boost::scoped_array buf; + for (int i = start_block; i < blocks_in_piece + && ((options & ignore_cache_size) + || in_use() < m_settings.cache_size); ++i) + { + int block_size = (std::min)(piece_size - piece_offset, m_block_size); + TORRENT_ASSERT(piece_offset <= piece_size); + + // this is a block that is already allocated + // free it and allocate a new one + if (p.blocks[i].buf) + { + free_buffer(p.blocks[i].buf); + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + p.blocks[i].buf = allocate_buffer("read cache"); + + // the allocation failed, break + if (p.blocks[i].buf == 0) + { + free_piece(p, l); + return -1; + } + ++p.num_blocks; + ++m_cache_stats.cache_size; + ++m_cache_stats.read_cache_size; + ++end_block; + ++num_read; + iov[iov_counter].iov_base = p.blocks[i].buf; + iov[iov_counter].iov_len = block_size; + ++iov_counter; + piece_offset += m_block_size; + if (num_read >= num_blocks) break; + } + + if (end_block == start_block) + { + // something failed. Free all buffers + // we just allocated + free_piece(p, l); + return -2; + } + + TORRENT_ASSERT(iov_counter <= (std::min)(blocks_in_piece - start_block, num_blocks)); + + // the buffer_size is the size of the buffer we need to read + // all these blocks. + const int buffer_size = (std::min)((end_block - start_block) * m_block_size + , piece_size - start_block * m_block_size); + TORRENT_ASSERT(buffer_size > 0); + TORRENT_ASSERT(buffer_size <= piece_size); + TORRENT_ASSERT(buffer_size + start_block * m_block_size <= piece_size); + + if (m_settings.coalesce_reads) + buf.reset(new (std::nothrow) char[buffer_size]); + + if (buf) + { + l.unlock(); + file::iovec_t b = { buf.get(), size_t(buffer_size) }; + ret = p.storage->read_impl(&b, p.piece, start_block * m_block_size, 1); + l.lock(); + ++m_cache_stats.reads; + if (p.storage->error()) + { + free_piece(p, l); + return -1; + } + + if (ret != buffer_size) + { + // this means the file wasn't big enough for this read + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d b: %d s: %d (read: %d)", p.piece, start_block, buffer_size, ret); + p.storage->get_storage_impl()->set_error(msg, errors::file_too_short); + free_piece(p, l); + return -1; + } + + int offset = 0; + for (int i = 0; i < iov_counter; ++i) + { + TORRENT_ASSERT(iov[i].iov_base); + TORRENT_ASSERT(iov[i].iov_len > 0); + TORRENT_ASSERT(int(offset + iov[i].iov_len) <= buffer_size); + std::memcpy(iov[i].iov_base, buf.get() + offset, iov[i].iov_len); + offset += iov[i].iov_len; + } + } + else + { + l.unlock(); + ret = p.storage->read_impl(iov, p.piece, start_block * m_block_size, iov_counter); + l.lock(); + ++m_cache_stats.reads; + if (p.storage->error()) + { + free_piece(p, l); + return -1; + } + + if (ret != buffer_size) + { + // this means the file wasn't big enough for this read + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d b: %d s: %d (read: %d)", p.piece, start_block, buffer_size, ret); + p.storage->get_storage_impl()->set_error(msg, errors::file_too_short); + free_piece(p, l); + return -1; + } + } + + TORRENT_ASSERT(ret == buffer_size); + return ret; + } + + // returns -1 on read error, -2 if there isn't any space in the cache + // or the number of bytes read + int disk_io_thread::cache_read_block(disk_io_job const& j, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(j.cache_min_time >= 0); + + // this function will create a new cached_piece_entry + // and requires that it doesn't already exist + cache_piece_index_t& idx = m_read_pieces.get<0>(); + TORRENT_ASSERT(find_cached_piece(m_read_pieces, j, l) == idx.end()); + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + int start_block = j.offset / m_block_size; + + int blocks_to_read = blocks_in_piece - start_block; + blocks_to_read = (std::min)(blocks_to_read, (std::max)((m_settings.cache_size + + m_cache_stats.read_cache_size - in_use())/2, 3)); + blocks_to_read = (std::min)(blocks_to_read, m_settings.read_cache_line_size); + if (j.max_cache_line > 0) blocks_to_read = (std::min)(blocks_to_read, j.max_cache_line); + + if (in_use() + blocks_to_read > m_settings.cache_size) + { + int clear = in_use() + blocks_to_read - m_settings.cache_size; + if (flush_cache_blocks(l, clear, ignore_t(j.piece, j.storage.get()) + , dont_flush_write_blocks) < clear) + return -2; + } + + cached_piece_entry p; + p.piece = j.piece; + p.storage = j.storage; + p.expire = time_now() + seconds(j.cache_min_time); + p.num_blocks = 0; + p.num_contiguous_blocks = 0; + p.next_block_to_hash = 0; + p.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!p.blocks) return -1; + + int ret = read_into_piece(p, start_block, 0, blocks_to_read, l); + + TORRENT_ASSERT(p.storage); + if (ret >= 0) idx.insert(p); + + return ret; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void disk_io_thread::check_invariant() const + { + int cached_write_blocks = 0; + cache_piece_index_t const& idx = m_pieces.get<0>(); + for (cache_piece_index_t::const_iterator i = idx.begin() + , end(idx.end()); i != end; ++i) + { + cached_piece_entry const& p = *i; + TORRENT_ASSERT(p.blocks); +// TORRENT_ASSERT(p.num_contiguous_blocks == contiguous_blocks(p)); + + TORRENT_ASSERT(p.storage); + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int blocks = 0; + for (int k = 0; k < blocks_in_piece; ++k) + { + if (p.blocks[k].buf) + { +#if !defined TORRENT_DISABLE_POOL_ALLOCATOR && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(is_disk_buffer(p.blocks[k].buf)); +#endif + ++blocks; + } + } +// TORRENT_ASSERT(blocks == p.num_blocks); + cached_write_blocks += blocks; + } + + int cached_read_blocks = 0; + for (cache_t::const_iterator i = m_read_pieces.begin() + , end(m_read_pieces.end()); i != end; ++i) + { + cached_piece_entry const& p = *i; + TORRENT_ASSERT(p.blocks); + + int piece_size = p.storage->info()->piece_size(p.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + int blocks = 0; + for (int k = 0; k < blocks_in_piece; ++k) + { + if (p.blocks[k].buf) + { +#if !defined TORRENT_DISABLE_POOL_ALLOCATOR && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(is_disk_buffer(p.blocks[k].buf)); +#endif + ++blocks; + } + } +// TORRENT_ASSERT(blocks == p.num_blocks); + cached_read_blocks += blocks; + } + + TORRENT_ASSERT(cached_read_blocks == m_cache_stats.read_cache_size); + TORRENT_ASSERT(cached_read_blocks + cached_write_blocks == m_cache_stats.cache_size); + +#ifdef TORRENT_DISK_STATS + int read_allocs = m_categories.find(std::string("read cache"))->second; + int write_allocs = m_categories.find(std::string("write cache"))->second; + TORRENT_ASSERT(cached_read_blocks == read_allocs); + TORRENT_ASSERT(cached_write_blocks == write_allocs); +#endif + + // when writing, there may be a one block difference, right before an old piece + // is flushed + TORRENT_ASSERT(m_cache_stats.cache_size <= m_settings.cache_size + 1); + } +#endif + + // reads the full piece specified by j into the read cache + // returns the iterator to it and whether or not it already + // was in the cache (hit). + int disk_io_thread::cache_piece(disk_io_job const& j, cache_piece_index_t::iterator& p + , bool& hit, int options, mutex::scoped_lock& l) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(j.cache_min_time >= 0); + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + p = find_cached_piece(m_read_pieces, j, l); + + hit = true; + int ret = 0; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + if (p != m_read_pieces.end() && p->num_blocks != blocks_in_piece) + { + INVARIANT_CHECK; + // we have the piece in the cache, but not all of the blocks + ret = read_into_piece(const_cast(*p), 0 + , options, blocks_in_piece, l); + hit = false; + if (ret < 0) return ret; + idx.modify(p, update_last_use(j.cache_min_time)); + } + else if (p == m_read_pieces.end()) + { + INVARIANT_CHECK; + // if the piece cannot be found in the cache, + // read the whole piece starting at the block + // we got a request for. + + cached_piece_entry pe; + pe.piece = j.piece; + pe.storage = j.storage; + pe.expire = time_now() + seconds(j.cache_min_time); + pe.num_blocks = 0; + pe.num_contiguous_blocks = 0; + pe.next_block_to_hash = 0; + pe.blocks.reset(new (std::nothrow) cached_block_entry[blocks_in_piece]); + if (!pe.blocks) return -1; + ret = read_into_piece(pe, 0, options, INT_MAX, l); + + hit = false; + if (ret < 0) return ret; + TORRENT_ASSERT(pe.storage); + p = idx.insert(pe).first; + } + else + { + idx.modify(p, update_last_use(j.cache_min_time)); + } + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + return ret; + } + + // cache the entire piece and hash it + int disk_io_thread::read_piece_from_cache_and_hash(disk_io_job const& j, sha1_hash& h) + { + TORRENT_ASSERT(j.buffer); + + TORRENT_ASSERT(j.cache_min_time >= 0); + + mutex::scoped_lock l(m_piece_mutex); + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + + if (in_use() + blocks_in_piece >= m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size + blocks_in_piece); + } + + cache_piece_index_t::iterator p; + bool hit; + int ret = cache_piece(j, p, hit, ignore_cache_size, l); + if (ret < 0) return ret; + + if (!m_settings.disable_hash_checks) + { + hasher ctx; + + for (int i = 0; i < blocks_in_piece; ++i) + { + TORRENT_ASSERT(p->blocks[i].buf); + ctx.update((char const*)p->blocks[i].buf, (std::min)(piece_size, m_block_size)); + piece_size -= m_block_size; + } + h = ctx.final(); + } + + ret = copy_from_piece(const_cast(*p), hit, j, l); + TORRENT_ASSERT(ret > 0); + if (ret < 0) return ret; + cache_piece_index_t& idx = m_read_pieces.get<0>(); + if (p->num_blocks == 0) idx.erase(p); + else idx.modify(p, update_last_use(j.cache_min_time)); + + // if read cache is disabled or we exceeded the + // limit, remove this piece from the cache + // also, if the piece wasn't in the cache when + // the function was called, and we're using an + // explicit read cache, remove it again + if (in_use() >= m_settings.cache_size + || !m_settings.use_read_cache + || (m_settings.explicit_read_cache && !hit)) + { + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + if (p != m_read_pieces.end()) + { + free_piece(const_cast(*p), l); + m_read_pieces.erase(p); + } + } + + ret = j.buffer_size; + ++m_cache_stats.blocks_read; + if (hit) ++m_cache_stats.blocks_read_hit; + return ret; + } + + // this doesn't modify the read cache, it only + // checks to see if the given read request can + // be fully satisfied from the given cached piece + // this is similar to copy_from_piece() but it + // doesn't do anything but determining if it's a + // cache hit or not + bool disk_io_thread::is_cache_hit(cached_piece_entry& p + , disk_io_job const& j, mutex::scoped_lock& l) + { + int block = j.offset / m_block_size; + int block_offset = j.offset & (m_block_size-1); + int size = j.buffer_size; + int min_blocks_to_read = block_offset > 0 && (size > m_block_size - block_offset) ? 2 : 1; + TORRENT_ASSERT(size <= m_block_size); + int start_block = block; + // if we have to read more than one block, and + // the first block is there, make sure we test + // for the second block + if (p.blocks[start_block].buf != 0 && min_blocks_to_read > 1) + ++start_block; + +#ifdef TORRENT_DEBUG + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + TORRENT_ASSERT(start_block < blocks_in_piece); +#endif + + return p.blocks[start_block].buf != 0; + } + + int disk_io_thread::copy_from_piece(cached_piece_entry& p, bool& hit + , disk_io_job const& j, mutex::scoped_lock& l) + { + TORRENT_ASSERT(j.buffer); + + // copy from the cache and update the last use timestamp + int block = j.offset / m_block_size; + int block_offset = j.offset & (m_block_size-1); + int buffer_offset = 0; + int size = j.buffer_size; + int min_blocks_to_read = block_offset > 0 && (size > m_block_size - block_offset) ? 2 : 1; + TORRENT_ASSERT(size <= m_block_size); + int start_block = block; + if (p.blocks[start_block].buf != 0 && min_blocks_to_read > 1) + ++start_block; + + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + TORRENT_ASSERT(start_block < blocks_in_piece); + + // if block_offset > 0, we need to read two blocks, and then + // copy parts of both, because it's not aligned to the block + // boundaries + if (p.blocks[start_block].buf == 0) + { + // if we use an explicit read cache, pretend there's no + // space to force hitting disk without caching anything + if (m_settings.explicit_read_cache) return -2; + + int end_block = start_block; + while (end_block < blocks_in_piece && p.blocks[end_block].buf == 0) ++end_block; + + int blocks_to_read = end_block - block; + blocks_to_read = (std::min)(blocks_to_read, (std::max)((m_settings.cache_size + + m_cache_stats.read_cache_size - in_use())/2, 3)); + blocks_to_read = (std::min)(blocks_to_read, m_settings.read_cache_line_size); + blocks_to_read = (std::max)(blocks_to_read, min_blocks_to_read); + if (j.max_cache_line > 0) blocks_to_read = (std::min)(blocks_to_read, j.max_cache_line); + + // if we don't have enough space for the new piece, try flushing something else + if (in_use() + blocks_to_read > m_settings.cache_size) + { + int clear = in_use() + blocks_to_read - m_settings.cache_size; + if (flush_cache_blocks(l, clear, ignore_t(p.piece, p.storage.get()) + , dont_flush_write_blocks) < clear) + return -2; + } + + int ret = read_into_piece(p, block, 0, blocks_to_read, l); + hit = false; + if (ret < 0) return ret; + if (ret < size + block_offset) return -2; + TORRENT_ASSERT(p.blocks[block].buf); + } + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + while (size > 0) + { + TORRENT_ASSERT(p.blocks[block].buf); + int to_copy = (std::min)(m_block_size + - block_offset, size); + std::memcpy(j.buffer + buffer_offset + , p.blocks[block].buf + block_offset + , to_copy); + size -= to_copy; + block_offset = 0; + buffer_offset += to_copy; + if (m_settings.volatile_read_cache) + { + // if volatile read cache is set, the assumption is + // that no other peer is likely to request the same + // piece. Therefore, for each request out of the cache + // we clear the block that was requested and any blocks + // the peer skipped + for (int i = block; i >= 0 && p.blocks[i].buf; --i) + { + buffers.push_back(p.blocks[i].buf); + p.blocks[i].buf = 0; + --p.num_blocks; + --m_cache_stats.cache_size; + --m_cache_stats.read_cache_size; + } + } + ++block; + } + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + return j.buffer_size; + } + + int disk_io_thread::try_read_from_cache(disk_io_job const& j, bool& hit, int flags) + { + TORRENT_ASSERT(m_magic == 0x1337); + + TORRENT_ASSERT(j.buffer); + TORRENT_ASSERT(j.cache_min_time >= 0); + + mutex::scoped_lock l(m_piece_mutex); + if (!m_settings.use_read_cache) + { + hit = false; + return -2; + } + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + cache_piece_index_t::iterator p + = find_cached_piece(m_read_pieces, j, l); + + hit = true; + int ret = 0; + + // if the piece cannot be found in the cache, + // read the whole piece starting at the block + // we got a request for. + if (p == idx.end()) + { + if (flags & cache_only) return -2; + // if we use an explicit read cache and we + // couldn't find the block in the cache, + // pretend that there's not enough space + // to cache it, to force the read operation + // go go straight to disk + if (m_settings.explicit_read_cache) return -2; + + ret = cache_read_block(j, l); + hit = false; + if (ret < 0) return ret; + + p = find_cached_piece(m_read_pieces, j, l); + TORRENT_ASSERT(!m_read_pieces.empty()); + TORRENT_ASSERT(p->piece == j.piece); + TORRENT_ASSERT(p->storage == j.storage); + } + + TORRENT_ASSERT(p != idx.end()); + + ret = copy_from_piece(const_cast(*p), hit, j, l); + if (ret < 0) return ret; + if (p->num_blocks == 0) idx.erase(p); + else idx.modify(p, update_last_use(j.cache_min_time)); + + ret = j.buffer_size; + ++m_cache_stats.blocks_read; + if (hit) ++m_cache_stats.blocks_read_hit; + return ret; + } + + size_type disk_io_thread::queue_buffer_size() const + { + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock l(m_queue_mutex); + return m_queue_buffer_size; + } + + typedef std::list > job_queue_t; + void completion_queue_handler(job_queue_t* completed_jobs) + { + boost::shared_ptr holder(completed_jobs); + + for (job_queue_t::iterator i = completed_jobs->begin() + , end(completed_jobs->end()); i != end; ++i) + { + TORRENT_TRY + { + i->first.callback(i->second, i->first); + } + TORRENT_CATCH(std::exception& e) + {} + } + } + + int disk_io_thread::add_job(disk_io_job const& j + , mutex::scoped_lock& l + , boost::function const& f) + { + TORRENT_ASSERT(m_magic == 0x1337); + + const_cast(j).start_time = time_now_hires(); + + if (j.action == disk_io_job::write) + { + m_queue_buffer_size += j.buffer_size; + if (m_queue_buffer_size >= m_settings.max_queued_disk_bytes + && m_settings.max_queued_disk_bytes > 0) + m_exceeded_write_queue = true; + } +/* + else if (j.action == disk_io_job::read) + { + // if this is a cache hit, return it right away! + // this is OK because the cache is actually protected by + // the m_piece_mutex + bool hit = false; + if (j.buffer == 0) + { + // this is OK because the disk_buffer pool has its + // own mutex to protect the pool allocator + const_cast(j).buffer = allocate_buffer("send buffer"); + } + int ret = try_read_from_cache(j, hit, cache_only); + if (hit && ret >= 0) + { + TORRENT_ASSERT(f); + const_cast(j).callback.swap( + const_cast&>(f)); + job_queue_t* q = new job_queue_t; + q->push_back(std::make_pair(j, ret)); + m_ios.post(boost::bind(completion_queue_handler, q)); + return m_queue_buffer_size; + } + free_buffer(j.buffer); + const_cast(j).buffer = 0; + } +*/ + TORRENT_ASSERT(l.locked()); + m_jobs.push_back(j); + m_jobs.back().callback.swap(const_cast&>(f)); + + m_signal.signal(l); + return m_queue_buffer_size; + } + + int disk_io_thread::add_job(disk_io_job const& j + , boost::function const& f) + { + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(!m_abort); + TORRENT_ASSERT(j.storage + || j.action == disk_io_job::abort_thread + || j.action == disk_io_job::update_settings); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + mutex::scoped_lock l(m_queue_mutex); + TORRENT_ASSERT(m_magic == 0x1337); + return add_job(j, l, f); + } + + bool disk_io_thread::test_error(disk_io_job& j) + { + TORRENT_ASSERT(m_magic == 0x1337); + + TORRENT_ASSERT(j.storage); + error_code const& ec = j.storage->error(); + if (ec) + { + j.buffer = 0; + j.str.clear(); + j.error = ec; + j.error_file = j.storage->error_file(); + j.storage->clear_error(); + return true; + } + return false; + } + + void disk_io_thread::post_callback(disk_io_job const& j, int ret) + { + if (!j.callback) return; + m_queued_completions.push_back(std::make_pair(j, ret)); + } + + enum action_flags_t + { + read_operation = 1 + , buffer_operation = 2 + , cancel_on_abort = 4 + }; + + static const boost::uint8_t action_flags[] = + { + read_operation + buffer_operation + cancel_on_abort // read + , buffer_operation // write + , 0 // hash + , 0 // move_storage + , 0 // release_files + , 0 // delete_files + , 0 // check_fastresume + , cancel_on_abort // check_files + , 0 // save_resume_data + , 0 // rename_file + , 0 // abort_thread + , 0 // clear_read_cache + , 0 // abort_torrent + , cancel_on_abort // update_settings + , read_operation + cancel_on_abort // read_and_hash + , read_operation + cancel_on_abort // cache_piece + , 0 // file_priority +#ifndef TORRENT_NO_DEPRECATE + , 0 // finalize_file +#endif + }; + + bool should_cancel_on_abort(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & cancel_on_abort; + } + + bool is_read_operation(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & read_operation; + } + + bool operation_has_buffer(disk_io_job const& j) + { + TORRENT_ASSERT(j.action >= 0 && j.action < int(sizeof(action_flags))); + return action_flags[j.action] & buffer_operation; + } + + void disk_io_thread::thread_fun() + { +#ifdef TORRENT_DISK_STATS + m_log.open("disk_io_thread.log", std::ios::trunc); +#endif + + // figure out how much physical RAM there is in + // this machine. This is used for automatically + // sizing the disk cache size when it's set to + // automatic. +#ifdef TORRENT_BSD +#ifdef HW_MEMSIZE + int mib[2] = { CTL_HW, HW_MEMSIZE }; +#else + // not entirely sure this sysctl supports 64 + // bit return values, but it's probably better + // than not building + int mib[2] = { CTL_HW, HW_PHYSMEM }; +#endif + size_t len = sizeof(m_physical_ram); + if (sysctl(mib, 2, &m_physical_ram, &len, NULL, 0) != 0) + m_physical_ram = 0; +#elif defined TORRENT_WINDOWS + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&ms)) + m_physical_ram = ms.ullTotalPhys; + else + m_physical_ram = 0; +#elif defined TORRENT_LINUX + m_physical_ram = sysconf(_SC_PHYS_PAGES); + m_physical_ram *= sysconf(_SC_PAGESIZE); +#elif defined TORRENT_AMIGA + m_physical_ram = AvailMem(MEMF_PUBLIC); +#endif + +#if TORRENT_USE_RLIMIT + if (m_physical_ram > 0) + { + struct rlimit r; + if (getrlimit(RLIMIT_AS, &r) == 0 && r.rlim_cur != RLIM_INFINITY) + { + if (m_physical_ram > r.rlim_cur) + m_physical_ram = r.rlim_cur; + } + } +#endif + // 1 = forward in list, -1 = backwards in list + int elevator_direction = 1; + + read_jobs_t::iterator elevator_job_pos = m_sorted_read_jobs.begin(); + size_type last_elevator_pos = 0; + bool need_update_elevator_pos = false; + int immediate_jobs_in_row = 0; + + for (;;) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " idle" << std::endl; +#endif + + TORRENT_ASSERT(m_magic == 0x1337); + + mutex::scoped_lock jl(m_queue_mutex); + + if (m_queued_completions.size() >= 30 || (m_jobs.empty() && !m_queued_completions.empty())) + { + job_queue_t* q = new job_queue_t; + q->swap(m_queued_completions); + m_ios.post(boost::bind(completion_queue_handler, q)); + } + + + ptime job_start; + while (m_jobs.empty() && m_sorted_read_jobs.empty() && !m_abort) + { + // if there hasn't been an event in one second + // see if we should flush the cache +// if (!m_signal.timed_wait(jl, boost::posix_time::seconds(1))) +// flush_expired_pieces(); + m_signal.wait(jl); + m_signal.clear(jl); + + job_start = time_now(); + if (job_start >= m_last_stats_flip + seconds(1)) flip_stats(job_start); + } + + if (m_abort && m_jobs.empty()) + { + jl.unlock(); + + mutex::scoped_lock l(m_piece_mutex); + // flush all disk caches + cache_piece_index_t& widx = m_pieces.get<0>(); + for (cache_piece_index_t::iterator i = widx.begin() + , end(widx.end()); i != end; ++i) + flush_range(const_cast(*i), 0, INT_MAX, l); + +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + // since we're aborting the thread, we don't actually + // need to free all the blocks individually. We can just + // clear the piece list and the memory will be freed when we + // destruct the m_pool. If we're not using a pool, we actually + // have to free everything individually though + cache_piece_index_t& idx = m_read_pieces.get<0>(); + for (cache_piece_index_t::iterator i = idx.begin() + , end(idx.end()); i != end; ++i) + free_piece(const_cast(*i), l); +#endif + + m_pieces.clear(); + m_read_pieces.clear(); + // release the io_service to allow the run() call to return + // we do this once we stop posting new callbacks to it. + m_work.reset(); + + TORRENT_ASSERT(m_magic == 0x1337); + + return; + } + + disk_io_job j; + + ptime now = time_now_hires(); + ptime operation_start = now; + + // make sure we don't starve out the read queue by just issuing + // write jobs constantly, mix in a read job every now and then + // with a configurable ratio + // this rate must increase to every other jobs if the queued + // up read jobs increases too far. + int read_job_every = m_settings.read_job_every; + + int unchoke_limit = m_settings.unchoke_slots_limit; + if (unchoke_limit < 0) unchoke_limit = 100; + + if (int(m_sorted_read_jobs.size()) > unchoke_limit * 2) + { + int range = unchoke_limit; + int exceed = m_sorted_read_jobs.size() - range * 2; + read_job_every = (exceed * 1 + (range - exceed) * read_job_every) / 2; + if (read_job_every < 1) read_job_every = 1; + } + + bool pick_read_job = m_jobs.empty() + || (immediate_jobs_in_row >= read_job_every + && !m_sorted_read_jobs.empty()); + + if (!pick_read_job) + { + // we have a job in the job queue. If it's + // a read operation and we are allowed to + // reorder jobs, sort it into the read job + // list and continue, otherwise just pop it + // and use it later + j = m_jobs.front(); + m_jobs.pop_front(); + if (j.action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= j.buffer_size); + m_queue_buffer_size -= j.buffer_size; + + if (m_exceeded_write_queue) + { + int low_watermark = m_settings.max_queued_disk_bytes_low_watermark == 0 + || m_settings.max_queued_disk_bytes_low_watermark >= m_settings.max_queued_disk_bytes + ? size_type(m_settings.max_queued_disk_bytes) * 7 / 8 + : m_settings.max_queued_disk_bytes_low_watermark; + + if (m_queue_buffer_size < low_watermark + || m_settings.max_queued_disk_bytes == 0) + { + m_exceeded_write_queue = false; + // we just dropped below the high watermark of number of bytes + // queued for writing to the disk. Notify the session so that it + // can trigger all the connections waiting for this event + if (m_queue_callback) m_ios.post(m_queue_callback); + } + } + } + + jl.unlock(); + + bool defer = false; + + if (is_read_operation(j)) + { + defer = true; + + // at this point the operation we're looking + // at is a read operation. If this read operation + // can be fully satisfied by the read cache, handle + // it immediately + if (m_settings.use_read_cache) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_cache_hit" << std::endl; +#endif + // unfortunately we need to lock the cache + // if the cache querying function would be + // made asyncronous, this would not be + // necessary anymore + mutex::scoped_lock l(m_piece_mutex); + cache_piece_index_t::iterator p + = find_cached_piece(m_read_pieces, j, l); + + cache_piece_index_t& idx = m_read_pieces.get<0>(); + // if it's a cache hit, process the job immediately + if (p != idx.end() && is_cache_hit(const_cast(*p), j, l)) + defer = false; + } + } + + if (m_settings.use_disk_read_ahead && defer) + { + j.storage->hint_read_impl(j.piece, j.offset, j.buffer_size); + } + + TORRENT_ASSERT(j.offset >= 0); + if (m_settings.allow_reordered_disk_operations && defer) + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " sorting_job" << std::endl; +#endif + ptime sort_start = time_now_hires(); + + size_type phys_off = j.storage->physical_offset(j.piece, j.offset); + need_update_elevator_pos = need_update_elevator_pos || m_sorted_read_jobs.empty(); + m_sorted_read_jobs.insert(std::pair(phys_off, j)); + + ptime now = time_now_hires(); + m_sort_time.add_sample(total_microseconds(now - sort_start)); + m_job_time.add_sample(total_microseconds(now - operation_start)); + m_cache_stats.cumulative_sort_time += total_milliseconds(now - sort_start); + m_cache_stats.cumulative_job_time += total_milliseconds(now - operation_start); + continue; + } + + ++immediate_jobs_in_row; + } + else + { + // the job queue is empty, pick the next read job + // from the sorted job list. So we don't need the + // job queue lock anymore + jl.unlock(); + + immediate_jobs_in_row = 0; + + TORRENT_ASSERT(!m_sorted_read_jobs.empty()); + + // if m_sorted_read_jobs used to be empty, + // we need to update the elevator position + if (need_update_elevator_pos) + { + elevator_job_pos = m_sorted_read_jobs.lower_bound(last_elevator_pos); + need_update_elevator_pos = false; + } + + // if we've reached the end, change the elevator direction + if (elevator_job_pos == m_sorted_read_jobs.end()) + { + elevator_direction = -1; + --elevator_job_pos; + } + TORRENT_ASSERT(!m_sorted_read_jobs.empty()); + + TORRENT_ASSERT(elevator_job_pos != m_sorted_read_jobs.end()); + j = elevator_job_pos->second; + read_jobs_t::iterator to_erase = elevator_job_pos; + + // if we've reached the begining of the sorted list, + // change the elvator direction + if (elevator_job_pos == m_sorted_read_jobs.begin()) + elevator_direction = 1; + + // move the elevator before erasing the job we're processing + // to keep the iterator valid + if (elevator_direction > 0) ++elevator_job_pos; + else --elevator_job_pos; + + TORRENT_ASSERT(to_erase != elevator_job_pos); + last_elevator_pos = to_erase->first; + m_sorted_read_jobs.erase(to_erase); + } + + m_queue_time.add_sample(total_microseconds(now - j.start_time)); + + // if there's a buffer in this job, it will be freed + // when this holder is destructed, unless it has been + // released. + disk_buffer_holder holder(*this + , operation_has_buffer(j) ? j.buffer : 0); + + flush_expired_pieces(); + + int ret = 0; + + TORRENT_ASSERT(j.storage + || j.action == disk_io_job::abort_thread + || j.action == disk_io_job::update_settings); +#ifdef TORRENT_DISK_STATS + ptime start = time_now(); +#endif + + if (j.cache_min_time < 0) + j.cache_min_time = j.cache_min_time == 0 ? m_settings.default_cache_min_age + : (std::max)(m_settings.default_cache_min_age, j.cache_min_time); + + TORRENT_TRY + { + + if (j.storage && j.storage->get_storage_impl()->m_settings == 0) + j.storage->get_storage_impl()->m_settings = &m_settings; + + switch (j.action) + { + case disk_io_job::update_settings: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " update_settings " << std::endl; +#endif + TORRENT_ASSERT(j.buffer); + session_settings const* s = ((session_settings*)j.buffer); + TORRENT_ASSERT(s->cache_size >= -1); + TORRENT_ASSERT(s->cache_expiry > 0); + +#if defined TORRENT_WINDOWS + if (m_settings.low_prio_disk != s->low_prio_disk) + { + m_file_pool.set_low_prio_io(s->low_prio_disk); + // we need to close all files, since the prio + // only takes affect when files are opened + m_file_pool.release(0); + } +#endif + m_settings = *s; + delete s; + + m_file_pool.resize(m_settings.file_pool_size); +#if defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD + , m_settings.low_prio_disk ? IOPOL_THROTTLE : IOPOL_DEFAULT); +#elif defined IOPRIO_WHO_PROCESS + syscall(ioprio_set, IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE + , m_settings.get_bool(settings_pack::low_prio_disk) ? 7: 0)); +#endif + if (m_settings.cache_size == -1) + { + // the cache size is set to automatic. Make it + // depend on the amount of physical RAM + // if we don't know how much RAM we have, just set the + // cache size to 16 MiB (1024 blocks) + if (m_physical_ram == 0) + m_settings.cache_size = 1024; + else + m_settings.cache_size = m_physical_ram / 8 / m_block_size; + } + break; + } + case disk_io_job::abort_torrent: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " abort_torrent " << std::endl; +#endif + mutex::scoped_lock jl(m_queue_mutex); + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (i->storage != j.storage) + { + ++i; + continue; + } + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + // now clear all the read jobs + for (read_jobs_t::iterator i = m_sorted_read_jobs.begin(); + i != m_sorted_read_jobs.end();) + { + if (i->second.storage != j.storage) + { + ++i; + continue; + } + post_callback(i->second, -3); + if (elevator_job_pos == i) ++elevator_job_pos; + m_sorted_read_jobs.erase(i++); + } + jl.unlock(); + + mutex::scoped_lock l(m_piece_mutex); + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + for (cache_t::iterator i = m_read_pieces.begin(); + i != m_read_pieces.end();) + { + if (i->storage == j.storage) + { + drain_piece_bufs(const_cast(*i), buffers, l); + i = m_read_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + release_memory(); + break; + } + case disk_io_job::abort_thread: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " abort_thread " << std::endl; +#endif + // clear all read jobs + mutex::scoped_lock jl(m_queue_mutex); + + for (std::deque::iterator i = m_jobs.begin(); + i != m_jobs.end();) + { + if (should_cancel_on_abort(*i)) + { + if (i->action == disk_io_job::write) + { + TORRENT_ASSERT(m_queue_buffer_size >= i->buffer_size); + m_queue_buffer_size -= i->buffer_size; + } + post_callback(*i, -3); + i = m_jobs.erase(i); + continue; + } + ++i; + } + jl.unlock(); + + for (read_jobs_t::iterator i = m_sorted_read_jobs.begin(); + i != m_sorted_read_jobs.end();) + { + if (i->second.storage != j.storage) + { + ++i; + continue; + } + post_callback(i->second, -3); + if (elevator_job_pos == i) ++elevator_job_pos; + m_sorted_read_jobs.erase(i++); + } + + m_abort = true; + break; + } + case disk_io_job::read_and_hash: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " read_and_hash " << j.buffer_size << std::endl; +#endif + INVARIANT_CHECK; + TORRENT_ASSERT(j.buffer == 0); + j.buffer = allocate_buffer("send buffer"); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + if (j.buffer == 0) + { + ret = -1; +#if BOOST_VERSION == 103500 + j.error = error_code(boost::system::posix_error::not_enough_memory + , get_posix_category()); +#elif BOOST_VERSION > 103500 + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); +#else + j.error = error::no_memory; +#endif + j.str.clear(); + break; + } + + disk_buffer_holder read_holder(*this, j.buffer); + + // read the entire piece and verify the piece hash + // since we need to check the hash, this function + // will ignore the cache size limit (at least for + // reading and hashing, not for keeping it around) + sha1_hash h; + ret = read_piece_from_cache_and_hash(j, h); + + // -2 means there's no space in the read cache + // or that the read cache is disabled + if (ret == -1) + { + test_error(j); + break; + } + if (!m_settings.disable_hash_checks) + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?ret:-3; + if (ret == -3) + { + j.storage->mark_failed(j.piece); + j.error = errors::failed_hash_check; + j.str.clear(); + j.buffer = 0; + break; + } + + TORRENT_ASSERT(j.buffer == read_holder.get()); + read_holder.release(); +#if TORRENT_DISK_STATS + rename_buffer(j.buffer, "released send buffer"); +#endif + break; + } +#ifndef TORRENT_NO_DEPRECATE + case disk_io_job::finalize_file: + break; +#endif + case disk_io_job::read: + { + if (test_error(j)) + { + ret = -1; + break; + } +#ifdef TORRENT_DISK_STATS + m_log << log_time(); +#endif + INVARIANT_CHECK; + if (j.buffer == 0) j.buffer = allocate_buffer("send buffer"); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + if (j.buffer == 0) + { +#ifdef TORRENT_DISK_STATS + m_log << " read 0" << std::endl; +#endif + ret = -1; +#if BOOST_VERSION == 103500 + j.error = error_code(boost::system::posix_error::not_enough_memory + , get_posix_category()); +#elif BOOST_VERSION > 103500 + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); +#else + j.error = error::no_memory; +#endif + j.str.clear(); + break; + } + + disk_buffer_holder read_holder(*this, j.buffer); + + bool hit; + ret = try_read_from_cache(j, hit); + +#ifdef TORRENT_DISK_STATS + m_log << (hit?" read-cache-hit ":" read ") << j.buffer_size << std::endl; +#endif + // -2 means there's no space in the read cache + // or that the read cache is disabled + if (ret == -1) + { + j.buffer = 0; + test_error(j); + break; + } + else if (ret == -2) + { + file::iovec_t b = { j.buffer, size_t(j.buffer_size) }; + ret = j.storage->read_impl(&b, j.piece, j.offset, 1); + if (ret < 0) + { + test_error(j); + break; + } + if (ret != j.buffer_size) + { + char msg[70]; + snprintf(msg, sizeof(msg), "reading p: %d o: %d s: %d (read: %d)", j.piece, j.offset, j.buffer_size, ret); + + // this means the file wasn't big enough for this read + j.buffer = 0; + j.error = errors::file_too_short; + j.error_file = msg; + j.str.clear(); + ret = -1; + break; + } + ++m_cache_stats.blocks_read; + hit = false; + } + if (!hit) + { + ptime now = time_now_hires(); + m_read_time.add_sample(total_microseconds(now - operation_start)); + m_cache_stats.cumulative_read_time += total_milliseconds(now - operation_start); + } + TORRENT_ASSERT(j.buffer == read_holder.get()); + read_holder.release(); +#if TORRENT_DISK_STATS + rename_buffer(j.buffer, "released send buffer"); +#endif + break; + } + case disk_io_job::write: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " write " << j.buffer_size << std::endl; +#endif + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + TORRENT_ASSERT(!j.storage->error()); + TORRENT_ASSERT(j.cache_min_time >= 0); + + if (in_use() >= m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size + 1); + if (test_error(j)) break; + } + TORRENT_ASSERT(!j.storage->error()); + + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator p = find_cached_piece(m_pieces, j, l); + int block = j.offset / m_block_size; + TORRENT_ASSERT(j.buffer); + TORRENT_ASSERT(j.buffer_size <= m_block_size); + int piece_size = j.storage->info()->piece_size(j.piece); + int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; + if (p != idx.end()) + { + bool recalc_contiguous = false; + TORRENT_ASSERT(p->blocks[block].buf == 0); + if (p->blocks[block].buf) + { + free_buffer(p->blocks[block].buf); + --m_cache_stats.cache_size; + --const_cast(*p).num_blocks; + } + else if ((block > 0 && p->blocks[block-1].buf) + || (block < blocks_in_piece-1 && p->blocks[block+1].buf) + || p->num_blocks == 0) + { + // update the contiguous blocks counter for this piece. Only if it has + // an adjacent block. If it doesn't, we already know it couldn't have + // increased the largest contiguous block span in this piece + recalc_contiguous = true; + } + p->blocks[block].buf = j.buffer; + p->blocks[block].callback.swap(j.callback); +#ifdef TORRENT_DISK_STATS + rename_buffer(j.buffer, "write cache"); +#endif + ++m_cache_stats.cache_size; + ++const_cast(*p).num_blocks; + if (recalc_contiguous) + { + const_cast(*p).num_contiguous_blocks = contiguous_blocks(*p); + } + idx.modify(p, update_last_use(j.cache_min_time)); + // we might just have created a contiguous range + // that meets the requirement to be flushed. try it + // if we're in avoid_readback mode, don't do this. Only flush + // pieces when we need more space in the cache (which will avoid + // flushing blocks out-of-order) or when we issue a hash job, + // wich indicates the piece is completely downloaded + flush_contiguous_blocks(const_cast(*p) + , l, m_settings.write_cache_line_size + , m_settings.disk_cache_algorithm == session_settings::avoid_readback); + + if (p->num_blocks == 0 && p->next_block_to_hash == 0) idx.erase(p); + test_error(j); + TORRENT_ASSERT(!j.storage->error()); + } + else + { + TORRENT_ASSERT(!j.storage->error()); + if (cache_block(j, j.callback, j.cache_min_time, l) < 0) + { + l.unlock(); + ptime start = time_now_hires(); + file::iovec_t iov = {j.buffer, size_t(j.buffer_size) }; + ret = j.storage->write_impl(&iov, j.piece, j.offset, 1); + l.lock(); + if (ret < 0) + { + test_error(j); + break; + } + ptime done = time_now_hires(); + m_write_time.add_sample(total_microseconds(done - start)); + m_cache_stats.cumulative_write_time += total_milliseconds(done - start); + // we successfully wrote the block. Ignore previous errors + j.storage->clear_error(); + break; + } + TORRENT_ASSERT(!j.storage->error()); + } + // we've now inserted the buffer + // in the cache, we should not + // free it at the end + holder.release(); + + if (in_use() > m_settings.cache_size) + { + flush_cache_blocks(l, in_use() - m_settings.cache_size); + test_error(j); + } + TORRENT_ASSERT(!j.storage->error()); + + break; + } + case disk_io_job::cache_piece: + { + mutex::scoped_lock l(m_piece_mutex); + + if (test_error(j)) + { + ret = -1; + break; + } +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " cache " << j.piece << std::endl; +#endif + INVARIANT_CHECK; + TORRENT_ASSERT(j.buffer == 0); + + cache_piece_index_t::iterator p; + bool hit; + ret = cache_piece(j, p, hit, 0, l); + if (ret == -2) ret = -1; + + if (ret < 0) test_error(j); + break; + } + case disk_io_job::hash: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " hash" << std::endl; +#endif + TORRENT_ASSERT(!j.storage->error()); + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator i = find_cached_piece(m_pieces, j, l); + if (i != idx.end()) + { + TORRENT_ASSERT(i->storage); + ret = flush_range(const_cast(*i), 0, INT_MAX, l); + idx.erase(i); + if (test_error(j)) + { + ret = -1; + j.storage->mark_failed(j.piece); + break; + } + } + l.unlock(); + if (m_settings.disable_hash_checks) + { + ret = 0; + break; + } + + ptime hash_start = time_now_hires(); + + int readback = 0; + sha1_hash h = j.storage->hash_for_piece_impl(j.piece, &readback); + if (test_error(j)) + { + ret = -1; + j.storage->mark_failed(j.piece); + break; + } + + m_cache_stats.total_read_back += readback / m_block_size; + + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?0:-2; + if (ret == -2) j.storage->mark_failed(j.piece); + + ptime done = time_now_hires(); + m_hash_time.add_sample(total_microseconds(done - hash_start)); + m_cache_stats.cumulative_hash_time += total_milliseconds(done - hash_start); + break; + } + case disk_io_job::move_storage: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " move" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + ret = j.storage->move_storage_impl(j.str, j.piece); + if (ret == piece_manager::file_exist) + { + j.error = error_code(boost::system::errc::file_exists, get_system_category()); + j.error_file = -1; + j.buffer = NULL; + break; + } + if (ret != piece_manager::no_error && ret != piece_manager::need_full_check) + { + test_error(j); + break; + } + j.str = j.storage->save_path(); + break; + } + case disk_io_job::release_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " release" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + for (cache_t::iterator i = m_pieces.begin(); i != m_pieces.end();) + { + if (i->storage == j.storage) + { + flush_range(const_cast(*i), 0, INT_MAX, l); + i = m_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + release_memory(); + + ret = j.storage->release_files_impl(); + if (ret != 0) test_error(j); + break; + } + case disk_io_job::clear_read_cache: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " clear-cache" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + for (cache_t::iterator i = m_read_pieces.begin(); + i != m_read_pieces.end();) + { + if (i->storage == j.storage) + { + free_piece(const_cast(*i), l); + i = m_read_pieces.erase(i); + } + else + { + ++i; + } + } + l.unlock(); + release_memory(); + ret = 0; + break; + } + case disk_io_job::delete_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " delete" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + // delete all write cache entries for this storage + cache_piece_index_t& idx = m_pieces.get<0>(); + cache_piece_index_t::iterator start = idx.lower_bound(std::pair(j.storage.get(), 0)); + cache_piece_index_t::iterator end = idx.upper_bound(std::pair(j.storage.get(), INT_MAX)); + + // build a vector of all the buffers we need to free + // and free them all in one go + std::vector buffers; + torrent_info const& ti = *j.storage->info(); + for (cache_piece_index_t::iterator i = start; i != end; ++i) + { + int blocks_in_piece = (ti.piece_size(i->piece) + m_block_size - 1) / m_block_size; + cached_piece_entry& e = const_cast(*i); + for (int j = 0; j < blocks_in_piece; ++j) + { + if (i->blocks[j].buf == 0) continue; + buffers.push_back(i->blocks[j].buf); + i->blocks[j].buf = 0; + --m_cache_stats.cache_size; + TORRENT_ASSERT(e.num_blocks > 0); + --e.num_blocks; + } + TORRENT_ASSERT(i->num_blocks == 0); + } + idx.erase(start, end); + l.unlock(); + if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); + release_memory(); + + ret = j.storage->delete_files_impl(); + if (ret != 0) test_error(j); + break; + } + case disk_io_job::check_fastresume: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_fastresume" << std::endl; +#endif + lazy_entry const* rd = (lazy_entry const*)j.buffer; + TORRENT_ASSERT(rd != 0); + ret = j.storage->check_fastresume(*rd, j.error); + test_error(j); + break; + } + case disk_io_job::check_files: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " check_files" << std::endl; +#endif + int piece_size = j.storage->info()->piece_length(); + for (int processed = 0; processed < 4 * 1024 * 1024; processed += piece_size) + { + ptime now = time_now_hires(); + TORRENT_ASSERT(now >= m_last_file_check); + // this happens sometimes on windows for some reason + if (now < m_last_file_check) now = m_last_file_check; + +#if BOOST_VERSION > 103600 + if (now - m_last_file_check < milliseconds(m_settings.file_checks_delay_per_block)) + { + int sleep_time = m_settings.file_checks_delay_per_block + * (piece_size / (16 * 1024)) + - total_milliseconds(now - m_last_file_check); + if (sleep_time < 0) sleep_time = 0; + TORRENT_ASSERT(sleep_time < 5 * 1000); + + sleep(sleep_time); + } + m_last_file_check = time_now_hires(); +#endif + + ptime hash_start = time_now_hires(); + if (m_waiting_to_shutdown) break; + + ret = j.storage->check_files(j.piece, j.offset, j.error); + + ptime done = time_now_hires(); + m_hash_time.add_sample(total_microseconds(done - hash_start)); + m_cache_stats.cumulative_hash_time += total_milliseconds(done - hash_start); + + TORRENT_TRY { + TORRENT_ASSERT(j.callback); + if (j.callback && ret == piece_manager::need_full_check) + post_callback(j, ret); + } TORRENT_CATCH(std::exception&) {} + if (ret != piece_manager::need_full_check) break; + } + if (test_error(j)) + { + ret = piece_manager::fatal_disk_error; + break; + } + TORRENT_ASSERT(ret != -2 || j.error); + + // if the check is not done, add it at the end of the job queue + if (ret == piece_manager::need_full_check) + { + // offset needs to be reset to 0 so that the disk + // job sorting can be done correctly + j.offset = 0; + add_job(j, j.callback); + continue; + } + break; + } + case disk_io_job::save_resume_data: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " save_resume_data" << std::endl; +#endif + j.resume_data.reset(new entry(entry::dictionary_t)); + j.storage->write_resume_data(*j.resume_data); + ret = 0; + break; + } + case disk_io_job::rename_file: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " rename_file" << std::endl; +#endif + ret = j.storage->rename_file_impl(j.piece, j.str); + if (ret != 0) + { + test_error(j); + } + break; + } + case disk_io_job::file_priority: + { + std::vector* p + = reinterpret_cast*>(j.buffer); + j.storage->set_file_priority_impl(*p); + delete p; + ret = 0; + break; + } + } + } + TORRENT_CATCH(std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + ret = -1; + TORRENT_TRY { + j.str = e.what(); + } TORRENT_CATCH(std::exception&) {} + } + + TORRENT_ASSERT(!j.storage || !j.storage->error()); + + ptime done = time_now_hires(); + m_job_time.add_sample(total_microseconds(done - operation_start)); + m_cache_stats.cumulative_job_time += total_milliseconds(done - operation_start); + +// if (!j.callback) std::cerr << "DISK THREAD: no callback specified" << std::endl; +// else std::cerr << "DISK THREAD: invoking callback" << std::endl; + TORRENT_TRY { + TORRENT_ASSERT(ret != -2 || j.error + || j.action == disk_io_job::hash); +#if TORRENT_DISK_STATS + if ((j.action == disk_io_job::read || j.action == disk_io_job::read_and_hash) + && j.buffer != 0) + rename_buffer(j.buffer, "posted send buffer"); +#endif + post_callback(j, ret); + } TORRENT_CATCH(std::exception&) { + TORRENT_ASSERT(false); + } + } + TORRENT_ASSERT(false); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/entry.cpp b/apps/Launcher/ext/libtorrent/src/entry.cpp new file mode 100644 index 0000000000..93a7a30271 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/entry.cpp @@ -0,0 +1,627 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#if TORRENT_USE_IOSTREAM +#include +#endif +#include +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/escape_string.hpp" + +#if defined(_MSC_VER) +#define for if (false) {} else for +#endif + +namespace +{ + template + void call_destructor(T* o) + { + TORRENT_ASSERT(o); + o->~T(); + } +} + +namespace libtorrent +{ + namespace detail + { + TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val) + { + int sign = 0; + if (val < 0) + { + sign = 1; + val = -val; + } + buf[--size] = '\0'; + if (val == 0) buf[--size] = '0'; + for (; size > sign && val != 0;) + { + buf[--size] = '0' + char(val % 10); + val /= 10; + } + if (sign) buf[--size] = '-'; + return buf + size; + } + } + + entry& entry::operator[](char const* key) + { + dictionary_type::iterator i = dict().find(key); + if (i != dict().end()) return i->second; + dictionary_type::iterator ret = dict().insert( + std::pair(key, entry())).first; + return ret->second; + } + + entry& entry::operator[](std::string const& key) + { + dictionary_type::iterator i = dict().find(key); + if (i != dict().end()) return i->second; + dictionary_type::iterator ret = dict().insert( + std::make_pair(key, entry())).first; + return ret->second; + } + + entry* entry::find_key(char const* key) + { + dictionary_type::iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry const* entry::find_key(char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry* entry::find_key(std::string const& key) + { + dictionary_type::iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + entry const* entry::find_key(std::string const& key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + +#ifndef BOOST_NO_EXCEPTIONS + const entry& entry::operator[](char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) throw type_error( + (std::string("key not found: ") + key).c_str()); + return i->second; + } + + const entry& entry::operator[](std::string const& key) const + { + return (*this)[key.c_str()]; + } +#endif + + entry::data_type entry::type() const + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + return (entry::data_type)m_type; + } + + entry::~entry() { destruct(); } + + void entry::operator=(const entry& e) + { + destruct(); + copy(e); + } + + entry::integer_type& entry::integer() + { + if (m_type == undefined_t) construct(int_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != int_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == int_t); + return *reinterpret_cast(data); + } + + entry::integer_type const& entry::integer() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != int_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == int_t); + return *reinterpret_cast(data); + } + + entry::string_type& entry::string() + { + if (m_type == undefined_t) construct(string_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != string_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == string_t); + return *reinterpret_cast(data); + } + + entry::string_type const& entry::string() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != string_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == string_t); + return *reinterpret_cast(data); + } + + entry::list_type& entry::list() + { + if (m_type == undefined_t) construct(list_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != list_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == list_t); + return *reinterpret_cast(data); + } + + entry::list_type const& entry::list() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != list_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == list_t); + return *reinterpret_cast(data); + } + + entry::dictionary_type& entry::dict() + { + if (m_type == undefined_t) construct(dictionary_t); +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != dictionary_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == dictionary_t); + return *reinterpret_cast(data); + } + + entry::dictionary_type const& entry::dict() const + { +#ifndef BOOST_NO_EXCEPTIONS + if (m_type != dictionary_t) throw_type_error(); +#elif defined TORRENT_DEBUG + TORRENT_ASSERT(m_type_queried); +#endif + TORRENT_ASSERT(m_type == dictionary_t); + return *reinterpret_cast(data); + } + + entry::entry() + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + entry::entry(data_type t) + : m_type(undefined_t) + { + construct(t); +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + entry::entry(const entry& e) + : m_type(undefined_t) + { + copy(e); +#ifdef TORRENT_DEBUG + m_type_queried = e.m_type_queried; +#endif + } + + entry::entry(dictionary_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + entry::entry(string_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) string_type(v); + m_type = string_t; + } + + entry::entry(list_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) list_type(v); + m_type = list_t; + } + + entry::entry(integer_type const& v) + : m_type(undefined_t) + { +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + new(data) integer_type(v); + m_type = int_t; + } + + // convert a lazy_entry into an old skool entry + void entry::operator=(lazy_entry const& e) + { + switch (e.type()) + { + case lazy_entry::string_t: + this->string() = e.string_value(); + break; + case lazy_entry::int_t: + this->integer() = e.int_value(); + break; + case lazy_entry::dict_t: + { + dictionary_type& d = this->dict(); + for (int i = 0; i < e.dict_size(); ++i) + { + std::pair elem = e.dict_at(i); + d[elem.first] = *elem.second; + } + break; + } + case lazy_entry::list_t: + { + list_type& l = this->list(); + for (int i = 0; i < e.list_size(); ++i) + { + l.push_back(entry()); + l.back() = *e.list_at(i); + } + break; + } + case lazy_entry::none_t: + destruct(); + break; + } + } + + void entry::operator=(dictionary_type const& v) + { + destruct(); + new(data) dictionary_type(v); + m_type = dictionary_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(string_type const& v) + { + destruct(); + new(data) string_type(v); + m_type = string_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(list_type const& v) + { + destruct(); + new(data) list_type(v); + m_type = list_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::operator=(integer_type const& v) + { + destruct(); + new(data) integer_type(v); + m_type = int_t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + bool entry::operator==(entry const& e) const + { + if (m_type != e.m_type) return false; + + switch(m_type) + { + case int_t: + return integer() == e.integer(); + case string_t: + return string() == e.string(); + case list_t: + return list() == e.list(); + case dictionary_t: + return dict() == e.dict(); + default: + TORRENT_ASSERT(m_type == undefined_t); + return true; + } + } + + void entry::construct(data_type t) + { + switch(t) + { + case int_t: + new(data) integer_type; + break; + case string_t: + new(data) string_type; + break; + case list_t: + new(data) list_type; + break; + case dictionary_t: + new (data) dictionary_type; + break; + default: + TORRENT_ASSERT(t == undefined_t); + } + m_type = t; +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::copy(entry const& e) + { + switch (e.type()) + { + case int_t: + new(data) integer_type(e.integer()); + break; + case string_t: + new(data) string_type(e.string()); + break; + case list_t: + new(data) list_type(e.list()); + break; + case dictionary_t: + new (data) dictionary_type(e.dict()); + break; + default: + TORRENT_ASSERT(e.type() == undefined_t); + } + m_type = e.type(); +#ifdef TORRENT_DEBUG + m_type_queried = true; +#endif + } + + void entry::destruct() + { + switch(m_type) + { + case int_t: + call_destructor(reinterpret_cast(data)); + break; + case string_t: + call_destructor(reinterpret_cast(data)); + break; + case list_t: + call_destructor(reinterpret_cast(data)); + break; + case dictionary_t: + call_destructor(reinterpret_cast(data)); + break; + default: + TORRENT_ASSERT(m_type == undefined_t); + break; + } + m_type = undefined_t; +#ifdef TORRENT_DEBUG + m_type_queried = false; +#endif + } + + void entry::swap(entry& e) + { + bool clear_this = false; + bool clear_that = false; + + if (m_type == undefined_t && e.m_type == undefined_t) + return; + + if (m_type == undefined_t) + { + construct((data_type)e.m_type); + clear_that = true; + } + + if (e.m_type == undefined_t) + { + e.construct((data_type)m_type); + clear_this = true; + } + + if (m_type == e.m_type) + { + switch (m_type) + { + case int_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case string_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case list_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + case dictionary_t: + std::swap(*reinterpret_cast(data) + , *reinterpret_cast(e.data)); + break; + default: + break; + } + + if (clear_this) + destruct(); + + if (clear_that) + e.destruct(); + } + else + { + // currently, only swapping entries of the same type or where one + // of the entries is uninitialized is supported. + TORRENT_ASSERT(false && "not implemented"); + } + } + + std::string entry::to_string() const + { + std::string ret; + to_string_impl(ret, 0); + return ret; + } + + void entry::to_string_impl(std::string& out, int indent) const + { + TORRENT_ASSERT(indent >= 0); + for (int i = 0; i < indent; ++i) out += " "; + switch (m_type) + { + case int_t: + out += libtorrent::to_string(integer()).elems; + out += "\n"; + break; + case string_t: + { + bool binary_string = false; + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + { + if (!is_print(static_cast(*i))) + { + binary_string = true; + break; + } + } + if (binary_string) + { + out += to_hex(string()); + out += "\n"; + } + else + { + out += string(); + out += "\n"; + } + } break; + case list_t: + { + out += "list\n"; + for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) + { + i->to_string_impl(out, indent+1); + } + } break; + case dictionary_t: + { + out += "dictionary\n"; + for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) + { + bool binary_string = false; + for (std::string::const_iterator k = i->first.begin(); k != i->first.end(); ++k) + { + if (!is_print(static_cast(*k))) + { + binary_string = true; + break; + } + } + for (int j = 0; j < indent+1; ++j) out += " "; + out += "["; + if (binary_string) out += to_hex(i->first); + else out += i->first; + out += "]"; + + if (i->second.type() != entry::string_t + && i->second.type() != entry::int_t) + out += "\n"; + else out += " "; + i->second.to_string_impl(out, indent+2); + } + } break; + default: + out += "\n"; + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/enum_net.cpp b/apps/Launcher/ext/libtorrent/src/enum_net.cpp new file mode 100644 index 0000000000..0994a6304f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/enum_net.cpp @@ -0,0 +1,1066 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include +#include +#include // for wcstombscstombs +#include "libtorrent/enum_net.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/assert.hpp" +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#if TORRENT_USE_IFCONF +#include +#include +#include +#include +#include +#endif + +#if TORRENT_USE_SYSCTL +#include +#include +#include +#endif + +#if TORRENT_USE_GETIPFORWARDTABLE || TORRENT_USE_GETADAPTERSADDRESSES +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + +#if TORRENT_USE_NETLINK +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if TORRENT_USE_IFADDRS +#include +#endif + +#if defined(TORRENT_OS2) && !defined(IF_NAMESIZE) +#define IF_NAMESIZE IFNAMSIZ +#endif + +namespace libtorrent { namespace +{ + + address inaddr_to_address(in_addr const* ina, int len = 4) + { + typedef asio::ip::address_v4::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0, b.size()); + if (len > 0) std::memcpy(&b[0], ina, (std::min)(len, int(b.size()))); + return address_v4(b); + } + +#if TORRENT_USE_IPV6 + address inaddr6_to_address(in6_addr const* ina6, int len = 16) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0, b.size()); + if (len > 0) std::memcpy(&b[0], ina6, (std::min)(len, int(b.size()))); + return address_v6(b); + } +#endif + + int sockaddr_len(sockaddr const* sin) + { +#if TORRENT_HAS_SALEN + return sin->sa_len; +#else + return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); +#endif + } + + address sockaddr_to_address(sockaddr const* sin, int assume_family = -1) + { + if (sin->sa_family == AF_INET || assume_family == AF_INET) + return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr + , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); +#if TORRENT_USE_IPV6 + else if (sin->sa_family == AF_INET6 || assume_family == AF_INET6) + return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr + , sockaddr_len(sin) - offsetof(sockaddr, sa_data)); +#endif + return address(); + } + +#if TORRENT_USE_NETLINK + + int read_nl_sock(int sock, char *buf, int bufsize, int seq, int pid) + { + nlmsghdr* nl_hdr; + + int msg_len = 0; + + do + { + int read_len = recv(sock, buf, bufsize - msg_len, 0); + if (read_len < 0) return -1; + + nl_hdr = (nlmsghdr*)buf; + + if ((NLMSG_OK(nl_hdr, read_len) == 0) || (nl_hdr->nlmsg_type == NLMSG_ERROR)) + return -1; + + if (nl_hdr->nlmsg_type == NLMSG_DONE) break; + + buf += read_len; + msg_len += read_len; + + if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) break; + + } while((nl_hdr->nlmsg_seq != seq) || (nl_hdr->nlmsg_pid != pid)); + return msg_len; + } + + bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info) + { + rtmsg* rt_msg = (rtmsg*)NLMSG_DATA(nl_hdr); + + if((rt_msg->rtm_family != AF_INET && rt_msg->rtm_family != AF_INET6) || (rt_msg->rtm_table != RT_TABLE_MAIN + && rt_msg->rtm_table != RT_TABLE_LOCAL)) + return false; + + int if_index = 0; + int rt_len = RTM_PAYLOAD(nl_hdr); + for (rtattr* rt_attr = (rtattr*)RTM_RTA(rt_msg); + RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len)) + { + switch(rt_attr->rta_type) + { + case RTA_OIF: + if_index = *(int*)RTA_DATA(rt_attr); + break; + case RTA_GATEWAY: +#if TORRENT_USE_IPV6 + if (rt_msg->rtm_family == AF_INET6) + { + rt_info->gateway = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr)); + } + else +#endif + { + rt_info->gateway = inaddr_to_address((in_addr*)RTA_DATA(rt_attr)); + } + break; + case RTA_DST: +#if TORRENT_USE_IPV6 + if (rt_msg->rtm_family == AF_INET6) + { + rt_info->destination = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr)); + } + else +#endif + { + rt_info->destination = inaddr_to_address((in_addr*)RTA_DATA(rt_attr)); + } + break; + } + } + + if_indextoname(if_index, rt_info->name); + ifreq req; + memset(&req, 0, sizeof(req)); + if_indextoname(if_index, req.ifr_name); + ioctl(s, SIOCGIFMTU, &req); + rt_info->mtu = req.ifr_mtu; +// obviously this doesn't work correctly. How do you get the netmask for a route? +// if (ioctl(s, SIOCGIFNETMASK, &req) == 0) { +// rt_info->netmask = sockaddr_to_address(&req.ifr_addr, req.ifr_addr.sa_family); +// } + return true; + } +#endif + +#if TORRENT_USE_SYSCTL +#ifdef TORRENT_OS2 +int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); +#endif + + bool parse_route(int s, rt_msghdr* rtm, ip_route* rt_info) + { + sockaddr* rti_info[RTAX_MAX]; + sockaddr* sa = (sockaddr*)(rtm + 1); + for (int i = 0; i < RTAX_MAX; ++i) + { + if ((rtm->rtm_addrs & (1 << i)) == 0) + { + rti_info[i] = 0; + continue; + } + rti_info[i] = sa; + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + + sa = (sockaddr*)((char*)(sa) + ROUNDUP(sa->sa_len)); + +#undef ROUNDUP + } + + sa = rti_info[RTAX_GATEWAY]; + if (sa == 0 + || rti_info[RTAX_DST] == 0 + || rti_info[RTAX_NETMASK] == 0 + || (sa->sa_family != AF_INET +#if TORRENT_USE_IPV6 + && sa->sa_family != AF_INET6 +#endif + )) + return false; + + rt_info->gateway = sockaddr_to_address(rti_info[RTAX_GATEWAY]); + rt_info->destination = sockaddr_to_address(rti_info[RTAX_DST]); + rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK] + , rt_info->destination.is_v4() ? AF_INET : AF_INET6); + if_indextoname(rtm->rtm_index, rt_info->name); + + ifreq req; + memset(&req, 0, sizeof(req)); + if_indextoname(rtm->rtm_index, req.ifr_name); + if (ioctl(s, SIOCGIFMTU, &req) < 0) return false; + rt_info->mtu = req.ifr_mtu; + + return true; + } +#endif + +#if TORRENT_USE_IFADDRS + bool iface_from_ifaddrs(ifaddrs *ifa, ip_interface &rv, error_code& ec) + { + int family = ifa->ifa_addr->sa_family; + + if (family != AF_INET +#if TORRENT_USE_IPV6 + && family != AF_INET6 +#endif + ) + { + return false; + } + + strncpy(rv.name, ifa->ifa_name, sizeof(rv.name)); + rv.name[sizeof(rv.name)-1] = 0; + + // determine address + rv.interface_address = sockaddr_to_address(ifa->ifa_addr); + // determine netmask + if (ifa->ifa_netmask != NULL) + { + rv.netmask = sockaddr_to_address(ifa->ifa_netmask); + } + return true; + } +#endif + +}} // + +namespace libtorrent +{ + + // return (a1 & mask) == (a2 & mask) + bool match_addr_mask(address const& a1, address const& a2, address const& mask) + { + // all 3 addresses needs to belong to the same family + if (a1.is_v4() != a2.is_v4()) return false; + if (a1.is_v4() != mask.is_v4()) return false; + +#if TORRENT_USE_IPV6 + if (a1.is_v6()) + { + address_v6::bytes_type b1; + address_v6::bytes_type b2; + address_v6::bytes_type m; + b1 = a1.to_v6().to_bytes(); + b2 = a2.to_v6().to_bytes(); + m = mask.to_v6().to_bytes(); + for (int i = 0; i < int(b1.size()); ++i) + { + b1[i] &= m[i]; + b2[i] &= m[i]; + } + return memcmp(&b1[0], &b2[0], b1.size()) == 0; + } +#endif + return (a1.to_v4().to_ulong() & mask.to_v4().to_ulong()) + == (a2.to_v4().to_ulong() & mask.to_v4().to_ulong()); + } + + bool in_local_network(io_service& ios, address const& addr, error_code& ec) + { + std::vector net = enum_net_interfaces(ios, ec); + if (ec) return false; + for (std::vector::iterator i = net.begin() + , end(net.end()); i != end; ++i) + { + if (match_addr_mask(addr, i->interface_address, i->netmask)) + return true; + } + return false; + } + +#if TORRENT_USE_GETIPFORWARDTABLE + address build_netmask(int bits, int family) + { + if (family == AF_INET) + { + typedef asio::ip::address_v4::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0xff, b.size()); + for (int i = sizeof(bytes_t)/8-1; i > 0; --i) + { + if (bits < 8) + { + b[i] <<= bits; + break; + } + b[i] = 0; + bits -= 8; + } + return address_v4(b); + } +#if TORRENT_USE_IPV6 + else if (family == AF_INET6) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t b; + std::memset(&b[0], 0xff, b.size()); + for (int i = sizeof(bytes_t)/8-1; i > 0; --i) + { + if (bits < 8) + { + b[i] <<= bits; + break; + } + b[i] = 0; + bits -= 8; + } + return address_v6(b); + } +#endif + else + { + return address(); + } + } +#endif + + std::vector enum_net_interfaces(io_service& ios, error_code& ec) + { + std::vector ret; +#if TORRENT_USE_IFADDRS + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return ret; + } + + ifaddrs *ifaddr; + if (getifaddrs(&ifaddr) == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + + for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == 0) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + + int family = ifa->ifa_addr->sa_family; + if (family == AF_INET +#if TORRENT_USE_IPV6 + || family == AF_INET6 +#endif + ) + { + ip_interface iface; + if (iface_from_ifaddrs(ifa, iface, ec)) + { + ifreq req; + memset(&req, 0, sizeof(req)); + // -1 to leave a null terminator + strncpy(req.ifr_name, iface.name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFMTU, &req) < 0) + { + continue; + } + iface.mtu = req.ifr_mtu; + ret.push_back(iface); + } + } + } + close(s); + freeifaddrs(ifaddr); +// MacOS X, BSD and solaris +#elif TORRENT_USE_IFCONF + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return ret; + } + ifconf ifc; + // make sure the buffer is aligned to hold ifreq structs + ifreq buf[40]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = (char*)buf; + if (ioctl(s, SIOCGIFCONF, &ifc) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + + char *ifr = (char*)ifc.ifc_req; + int remaining = ifc.ifc_len; + + while (remaining > 0) + { + ifreq const& item = *reinterpret_cast(ifr); + +#ifdef _SIZEOF_ADDR_IFREQ + int current_size = _SIZEOF_ADDR_IFREQ(item); +#elif defined TORRENT_BSD + int current_size = item.ifr_addr.sa_len + IFNAMSIZ; +#else + int current_size = sizeof(ifreq); +#endif + + if (remaining < current_size) break; + + if (item.ifr_addr.sa_family == AF_INET +#if TORRENT_USE_IPV6 + || item.ifr_addr.sa_family == AF_INET6 +#endif + ) + { + ip_interface iface; + iface.interface_address = sockaddr_to_address(&item.ifr_addr); + strcpy(iface.name, item.ifr_name); + + ifreq req; + memset(&req, 0, sizeof(req)); + // -1 to leave a null terminator + strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFMTU, &req) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } +#ifndef TORRENT_OS2 + iface.mtu = req.ifr_mtu; +#else + iface.mtu = req.ifr_metric; // according to tcp/ip reference +#endif + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE - 1); + if (ioctl(s, SIOCGIFNETMASK, &req) < 0) + { +#if TORRENT_USE_IPV6 + if (iface.interface_address.is_v6()) + { + // this is expected to fail (at least on MacOS X) + iface.netmask = address_v6::any(); + } + else +#endif + { + ec = error_code(errno, asio::error::system_category); + close(s); + return ret; + } + } + else + { + iface.netmask = sockaddr_to_address(&req.ifr_addr, item.ifr_addr.sa_family); + } + ret.push_back(iface); + } + + ifr += current_size; + remaining -= current_size; + } + close(s); + +#elif TORRENT_USE_GETADAPTERSADDRESSES + +#if _WIN32_WINNT >= 0x0501 + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (iphlp) + { + // Get GetAdaptersAddresses() pointer + typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG); + GetAdaptersAddresses_t GetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress( + iphlp, "GetAdaptersAddresses"); + + if (GetAdaptersAddresses) + { + PIP_ADAPTER_ADDRESSES adapter_addresses = 0; + ULONG out_buf_size = 0; + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) != ERROR_BUFFER_OVERFLOW) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(out_buf_size); + if (!adapter_addresses) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) == NO_ERROR) + { + for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses; + adapter != 0; adapter = adapter->Next) + { + ip_interface r; + strncpy(r.name, adapter->AdapterName, sizeof(r.name)); + r.name[sizeof(r.name)-1] = 0; + r.mtu = adapter->Mtu; + IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; + while (unicast) + { + r.interface_address = sockaddr_to_address(unicast->Address.lpSockaddr); + + ret.push_back(r); + + unicast = unicast->Next; + } + } + } + + // Free memory + free(adapter_addresses); + FreeLibrary(iphlp); + return ret; + } + FreeLibrary(iphlp); + } +#endif + + SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == SOCKET_ERROR) + { + ec = error_code(WSAGetLastError(), asio::error::system_category); + return ret; + } + + INTERFACE_INFO buffer[30]; + DWORD size; + + if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, 0, 0, buffer, + sizeof(buffer), &size, 0, 0) != 0) + { + ec = error_code(WSAGetLastError(), asio::error::system_category); + closesocket(s); + return ret; + } + closesocket(s); + + int n = size / sizeof(INTERFACE_INFO); + + ip_interface iface; + for (int i = 0; i < n; ++i) + { + iface.interface_address = sockaddr_to_address(&buffer[i].iiAddress.Address); + if (iface.interface_address == address_v4::any()) continue; + iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address + , iface.interface_address.is_v4() ? AF_INET : AF_INET6); + iface.name[0] = 0; + iface.mtu = 1500; // how to get the MTU? + ret.push_back(iface); + } + +#else + +#ifdef _MSC_VER +#pragma message ( "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" ) +#else +#warning "THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK" +#endif + + // make a best guess of the interface we're using and its IP + udp::resolver r(ios); + udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(ec), "0"), ec); + if (ec) return ret; + ip_interface iface; + for (;i != udp::resolver_iterator(); ++i) + { + iface.interface_address = i->endpoint().address(); + iface.mtu = 1500; + if (iface.interface_address.is_v4()) + iface.netmask = address_v4::netmask(iface.interface_address.to_v4()); + ret.push_back(iface); + } +#endif + return ret; + } + + address get_default_gateway(io_service& ios, error_code& ec) + { + std::vector ret = enum_routes(ios, ec); +#if defined TORRENT_WINDOWS || defined TORRENT_MINGW + std::vector::iterator i = std::find_if(ret.begin(), ret.end() + , boost::bind(&is_loopback, boost::bind(&ip_route::destination, _1))); +#else + std::vector::iterator i = std::find_if(ret.begin(), ret.end() + , boost::bind(&ip_route::destination, _1) == address()); +#endif + if (i == ret.end()) return address(); + return i->gateway; + } + + std::vector enum_routes(io_service& ios, error_code& ec) + { + std::vector ret; + +#if TORRENT_USE_SYSCTL +/* + struct rt_msg + { + rt_msghdr m_rtm; + char buf[512]; + }; + + rt_msg m; + int len = sizeof(rt_msg); + bzero(&m, len); + m.m_rtm.rtm_type = RTM_GET; + m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + m.m_rtm.rtm_version = RTM_VERSION; + m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + m.m_rtm.rtm_seq = 0; + m.m_rtm.rtm_msglen = len; + + int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (s == -1) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + int n = write(s, &m, len); + if (n == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return std::vector(); + } + else if (n != len) + { + ec = asio::error::operation_not_supported; + close(s); + return std::vector(); + } + bzero(&m, len); + + n = read(s, &m, len); + if (n == -1) + { + ec = error_code(errno, asio::error::system_category); + close(s); + return std::vector(); + } + + for (rt_msghdr* ptr = &m.m_rtm; (char*)ptr < ((char*)&m.m_rtm) + n; ptr = (rt_msghdr*)(((char*)ptr) + ptr->rtm_msglen)) + { + std::cout << " rtm_msglen: " << ptr->rtm_msglen << std::endl; + std::cout << " rtm_type: " << ptr->rtm_type << std::endl; + if (ptr->rtm_errno) + { + ec = error_code(ptr->rtm_errno, asio::error::system_category); + return std::vector(); + } + if (m.m_rtm.rtm_flags & RTF_UP == 0 + || m.m_rtm.rtm_flags & RTF_GATEWAY == 0) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + if (ptr->rtm_addrs & RTA_DST == 0 + || ptr->rtm_addrs & RTA_GATEWAY == 0 + || ptr->rtm_addrs & RTA_NETMASK == 0) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + if (ptr->rtm_msglen > len - ((char*)ptr - ((char*)&m.m_rtm))) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + int min_len = sizeof(rt_msghdr) + 2 * sizeof(sockaddr_in); + if (m.m_rtm.rtm_msglen < min_len) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + ip_route r; + // destination + char* p = m.buf; + sockaddr_in* sin = (sockaddr_in*)p; + r.destination = sockaddr_to_address((sockaddr*)p); + + // gateway + p += sin->sin_len; + sin = (sockaddr_in*)p; + r.gateway = sockaddr_to_address((sockaddr*)p); + + // netmask + p += sin->sin_len; + sin = (sockaddr_in*)p; + r.netmask = sockaddr_to_address((sockaddr*)p); + ret.push_back(r); + } + close(s); +*/ + int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0}; + + size_t needed = 0; +#ifdef TORRENT_OS2 + if (__libsocket_sysctl(mib, 6, 0, &needed, 0, 0) < 0) +#else + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) +#endif + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + if (needed <= 0) + { + return std::vector(); + } + + boost::scoped_array buf(new (std::nothrow) char[needed]); + if (buf.get() == 0) + { + ec = asio::error::no_memory; + return std::vector(); + } + +#ifdef TORRENT_OS2 + if (__libsocket_sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) +#else + if (sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) +#endif + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + char* end = buf.get() + needed; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + rt_msghdr* rtm; + for (char* next = buf.get(); next < end; next += rtm->rtm_msglen) + { + rtm = (rt_msghdr*)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + + ip_route r; + if (parse_route(s, rtm, &r)) ret.push_back(r); + } + close(s); + +#elif TORRENT_USE_GETIPFORWARDTABLE +/* + move this to enum_net_interfaces + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (!iphlp) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + // Get GetAdaptersInfo() pointer + typedef DWORD (WINAPI *GetAdaptersInfo_t)(PIP_ADAPTER_INFO, PULONG); + GetAdaptersInfo_t GetAdaptersInfo = (GetAdaptersInfo_t)GetProcAddress(iphlp, "GetAdaptersInfo"); + if (!GetAdaptersInfo) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + PIP_ADAPTER_INFO adapter_info = 0; + ULONG out_buf_size = 0; + if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + adapter_info = (IP_ADAPTER_INFO*)malloc(out_buf_size); + if (!adapter_info) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) + { + for (PIP_ADAPTER_INFO adapter = adapter_info; + adapter != 0; adapter = adapter->Next) + { + + ip_route r; + r.destination = address::from_string(adapter->IpAddressList.IpAddress.String, ec); + r.gateway = address::from_string(adapter->GatewayList.IpAddress.String, ec); + r.netmask = address::from_string(adapter->IpAddressList.IpMask.String, ec); + strncpy(r.name, adapter->AdapterName, sizeof(r.name)); + + if (ec) + { + ec = error_code(); + continue; + } + ret.push_back(r); + } + } + + // Free memory + free(adapter_info); + FreeLibrary(iphlp); +*/ + + // Load Iphlpapi library + HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); + if (!iphlp) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + + typedef DWORD (WINAPI *GetIfEntry_t)(PMIB_IFROW pIfRow); + GetIfEntry_t GetIfEntry = (GetIfEntry_t)GetProcAddress(iphlp, "GetIfEntry"); + if (!GetIfEntry) + { + ec = asio::error::operation_not_supported; + return std::vector(); + } + +#if _WIN32_WINNT >= 0x0600 + typedef DWORD (WINAPI *GetIpForwardTable2_t)( + ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*); + typedef void (WINAPI *FreeMibTable_t)(PVOID Memory); + + GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress( + iphlp, "GetIpForwardTable2"); + FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress( + iphlp, "FreeMibTable"); + if (GetIpForwardTable2 && FreeMibTable) + { + MIB_IPFORWARD_TABLE2* routes = NULL; + int res = GetIpForwardTable2(AF_UNSPEC, &routes); + if (res == NO_ERROR) + { + for (int i = 0; i < routes->NumEntries; ++i) + { + ip_route r; + r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop); + r.destination = sockaddr_to_address( + (const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix); + r.netmask = build_netmask(routes->Table[i].SitePrefixLength + , routes->Table[i].DestinationPrefix.Prefix.si_family); + MIB_IFROW ifentry; + ifentry.dwIndex = routes->Table[i].InterfaceIndex; + if (GetIfEntry(&ifentry) == NO_ERROR) + { + wcstombs(r.name, ifentry.wszName, sizeof(r.name)); + r.mtu = ifentry.dwMtu; + ret.push_back(r); + } + } + } + if (routes) FreeMibTable(routes); + FreeLibrary(iphlp); + return ret; + } +#endif + + // Get GetIpForwardTable() pointer + typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder); + + GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress( + iphlp, "GetIpForwardTable"); + if (!GetIpForwardTable) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + MIB_IPFORWARDTABLE* routes = NULL; + ULONG out_buf_size = 0; + if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER) + { + FreeLibrary(iphlp); + ec = asio::error::operation_not_supported; + return std::vector(); + } + + routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size); + if (!routes) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR) + { + for (int i = 0; i < routes->dwNumEntries; ++i) + { + ip_route r; + r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest); + r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask); + r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop); + MIB_IFROW ifentry; + ifentry.dwIndex = routes->table[i].dwForwardIfIndex; + if (GetIfEntry(&ifentry) == NO_ERROR) + { + wcstombs(r.name, ifentry.wszName, sizeof(r.name)); + r.name[sizeof(r.name)-1] = 0; + r.mtu = ifentry.dwMtu; + ret.push_back(r); + } + } + } + + // Free memory + free(routes); + FreeLibrary(iphlp); +#elif TORRENT_USE_NETLINK + enum { BUFSIZE = 8192 }; + + int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + + int seq = 0; + + char msg[BUFSIZE]; + memset(msg, 0, BUFSIZE); + nlmsghdr* nl_msg = (nlmsghdr*)msg; + + nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); + nl_msg->nlmsg_type = RTM_GETROUTE; + nl_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + nl_msg->nlmsg_seq = seq++; + nl_msg->nlmsg_pid = getpid(); + + if (send(sock, nl_msg, nl_msg->nlmsg_len, 0) < 0) + { + ec = error_code(errno, asio::error::system_category); + close(sock); + return std::vector(); + } + + int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid()); + if (len < 0) + { + ec = error_code(errno, asio::error::system_category); + close(sock); + return std::vector(); + } + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } + for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len)) + { + ip_route r; + if (parse_route(s, nl_msg, &r)) ret.push_back(r); + } + close(s); + close(sock); + +#endif + return ret; + } + +} + + diff --git a/apps/Launcher/ext/libtorrent/src/error_code.cpp b/apps/Launcher/ext/libtorrent/src/error_code.cpp new file mode 100644 index 0000000000..68853a1026 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/error_code.cpp @@ -0,0 +1,355 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/escape_string.hpp" // for to_string() + +namespace libtorrent +{ +#if BOOST_VERSION >= 103500 + + struct libtorrent_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* libtorrent_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "libtorrent error"; + } + + std::string libtorrent_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "torrent file collides with file from another torrent", + "hash check failed", + "torrent file is not a dictionary", + "missing or invalid 'info' section in torrent file", + "'info' entry is not a dictionary", + "invalid or missing 'piece length' entry in torrent file", + "missing name in torrent file", + "invalid 'name' of torrent (possible exploit attempt)", + "invalid length of torrent", + "failed to parse files from torrent file", + "invalid or missing 'pieces' entry in torrent file", + "incorrect number of piece hashes in torrent file", + "too many pieces in torrent", + "invalid metadata received from swarm", + "invalid bencoding", + "no files in torrent", + "invalid escaped string", + "session is closing", + "torrent already exists in session", + "invalid torrent handle used", + "invalid type requested from entry", + "missing info-hash from URI", + "file too short", + "unsupported URL protocol", + "failed to parse URL", + "peer sent 0 length piece", + "parse failed", + "invalid file format tag", + "missing info-hash", + "mismatching info-hash", + "invalid hostname", + "invalid port", + "port blocked by port-filter", + "expected closing ] for address", + "destructing torrent", + "timed out", + "upload to upload connection", + "uninteresting upload-only peer", + "invalid info-hash", + "torrent paused", + "'have'-message with higher index than the number of pieces", + "bitfield of invalid size", + "too many piece requests while choked", + "invalid piece packet", + "out of memory", + "torrent aborted", + "connected to ourselves", + "invalid piece size", + "timed out: no interest", + "timed out: inactivity", + "timed out: no handshake", + "timed out: no request", + "invalid choke message", + "invalid unchoke message", + "invalid interested message", + "invalid not-interested message", + "invalid request message", + "invalid hash list", + "invalid hash piece message", + "invalid cancel message", + "invalid dht-port message", + "invalid suggest piece message", + "invalid have-all message", + "invalid have-none message", + "invalid reject message", + "invalid allow-fast message", + "invalid extended message", + "invalid message", + "sync hash not found", + "unable to verify encryption constant", + "plaintext mode not provided", + "rc4 mode not provided", + "unsupported encryption mode", + "peer selected unsupported encryption mode", + "invalid encryption pad size", + "invalid encryption handshake", + "incoming encrypted connections disabled", + "incoming regular connections disabled", + "duplicate peer-id", + "torrent removed", + "packet too large", + "", + "HTTP error", + "missing location header", + "invalid redirection", + "redirecting", + "invalid HTTP range", + "missing content-length", + "banned by IP filter", + "too many connections", + "peer banned", + "stopping torrent", + "too many corrupt pieces", + "torrent is not ready to accept peers", + "peer is not properly constructed", + "session is closing", + "optimistic disconnect", + "torrent finished", + "no router found", + "metadata too large", + "invalid metadata request", + "invalid metadata size", + "invalid metadata offset", + "invalid metadata message", + "pex message too large", + "invalid pex message", + "invalid lt_tracker message", + "pex messages sent too frequent (possible attack)", + "torrent has no metadata", + "invalid dont-have message", + "SSL connection required", + "invalid SSL certificate", + "not an SSL torrent", + "", + "", + "", + "", + "", + "", + +// natpmp errors + "unsupported protocol version", + "not authorized to create port map (enable NAT-PMP on your router)", + "network failure", + "out of resources", + "unsupported opcode", + "", + "", + "", + "", + "", + +// fastresume errors + "missing or invalid 'file sizes' entry", + "no files in resume data", + "missing 'slots' and 'pieces' entry", + "mismatching number of files", + "mismatching file size", + "mismatching file timestamp", + "not a dictionary", + "invalid 'blocks per piece' entry", + "missing slots list", + "file has more slots than torrent", + "invalid entry type in slot list", + "invalid piece index in slot list", + "pieces needs to be reordered", + "", + "", + "", + "", + "", + "", + "", + +// HTTP errors + "Invalid HTTP header", + "missing Location header in HTTP redirect", + "failed to decompress HTTP response", + "", + "", + "", + "", + "", + "", + "", + +// i2p errors + "no i2p router is set up", + "", + "", + "", + "", + "", + "", + "", + "", + "", + +// tracker errors + "scrape not available on tracker", + "invalid tracker response", + "invalid peer dictionary entry", + "tracker sent a failure message", + "missing or invalid 'files' entry", + "missing or invalid 'hash' entry", + "missing or invalid 'peers' and 'peers6' entry", + "udp tracker response packet has invalid size", + "invalid transaction id in udp tracker response", + "invalid action field in udp tracker response", +#ifndef TORRENT_NO_DEPRECATE + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + +// bdecode errors + "expected string in bencoded string", + "expected colon in bencoded string", + "unexpected end of file in bencoded string", + "expected value (list, dict, int or string) in bencoded string", + "bencoded nesting depth exceeded", + "bencoded item count limit exceeded", + "integer overflow", +#endif + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_libtorrent_category() + { + static libtorrent_error_category libtorrent_category; + return libtorrent_category; + } + + struct TORRENT_EXPORT http_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "http error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + std::string ret; + ret += to_string(ev).elems; + ret += " "; + switch (ev) + { + case errors::cont: ret += "Continue"; break; + case errors::ok: ret += "OK"; break; + case errors::created: ret += "Created"; break; + case errors::accepted: ret += "Accepted"; break; + case errors::no_content: ret += "No Content"; break; + case errors::multiple_choices: ret += "Multiple Choices"; break; + case errors::moved_permanently: ret += "Moved Permanently"; break; + case errors::moved_temporarily: ret += "Moved Temporarily"; break; + case errors::not_modified: ret += "Not Modified"; break; + case errors::bad_request: ret += "Bad Request"; break; + case errors::unauthorized: ret += "Unauthorized"; break; + case errors::forbidden: ret += "Forbidden"; break; + case errors::not_found: ret += "Not Found"; break; + case errors::internal_server_error: ret += "Internal Server Error"; break; + case errors::not_implemented: ret += "Not Implemented"; break; + case errors::bad_gateway: ret += "Bad Gateway"; break; + case errors::service_unavailable: ret += "Service Unavailable"; break; + default: ret += "(unknown HTTP error)"; break; + } + return ret; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + boost::system::error_category& get_http_category() + { + static http_error_category http_category; + return http_category; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + const char* libtorrent_exception::what() const throw() + { + if (!m_msg) + { + std::string msg = convert_from_native(m_error.message()); + m_msg = allocate_string_copy(msg.c_str()); + } + + return m_msg; + } + + libtorrent_exception::~libtorrent_exception() throw() + { + free(m_msg); + } +#endif + + namespace errors + { + // hidden + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_libtorrent_category()); + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/escape_string.cpp b/apps/Launcher/ext/libtorrent/src/escape_string.cpp new file mode 100644 index 0000000000..e6400314dd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/escape_string.cpp @@ -0,0 +1,641 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/random.hpp" + +#ifdef TORRENT_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#include "libtorrent/utf8.hpp" +#include "libtorrent/thread.hpp" + +#if TORRENT_USE_ICONV +#include +#include +#endif + +namespace libtorrent +{ + + // lexical_cast's result depends on the locale. We need + // a well defined result + boost::array::digits10> to_string(size_type n) + { + boost::array::digits10> ret; + char *p = &ret.back(); + *p = '\0'; + unsigned_size_type un = n; + if (n < 0) un = -un; // TODO: warning C4146: unary minus operator applied to unsigned type, result still unsigned + do { + *--p = '0' + un % 10; + un /= 10; + } while (un); + if (n < 0) *--p = '-'; + std::memmove(&ret[0], p, &ret.back() - p + 1); + return ret; + } + + std::string unescape_string(std::string const& s, error_code& ec) + { + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + { + if(*i == '+') + { + ret += ' '; + } + else if (*i != '%') + { + ret += *i; + } + else + { + ++i; + if (i == s.end()) + { + ec = errors::invalid_escaped_string; + return ret; + } + + int high; + if(*i >= '0' && *i <= '9') high = *i - '0'; + else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a'; + else + { + ec = errors::invalid_escaped_string; + return ret; + } + + ++i; + if (i == s.end()) + { + ec = errors::invalid_escaped_string; + return ret; + } + + int low; + if(*i >= '0' && *i <= '9') low = *i - '0'; + else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a'; + else + { + ec = errors::invalid_escaped_string; + return ret; + } + + ret += char(high * 16 + low); + } + } + return ret; + } + + // http://www.ietf.org/rfc/rfc2396.txt + // section 2.3 + static const char unreserved_chars[] = + // when determining if a url needs encoding + // % should be ok + "%+" + // reserved + ";?:@=&,$/" + // unreserved (special characters) ' excluded, + // since some buggy trackers fail with those + "-_!.~*()" + // unreserved (alphanumerics) + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + static const char hex_chars[] = "0123456789abcdef"; + + // the offset is used to ignore the first characters in the unreserved_chars table. + static std::string escape_string_impl(const char* str, int len, int offset) + { + TORRENT_ASSERT(str != 0); + TORRENT_ASSERT(len >= 0); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset < int(sizeof(unreserved_chars))-1); + + std::string ret; + for (int i = 0; i < len; ++i) + { + if (std::strchr(unreserved_chars+offset, *str) && *str != 0) + { + ret += *str; + } + else + { + ret += '%'; + ret += hex_chars[((unsigned char)*str) >> 4]; + ret += hex_chars[((unsigned char)*str) & 15]; + } + ++str; + } + return ret; + } + + std::string escape_string(const char* str, int len) + { + return escape_string_impl(str, len, 11); + } + + std::string escape_path(const char* str, int len) + { + return escape_string_impl(str, len, 10); + } + + bool need_encoding(char const* str, int len) + { + for (int i = 0; i < len; ++i) + { + if (std::strchr(unreserved_chars, *str) == 0 || *str == 0) + return true; + ++str; + } + return false; + } + + void convert_path_to_posix(std::string& path) + { + for (std::string::iterator i = path.begin() + , end(path.end()); i != end; ++i) + if (*i == '\\') *i = '/'; + } + + std::string read_until(char const*& str, char delim, char const* end) + { + TORRENT_ASSERT(str <= end); + + std::string ret; + while (str != end && *str != delim) + { + ret += *str; + ++str; + } + // skip the delimiter as well + while (str != end && *str == delim) ++str; + return ret; + } + + std::string maybe_url_encode(std::string const& url) + { + std::string protocol, host, auth, path; + int port; + error_code ec; + boost::tie(protocol, auth, host, port, path) = parse_url_components(url, ec); + if (ec) return url; + + // first figure out if this url contains unencoded characters + if (!need_encoding(path.c_str(), path.size())) + return url; + + char msg[TORRENT_MAX_PATH*4]; + snprintf(msg, sizeof(msg), "%s://%s%s%s%s%s%s", protocol.c_str(), auth.c_str() + , auth.empty()?"":"@", host.c_str() + , port == -1 ? "" : ":" + , port == -1 ? "" : to_string(port).elems + , escape_path(path.c_str(), path.size()).c_str()); + return msg; + } + + std::string base64encode(const std::string& s) + { + static const char base64_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + unsigned char inbuf[3]; + unsigned char outbuf[4]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + // available input is 1,2 or 3 bytes + // since we read 3 bytes at a time at most + int available_input = (std::min)(3, int(s.end()-i)); + + // clear input buffer + std::fill(inbuf, inbuf+3, 0); + + // read a chunk of input into inbuf + std::copy(i, i + available_input, inbuf); + i += available_input; + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xfc) >> 2; + outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4); + outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6); + outbuf[3] = inbuf[2] & 0x3f; + + // write output + for (int j = 0; j < available_input+1; ++j) + { + ret += base64_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 3 - available_input; ++j) + { + ret += '='; + } + } + return ret; + } + + std::string base32encode(std::string const& s) + { + static const char base32_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '2', '3', '4', '5', '6', '7' + }; + + int input_output_mapping[] = {0, 2, 4, 5, 7, 8}; + + unsigned char inbuf[5]; + unsigned char outbuf[8]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + int available_input = (std::min)(5, int(s.end()-i)); + + // clear input buffer + std::fill(inbuf, inbuf+5, 0); + + // read a chunk of input into inbuf + std::copy(i, i + available_input, inbuf); + i += available_input; + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xf8) >> 3; + outbuf[1] = ((inbuf[0] & 0x07) << 2) | ((inbuf[1] & 0xc0) >> 6); + outbuf[2] = ((inbuf[1] & 0x3e) >> 1); + outbuf[3] = ((inbuf[1] & 0x01) << 4) | ((inbuf[2] & 0xf0) >> 4); + outbuf[4] = ((inbuf[2] & 0x0f) << 1) | ((inbuf[3] & 0x80) >> 7); + outbuf[5] = ((inbuf[3] & 0x7c) >> 2); + outbuf[6] = ((inbuf[3] & 0x03) << 3) | ((inbuf[4] & 0xe0) >> 5); + outbuf[7] = inbuf[4] & 0x1f; + + // write output + int num_out = input_output_mapping[available_input]; + for (int j = 0; j < num_out; ++j) + { + ret += base32_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 8 - num_out; ++j) + { + ret += '='; + } + } + return ret; + } + + std::string base32decode(std::string const& s) + { + unsigned char inbuf[8]; + unsigned char outbuf[5]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + int available_input = (std::min)(8, int(s.end()-i)); + + int pad_start = 0; + if (available_input < 8) pad_start = available_input; + + // clear input buffer + std::fill(inbuf, inbuf+8, 0); + for (int j = 0; j < available_input; ++j) + { + char in = std::toupper(*i++); + if (in >= 'A' && in <= 'Z') + inbuf[j] = in - 'A'; + else if (in >= '2' && in <= '7') + inbuf[j] = in - '2' + ('Z' - 'A') + 1; + else if (in == '=') + { + inbuf[j] = 0; + if (pad_start == 0) pad_start = j; + } + else if (in == '1') + inbuf[j] = 'I' - 'A'; + else + return std::string(); + TORRENT_ASSERT(inbuf[j] == (inbuf[j] & 0x1f)); + } + + // decode inbuf to outbuf + outbuf[0] = inbuf[0] << 3; + outbuf[0] |= inbuf[1] >> 2; + outbuf[1] = (inbuf[1] & 0x3) << 6; + outbuf[1] |= inbuf[2] << 1; + outbuf[1] |= (inbuf[3] & 0x10) >> 4; + outbuf[2] = (inbuf[3] & 0x0f) << 4; + outbuf[2] |= (inbuf[4] & 0x1e) >> 1; + outbuf[3] = (inbuf[4] & 0x01) << 7; + outbuf[3] |= (inbuf[5] & 0x1f) << 2; + outbuf[3] |= (inbuf[6] & 0x18) >> 3; + outbuf[4] = (inbuf[6] & 0x07) << 5; + outbuf[4] |= inbuf[7]; + + int input_output_mapping[] = {5, 1, 1, 2, 2, 3, 4, 4, 5}; + int num_out = input_output_mapping[pad_start]; + + // write output + std::copy(outbuf, outbuf + num_out, std::back_inserter(ret)); + } + return ret; + } + + std::string url_has_argument( + std::string const& url, std::string argument, std::string::size_type* out_pos) + { + size_t i = url.find('?'); + if (i == std::string::npos) return std::string(); + ++i; + + argument += '='; + + if (url.compare(i, argument.size(), argument) == 0) + { + size_t pos = i + argument.size(); + if (out_pos) *out_pos = pos; + return url.substr(pos, url.find('&', pos) - pos); + } + argument.insert(0, "&"); + i = url.find(argument, i); + if (i == std::string::npos) return std::string(); + size_t pos = i + argument.size(); + if (out_pos) *out_pos = pos; + return url.substr(pos, url.find('&', pos) - pos); + } + + TORRENT_EXTRA_EXPORT std::string to_hex(std::string const& s) + { + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + { + ret += hex_chars[((unsigned char)*i) >> 4]; + ret += hex_chars[((unsigned char)*i) & 0xf]; + } + return ret; + } + + TORRENT_EXTRA_EXPORT void to_hex(char const *in, int len, char* out) + { + for (char const* end = in + len; in < end; ++in) + { + *out++ = hex_chars[((unsigned char)*in) >> 4]; + *out++ = hex_chars[((unsigned char)*in) & 0xf]; + } + *out = '\0'; + } + + TORRENT_EXTRA_EXPORT int hex_to_int(char in) + { + if (in >= '0' && in <= '9') return int(in) - '0'; + if (in >= 'A' && in <= 'F') return int(in) - 'A' + 10; + if (in >= 'a' && in <= 'f') return int(in) - 'a' + 10; + return -1; + } + + TORRENT_EXTRA_EXPORT bool is_hex(char const *in, int len) + { + for (char const* end = in + len; in < end; ++in) + { + int t = hex_to_int(*in); + if (t == -1) return false; + } + return true; + } + + TORRENT_EXTRA_EXPORT bool from_hex(char const *in, int len, char* out) + { + for (char const* end = in + len; in < end; ++in, ++out) + { + int t = hex_to_int(*in); + if (t == -1) return false; + *out = t << 4; + ++in; + t = hex_to_int(*in); + if (t == -1) return false; + *out |= t & 15; + } + return true; + } + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + std::wstring convert_to_wstring(std::string const& s) + { + std::wstring ret; + int result = libtorrent::utf8_wchar(s, ret); + if (result == 0) return ret; + + ret.clear(); + const char* end = &s[0] + s.size(); + for (const char* i = &s[0]; i < end;) + { + wchar_t c = '.'; + result = std::mbtowc(&c, i, end - i); + if (result > 0) i += result; + else ++i; + ret += c; + } + return ret; + } + + std::string convert_from_wstring(std::wstring const& s) + { + std::string ret; + int result = libtorrent::wchar_utf8(s, ret); + if (result == 0) return ret; + + ret.clear(); + const wchar_t* end = &s[0] + s.size(); + for (const wchar_t* i = &s[0]; i < end;) + { + char c[10]; + TORRENT_ASSERT(sizeof(c) >= MB_CUR_MAX); + result = std::wctomb(c, *i); + if (result > 0) + { + i += result; + ret.append(c, result); + } + else + { + ++i; + ret += "."; + } + } + return ret; + } +#endif + +#if TORRENT_USE_ICONV + std::string iconv_convert_impl(std::string const& s, iconv_t h) + { + std::string ret; + size_t insize = s.size(); + size_t outsize = insize * 4; + ret.resize(outsize); + char const* in = s.c_str(); + char* out = &ret[0]; + // posix has a weird iconv signature. implementations + // differ on what this signature should be, so we use + // a macro to let config.hpp determine it + size_t retval = iconv(h, TORRENT_ICONV_ARG &in, &insize, + &out, &outsize); + if (retval == (size_t)-1) return s; + // if this string has an invalid utf-8 sequence in it, don't touch it + if (insize != 0) return s; + // not sure why this would happen, but it seems to be possible + if (outsize > s.size() * 4) return s; + // outsize is the number of bytes unused of the out-buffer + TORRENT_ASSERT(ret.size() >= outsize); + ret.resize(ret.size() - outsize); + return ret; + } + + std::string convert_to_native(std::string const& s) + { + static mutex iconv_mutex; + // only one thread can use this handle at a time + mutex::scoped_lock l(iconv_mutex); + + // the empty string represents the local dependent encoding + static iconv_t iconv_handle = iconv_open("", "UTF-8"); + if (iconv_handle == iconv_t(-1)) return s; + return iconv_convert_impl(s, iconv_handle); + } + + std::string convert_from_native(std::string const& s) + { + static mutex iconv_mutex; + // only one thread can use this handle at a time + mutex::scoped_lock l(iconv_mutex); + + // the empty string represents the local dependent encoding + static iconv_t iconv_handle = iconv_open("UTF-8", ""); + if (iconv_handle == iconv_t(-1)) return s; + return iconv_convert_impl(s, iconv_handle); + } + +#elif defined TORRENT_WINDOWS + + std::string convert_to_native(std::string const& s) + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::string ret; + ret.resize(ws.size() * 4 + 1); + std::size_t size = WideCharToMultiByte(CP_ACP, 0, ws.c_str(), -1, &ret[0], ret.size(), NULL, NULL); + if (size == std::size_t(-1)) return s; + if (size != 0 && ret[size - 1] == '\0') --size; + ret.resize(size); + return ret; + } + + std::string convert_from_native(std::string const& s) + { + std::wstring ws; + ws.resize(s.size() + 1); + std::size_t size = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, &ws[0], ws.size()); + if (size == std::size_t(-1)) return s; + if (size != 0 && ws[size - 1] == '\0') --size; + ws.resize(size); + std::string ret; + libtorrent::wchar_utf8(ws, ret); + return ret; + } + +#elif TORRENT_USE_LOCALE + + std::string convert_to_native(std::string const& s) + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::size_t size = wcstombs(0, ws.c_str(), 0); + if (size == std::size_t(-1)) return s; + std::string ret; + ret.resize(size); + size = wcstombs(&ret[0], ws.c_str(), size + 1); + if (size == std::size_t(-1)) return s; + ret.resize(size); + return ret; + } + + std::string convert_from_native(std::string const& s) + { + std::wstring ws; + ws.resize(s.size()); + std::size_t size = mbstowcs(&ws[0], s.c_str(), s.size()); + if (size == std::size_t(-1)) return s; + std::string ret; + libtorrent::wchar_utf8(ws, ret); + return ret; + } + +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file.cpp b/apps/Launcher/ext/libtorrent/src/file.cpp new file mode 100644 index 0000000000..a3219b0862 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file.cpp @@ -0,0 +1,2412 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + Physical file offset patch by Morten Husveit +*/ + +#define _FILE_OFFSET_BITS 64 +#define _LARGE_FILES 1 + +// on mingw this is necessary to enable 64-bit time_t, specifically used for +// the stat struct. Without this, modification times returned by stat may be +// incorrect and consistently fail resume data +#ifndef __MINGW_USE_VC2005_COMPAT +# define __MINGW_USE_VC2005_COMPAT +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/allocator.hpp" // page_size +#include "libtorrent/escape_string.hpp" // for string conversion + +#include +#include + +#include + +#ifdef TORRENT_WINDOWS +// windows part + +#ifndef PtrToPtr64 +#define PtrToPtr64(x) (x) +#endif + +#include "libtorrent/utf8.hpp" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#ifndef TORRENT_MINGW +#include // for _getcwd, _mkdir +#else +#include +#endif +#include +#else +// posix part + +#include +#include // for F_LOG2PHYS +#include +#include +#include + +#ifdef TORRENT_LINUX +// linux specifics + +#ifdef TORRENT_ANDROID +#include +#define statvfs statfs +#define fstatvfs fstatfs +#else +#include +#endif + +#include +#include +#ifdef HAVE_LINUX_FIEMAP_H +#include // FIEMAP_* +#include // FS_IOC_FIEMAP +#endif + +#ifdef TORRENT_ANDROID +#include +#define lseek lseek64 +#endif + +#include // For __NR_fallocate + +// circumvent the lack of support in glibc +static int my_fallocate(int fd, int mode, loff_t offset, loff_t len) +{ +#ifdef __NR_fallocate + // the man page on fallocate differes between versions of linux. + // it appears that fallocate in fact sets errno and returns -1 + // on failure. + return syscall(__NR_fallocate, fd, mode, offset, len); +#else + // pretend that the system call doesn't exist + errno = ENOSYS; + return -1; +#endif +} + +#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 +// mac specifics + +#include + +#endif + +#undef _FILE_OFFSET_BITS + +// make sure the _FILE_OFFSET_BITS define worked +// on this platform. It's supposed to make file +// related functions support 64-bit offsets. +// this test makes sure lseek() returns a type +// at least 64 bits wide +BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); + +#endif // posix part + +#include "libtorrent/file.hpp" +#include +#include + +// for convert_to_wstring and convert_to_native +#include "libtorrent/escape_string.hpp" +#include +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_DEBUG +BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::no_buffer) == 0); +BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0); +BOOST_STATIC_ASSERT((libtorrent::file::no_buffer & libtorrent::file::attribute_mask) == 0); +#endif + +#if defined TORRENT_WINDOWS && defined UNICODE && !TORRENT_USE_WSTRING + +#ifdef _MSC_VER +#pragma message ( "wide character support not available. Files will be saved using narrow string names" ) +#else +#warning "wide character support not available. Files will be saved using narrow string names" +#endif + +#endif // TORRENT_WINDOWS + +namespace libtorrent +{ + +#ifdef TORRENT_WINDOWS + std::string convert_separators(std::string p) + { + for (int i = 0; i < int(p.size()); ++i) + if (p[i] == '/') p[i] = '\\'; + return p; + } + + time_t file_time_to_posix(FILETIME f) + { + const boost::uint64_t posix_time_offset = 11644473600LL; + boost::uint64_t ft = (boost::uint64_t(f.dwHighDateTime) << 32) + | f.dwLowDateTime; + + // windows filetime is specified in 100 nanoseconds resolution. + // convert to seconds + return time_t(ft / 10000000 - posix_time_offset); + } +#endif + + void stat_file(std::string inf, file_status* s + , error_code& ec, int flags) + { + ec.clear(); +#ifdef TORRENT_WINDOWS + // apparently windows doesn't expect paths + // to directories to ever end with a \ or / + if (!inf.empty() && (inf[inf.size() - 1] == '\\' + || inf[inf.size() - 1] == '/')) + inf.resize(inf.size() - 1); + +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS +#define GetFileAttributesEx_ GetFileAttributesExW + std::wstring f = convert_to_wstring(inf); +#else +#define GetFileAttributesEx_ GetFileAttributesExA + std::string f = convert_to_native(inf); +#endif + WIN32_FILE_ATTRIBUTE_DATA data; + if (!GetFileAttributesEx(f.c_str(), GetFileExInfoStandard, &data)) + { + ec.assign(GetLastError(), get_system_category()); + return; + } + + s->file_size = (boost::uint64_t(data.nFileSizeHigh) << 32) | data.nFileSizeLow; + s->ctime = file_time_to_posix(data.ftCreationTime); + s->atime = file_time_to_posix(data.ftLastAccessTime); + s->mtime = file_time_to_posix(data.ftLastWriteTime); + + s->mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? file_status::directory + : (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) + ? file_status::character_special : file_status::regular_file; + +#else + + // posix version + + std::string f = convert_to_native(inf); + + struct stat ret; + int retval; + if (flags & dont_follow_links) + retval = ::lstat(f.c_str(), &ret); + else + retval = ::stat(f.c_str(), &ret); + if (retval < 0) + { + ec.assign(errno, generic_category()); + return; + } + + s->file_size = ret.st_size; + s->atime = ret.st_atime; + s->mtime = ret.st_mtime; + s->ctime = ret.st_ctime; + + s->mode = (S_ISREG(ret.st_mode) ? file_status::regular_file : 0) + | (S_ISDIR(ret.st_mode) ? file_status::directory : 0) + | (S_ISLNK(ret.st_mode) ? file_status::link : 0) + | (S_ISFIFO(ret.st_mode) ? file_status::fifo : 0) + | (S_ISCHR(ret.st_mode) ? file_status::character_special : 0) + | (S_ISBLK(ret.st_mode) ? file_status::block_special : 0) + | (S_ISSOCK(ret.st_mode) ? file_status::socket : 0); + +#endif // TORRENT_WINDOWS + } + + void rename(std::string const& inf, std::string const& newf, error_code& ec) + { + ec.clear(); + +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS + std::wstring f1 = convert_to_wstring(inf); + std::wstring f2 = convert_to_wstring(newf); + if (_wrename(f1.c_str(), f2.c_str()) < 0) +#else + std::string f1 = convert_to_native(inf); + std::string f2 = convert_to_native(newf); + if (::rename(f1.c_str(), f2.c_str()) < 0) +#endif + { + ec.assign(errno, generic_category()); + return; + } + } + + void create_directories(std::string const& f, error_code& ec) + { + ec.clear(); + if (is_directory(f, ec)) return; + if (ec != boost::system::errc::no_such_file_or_directory) + return; + ec.clear(); + if (is_root_path(f)) return; + if (has_parent_path(f)) + { + create_directories(parent_path(f), ec); + if (ec) return; + } + create_directory(f, ec); + } + + void create_directory(std::string const& f, error_code& ec) + { + ec.clear(); + +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING +#define CreateDirectory_ CreateDirectoryW + std::wstring n = convert_to_wstring(f); +#else +#define CreateDirectory_ CreateDirectoryA + std::string n = convert_to_native(f); +#endif + +#ifdef TORRENT_WINDOWS + if (CreateDirectory_(n.c_str(), 0) == 0 + && GetLastError() != ERROR_ALREADY_EXISTS) + ec.assign(GetLastError(), get_system_category()); +#else + int ret = mkdir(n.c_str(), 0777); + if (ret < 0 && errno != EEXIST) + ec.assign(errno, generic_category()); +#endif + } + + bool is_directory(std::string const& f, error_code& ec) + { + ec.clear(); + error_code e; + file_status s; + stat_file(f, &s, e); + if (!e && s.mode & file_status::directory) return true; + ec = e; + return false; + } + + void recursive_copy(std::string const& old_path, std::string const& new_path, error_code& ec) + { + TORRENT_ASSERT(!ec); + if (is_directory(old_path, ec)) + { + create_directory(new_path, ec); + if (ec) return; + for (directory i(old_path, ec); !i.done(); i.next(ec)) + { + std::string f = i.file(); + if (f == ".." || f == ".") continue; + recursive_copy(combine_path(old_path, f), combine_path(new_path, f), ec); + if (ec) return; + } + } + else if (!ec) + { + copy_file(old_path, new_path, ec); + } + } + + void copy_file(std::string const& inf, std::string const& newf, error_code& ec) + { + ec.clear(); +#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS +#define CopyFile_ CopyFileW + std::wstring f1 = convert_to_wstring(inf); + std::wstring f2 = convert_to_wstring(newf); +#else +#define CopyFile_ CopyFileA + std::string f1 = convert_to_native(inf); + std::string f2 = convert_to_native(newf); +#endif + +#ifdef TORRENT_WINDOWS + if (CopyFile_(f1.c_str(), f2.c_str(), false) == 0) + ec.assign(GetLastError(), get_system_category()); +#elif defined __APPLE__ && defined __MACH__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + // this only works on 10.5 + copyfile_state_t state = copyfile_state_alloc(); + if (copyfile(f1.c_str(), f2.c_str(), state, COPYFILE_ALL) < 0) + ec.assign(errno, generic_category()); + copyfile_state_free(state); +#else + int infd = ::open(inf.c_str(), O_RDONLY); + if (infd < 0) + { + ec.assign(errno, generic_category()); + return; + } + + // rely on default umask to filter x and w permissions + // for group and others + int permissions = S_IRUSR | S_IWUSR + | S_IRGRP | S_IWGRP + | S_IROTH | S_IWOTH; + + int outfd = ::open(newf.c_str(), O_WRONLY | O_CREAT, permissions); + if (outfd < 0) + { + close(infd); + ec.assign(errno, generic_category()); + return; + } + char buffer[4096]; + for (;;) + { + int num_read = read(infd, buffer, sizeof(buffer)); + if (num_read == 0) break; + if (num_read < 0) + { + ec.assign(errno, generic_category()); + break; + } + int num_written = write(outfd, buffer, num_read); + if (num_written < num_read) + { + ec.assign(errno, generic_category()); + break; + } + if (num_read < int(sizeof(buffer))) break; + } + close(infd); + close(outfd); +#endif // TORRENT_WINDOWS + } + + std::string split_path(std::string const& f) + { + if (f.empty()) return f; + + std::string ret; + char const* start = f.c_str(); + char const* p = start; + while (*start != 0) + { + while (*p != '/' + && *p != '\0' +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + && *p != '\\' +#endif + ) ++p; + if (p - start > 0) + { + ret.append(start, p - start); + ret.append(1, '\0'); + } + if (*p != 0) ++p; + start = p; + } + ret.append(1, '\0'); + return ret; + } + + char const* next_path_element(char const* p) + { + p += strlen(p) + 1; + if (*p == 0) return 0; + return p; + } + + std::string extension(std::string const& f) + { + for (int i = f.size() - 1; i >= 0; --i) + { + if (f[i] == '/') break; +#ifdef TORRENT_WINDOWS + if (f[i] == '\\') break; +#endif + if (f[i] != '.') continue; + return f.substr(i); + } + return ""; + } + + std::string remove_extension(std::string const& f) + { + char const* slash = strrchr(f.c_str(), '/'); +#ifdef TORRENT_WINDOWS + slash = (std::max)((char const*)strrchr(f.c_str(), '\\'), slash); +#endif + char const* ext = strrchr(f.c_str(), '.'); + // if we don't have an extension, just return f + if (ext == 0 || ext == &f[0] || (slash != NULL && ext < slash)) return f; + return f.substr(0, ext - &f[0]); + } + + void replace_extension(std::string& f, std::string const& ext) + { + for (int i = f.size() - 1; i >= 0; --i) + { + if (f[i] == '/') break; +#ifdef TORRENT_WINDOWS + if (f[i] == '\\') break; +#endif + + if (f[i] != '.') continue; + + f.resize(i); + break; + } + f += '.'; + f += ext; + } + + bool is_root_path(std::string const& f) + { + if (f.empty()) return false; + +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + // match \\ form + if (f == "\\\\") return true; + int i = 0; + // match the xx:\ or xx:/ form + while (f[i] && is_alpha(f[i])) ++i; + if (i == int(f.size()-2) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) + return true; + // match network paths \\computer_name\ form + if (f.size() > 2 && f[0] == '\\' && f[1] == '\\') + { + // we don't care about the last character, since it's OK for it + // to be a slash or a back slash + bool found = false; + for (int i = 2; i < int(f.size()) - 1; ++i) + { + if (f[i] != '\\' && f[i] != '/') continue; + // there is a directory separator in here, + // i.e. this is not the root + found = true; + break; + } + if (!found) return true; + } +#else + // as well as parent_path("/") should be "/". + if (f == "/") return true; +#endif + return false; + } + + bool has_parent_path(std::string const& f) + { + if (f.empty()) return false; + if (is_root_path(f)) return false; + + int len = f.size() - 1; + // if the last character is / or \ ignore it + if (f[len] == '/' || f[len] == '\\') --len; + while (len >= 0) + { + if (f[len] == '/' || f[len] == '\\') + break; + --len; + } + + return len >= 0; + } + + std::string parent_path(std::string const& f) + { + if (f.empty()) return f; + +#ifdef TORRENT_WINDOWS + if (f == "\\\\") return ""; +#endif + if (f == "/") return ""; + + int len = f.size(); + // if the last character is / or \ ignore it + if (f[len-1] == '/' || f[len-1] == '\\') --len; + while (len > 0) + { + --len; + if (f[len] == '/' || f[len] == '\\') + break; + } + + if (f[len] == '/' || f[len] == '\\') ++len; + return std::string(f.c_str(), len); + } + + std::string filename(std::string const& f) + { + if (f.empty()) return ""; + char const* first = f.c_str(); + char const* sep = strrchr(first, '/'); +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + char const* altsep = strrchr(first, '\\'); + if (sep == 0 || altsep > sep) sep = altsep; +#endif + if (sep == 0) return f; + + if (sep - first == int(f.size()) - 1) + { + // if the last character is a / (or \) + // ignore it + int len = 0; + while (sep > first) + { + --sep; + if (*sep == '/' +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + || *sep == '\\' +#endif + ) + return std::string(sep + 1, len); + ++len; + } + return std::string(first, len); + + } + return std::string(sep + 1); + } + + std::string combine_path(std::string const& lhs, std::string const& rhs) + { + TORRENT_ASSERT(!is_complete(rhs)); + if (lhs.empty() || lhs == ".") return rhs; + if (rhs.empty() || rhs == ".") return lhs; + +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) +#define TORRENT_SEPARATOR "\\" + bool need_sep = lhs[lhs.size()-1] != '\\' && lhs[lhs.size()-1] != '/'; +#else +#define TORRENT_SEPARATOR "/" + bool need_sep = lhs[lhs.size()-1] != '/'; +#endif + std::string ret; + int target_size = lhs.size() + rhs.size() + 2; + ret.resize(target_size); + target_size = snprintf(&ret[0], target_size, "%s%s%s", lhs.c_str() + , (need_sep?TORRENT_SEPARATOR:""), rhs.c_str()); + ret.resize(target_size); + return ret; + } + + std::string current_working_directory() + { +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW +#if TORRENT_USE_WSTRING + wchar_t cwd[TORRENT_MAX_PATH]; + _wgetcwd(cwd, sizeof(cwd) / sizeof(wchar_t)); +#else + char cwd[TORRENT_MAX_PATH]; + _getcwd(cwd, sizeof(cwd)); +#endif // TORRENT_USE_WSTRING +#else + char cwd[TORRENT_MAX_PATH]; + if (getcwd(cwd, sizeof(cwd)) == 0) return "/"; +#endif +#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW && TORRENT_USE_WSTRING + return convert_from_wstring(cwd); +#else + return convert_from_native(cwd); +#endif + } + +#if TORRENT_USE_UNC_PATHS + std::string canonicalize_path(std::string const& f) + { + std::string ret; + ret.resize(f.size()); + char* write_cur = &ret[0]; + char* last_write_sep = write_cur; + + char const* read_cur = f.c_str(); + char const* last_read_sep = read_cur; + + // the last_*_sep pointers point to one past + // the last path separator encountered and is + // initializes to the first character in the path + while (*read_cur) + { + if (*read_cur != '\\') + { + *write_cur++ = *read_cur++; + continue; + } + int element_len = read_cur - last_read_sep; + if (element_len == 1 && memcmp(last_read_sep, ".", 1) == 0) + { + --write_cur; + ++read_cur; + last_read_sep = read_cur; + continue; + } + if (element_len == 2 && memcmp(last_read_sep, "..", 2) == 0) + { + // find the previous path separator + if (last_write_sep > &ret[0]) + { + --last_write_sep; + while (last_write_sep > &ret[0] + && last_write_sep[-1] != '\\') + --last_write_sep; + } + write_cur = last_write_sep; + // find the previous path separator + if (last_write_sep > &ret[0]) + { + --last_write_sep; + while (last_write_sep > &ret[0] + && last_write_sep[-1] != '\\') + --last_write_sep; + } + ++read_cur; + last_read_sep = read_cur; + continue; + } + *write_cur++ = *read_cur++; + last_write_sep = write_cur; + last_read_sep = read_cur; + } + // terminate destination string + *write_cur = 0; + ret.resize(write_cur - &ret[0]); + return ret; + } +#endif + + size_type file_size(std::string const& f) + { + error_code ec; + file_status s; + stat_file(f, &s, ec); + if (ec) return 0; + return s.file_size; + } + + bool exists(std::string const& f) + { + error_code ec; + file_status s; + stat_file(f, &s, ec); + if (ec) return false; + return true; + } + + void remove(std::string const& inf, error_code& ec) + { + ec.clear(); + +#ifdef TORRENT_WINDOWS + // windows does not allow trailing / or \ in + // the path when removing files + std::string pruned; + if (inf[inf.size() - 1] == '/' + || inf[inf.size() - 1] == '\\') + pruned = inf.substr(0, inf.size() - 1); + else + pruned = inf; +#if TORRENT_USE_WSTRING +#define DeleteFile_ DeleteFileW +#define RemoveDirectory_ RemoveDirectoryW + std::wstring f = convert_to_wstring(pruned); +#else +#define DeleteFile_ DeleteFileA +#define RemoveDirectory_ RemoveDirectoryA + std::string f = convert_to_native(pruned); +#endif + if (DeleteFile_(f.c_str()) == 0) + { + if (GetLastError() == ERROR_ACCESS_DENIED) + { + if (RemoveDirectory_(f.c_str()) != 0) + return; + } + ec.assign(GetLastError(), get_system_category()); + return; + } +#else // TORRENT_WINDOWS + std::string f = convert_to_native(inf); + if (::remove(f.c_str()) < 0) + { + ec.assign(errno, generic_category()); + return; + } +#endif // TORRENT_WINDOWS + } + + void remove_all(std::string const& f, error_code& ec) + { + ec.clear(); + + file_status s; + stat_file(f, &s, ec); + if (ec) return; + + if (s.mode & file_status::directory) + { + for (directory i(f, ec); !i.done(); i.next(ec)) + { + if (ec) return; + std::string p = i.file(); + if (p == "." || p == "..") continue; + remove_all(combine_path(f, p), ec); + if (ec) return; + } + } + remove(f, ec); + } + + std::string complete(std::string const& f) + { + if (is_complete(f)) return f; + if (f == ".") return current_working_directory(); + return combine_path(current_working_directory(), f); + } + + bool is_complete(std::string const& f) + { + if (f.empty()) return false; +#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2) + int i = 0; + // match the xx:\ or xx:/ form + while (f[i] && is_alpha(f[i])) ++i; + if (i < int(f.size()-1) && f[i] == ':' && (f[i+1] == '\\' || f[i+1] == '/')) + return true; + + // match the \\ form + if (int(f.size()) >= 2 && f[0] == '\\' && f[1] == '\\') + return true; + return false; +#else + if (f[0] == '/') return true; + return false; +#endif + } + + directory::directory(std::string const& path, error_code& ec) + : m_done(false) + { + ec.clear(); +#ifdef TORRENT_WINDOWS + m_inode = 0; + // the path passed to FindFirstFile() must be + // a pattern + std::string f = convert_separators(path); + if (!f.empty() && f[f.size()-1] != '\\') f += "\\*"; + else f += "*"; +#if TORRENT_USE_WSTRING +#define FindFirstFile_ FindFirstFileW + std::wstring p = convert_to_wstring(f); +#else +#define FindFirstFile_ FindFirstFileA + std::string p = convert_to_native(f); +#endif + m_handle = FindFirstFile_(p.c_str(), &m_fd); + if (m_handle == INVALID_HANDLE_VALUE) + { + ec.assign(GetLastError(), boost::system::get_system_category()); + m_done = true; + return; + } +#else + + memset(&m_dirent, 0, sizeof(dirent)); + m_name[0] = 0; + + // the path passed to opendir() may not + // end with a / + std::string p = path; + if (!path.empty() && path[path.size()-1] == '/') + p.resize(path.size()-1); + + p = convert_to_native(p); + m_handle = opendir(p.c_str()); + if (m_handle == 0) + { + ec.assign(errno, generic_category()); + m_done = true; + return; + } + // read the first entry + next(ec); +#endif + } + + directory::~directory() + { +#ifdef TORRENT_WINDOWS + if (m_handle != INVALID_HANDLE_VALUE) + FindClose(m_handle); +#else + if (m_handle) closedir(m_handle); +#endif + } + + boost::uint64_t directory::inode() const + { +#ifdef TORRENT_WINDOWS + return m_inode; +#else + return m_dirent.d_ino; +#endif + } + + std::string directory::file() const + { +#ifdef TORRENT_WINDOWS +#if TORRENT_USE_WSTRING + return convert_from_wstring(m_fd.cFileName); +#else + return convert_from_native(m_fd.cFileName); +#endif +#else + return convert_from_native(m_dirent.d_name); +#endif + } + + void directory::next(error_code& ec) + { + ec.clear(); +#ifdef TORRENT_WINDOWS +#if TORRENT_USE_WSTRING +#define FindNextFile_ FindNextFileW +#else +#define FindNextFile_ FindNextFileA +#endif + if (FindNextFile_(m_handle, &m_fd) == 0) + { + m_done = true; + int err = GetLastError(); + if (err != ERROR_NO_MORE_FILES) + ec.assign(err, boost::system::get_system_category()); + } + ++m_inode; +#else + dirent* dummy; + if (readdir_r(m_handle, &m_dirent, &dummy) != 0) + { + ec.assign(errno, generic_category()); + m_done = true; + } + if (dummy == 0) m_done = true; +#endif + } + +#ifdef TORRENT_WINDOWS + struct overlapped_t + { + overlapped_t() + { + memset(&ol, 0, sizeof(ol)); + ol.hEvent = CreateEvent(0, true, false, 0); + } + ~overlapped_t() + { + if (ol.hEvent != INVALID_HANDLE_VALUE) + CloseHandle(ol.hEvent); + } + int wait(HANDLE file, error_code& ec) + { + if (ol.hEvent != INVALID_HANDLE_VALUE + && WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + DWORD ret = -1; + if (GetOverlappedResult(file, &ol, &ret, false) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_HANDLE_EOF) + { +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + return -1; + } + } + return ret; + } + + OVERLAPPED ol; + }; +#endif // TORRENT_WINDOWS + + +#ifdef TORRENT_WINDOWS + bool get_manage_volume_privs(); + + // this needs to be run before CreateFile + bool file::has_manage_volume_privs = get_manage_volume_privs(); +#endif + + file::file() +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) +#else + : m_fd(-1) +#endif + , m_open_mode(0) +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + , m_sector_size(0) +#endif + {} + + file::file(std::string const& path, int mode, error_code& ec) +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) +#else + : m_fd(-1) +#endif + , m_open_mode(0) + { + // the return value is not important, since the + // error code contains the same information + open(path, mode, ec); + } + + file::~file() + { + close(); + } + + bool file::open(std::string const& path, int mode, error_code& ec) + { + close(); +#ifdef TORRENT_WINDOWS + + struct open_mode_t + { + DWORD rw_mode; + DWORD create_mode; + }; + + const static open_mode_t mode_array[] = + { + // read_only + {GENERIC_READ, OPEN_EXISTING}, + // write_only + {GENERIC_WRITE, OPEN_ALWAYS}, + // read_write + {GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS}, + }; + + const static DWORD attrib_array[] = + { + FILE_ATTRIBUTE_NORMAL, // no attrib + FILE_ATTRIBUTE_HIDDEN, // hidden + FILE_ATTRIBUTE_NORMAL, // executable + FILE_ATTRIBUTE_HIDDEN, // hidden + executable + }; + + const static DWORD share_array[] = + { + // read only (no locking) + FILE_SHARE_READ | FILE_SHARE_WRITE, + // write only (no locking) + FILE_SHARE_READ, + // read/write (no locking) + FILE_SHARE_READ, + }; + + std::string p = convert_separators(path); +#if TORRENT_USE_UNC_PATHS + // UNC paths must be absolute + // network paths are already UNC paths + if (path.substr(0,2) == "\\\\") p = path; + else p = "\\\\?\\" + (is_complete(p) ? p : combine_path(current_working_directory(), p)); +#endif + +#if TORRENT_USE_WSTRING +#define CreateFile_ CreateFileW + m_path = convert_to_wstring(p); +#else +#define CreateFile_ CreateFileA + m_path = convert_to_native(p); +#endif + + TORRENT_ASSERT((mode & rw_mask) < sizeof(mode_array)/sizeof(mode_array[0])); + open_mode_t const& m = mode_array[mode & rw_mask]; + DWORD a = attrib_array[(mode & attribute_mask) >> 12]; + + // one might think it's a good idea to pass in FILE_FLAG_RANDOM_ACCESS. It + // turns out that it isn't. That flag will break your operating system: + // http://support.microsoft.com/kb/2549369 + + DWORD flags + = ((mode & random_access) ? 0 : FILE_FLAG_SEQUENTIAL_SCAN) + | (a ? a : FILE_ATTRIBUTE_NORMAL) + | ((mode & no_buffer) ? FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING : 0); + + m_file_handle = CreateFile_(m_path.c_str(), m.rw_mode + , (mode & lock_file) ? 0 : share_array[mode & rw_mask] + , 0, m.create_mode, flags, 0); + + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec.assign(GetLastError(), get_system_category()); + TORRENT_ASSERT(ec); + return false; + } + + // try to make the file sparse if supported + // only set this flag if the file is opened for writing + if ((mode & file::sparse) && (mode & rw_mask) != read_only) + { + DWORD temp; + bool use_overlapped = (m_open_mode & no_buffer) != 0; + overlapped_t ol; + BOOL ret = ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 + , 0, 0, &temp, use_overlapped ? &ol.ol : NULL); + error_code error; + if (use_overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + ol.wait(m_file_handle, error); + } +#else // TORRENT_WINDOWS + + // rely on default umask to filter x and w permissions + // for group and others + int permissions = S_IRUSR | S_IWUSR + | S_IRGRP | S_IWGRP + | S_IROTH | S_IWOTH; + + if (mode & attribute_executable) + permissions |= S_IXGRP | S_IXOTH | S_IXUSR; +#ifdef O_BINARY + static const int mode_array[] = {O_RDONLY | O_BINARY, O_WRONLY | O_CREAT | O_BINARY, O_RDWR | O_CREAT | O_BINARY}; +#else + static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT}; +#endif +#ifdef O_DIRECT + static const int no_buffer_flag[] = {0, O_DIRECT}; +#else + static const int no_buffer_flag[] = {0, 0}; +#endif + +#ifdef O_NOATIME + static const int no_atime_flag[] = {0, O_NOATIME}; +#endif + + m_fd = ::open(convert_to_native(path).c_str() + , mode_array[mode & rw_mask] + | no_buffer_flag[(mode & no_buffer) >> 2] +#ifdef O_NOATIME + | no_atime_flag[(mode & no_atime) >> 4] +#endif + , permissions); + +#ifdef TORRENT_LINUX + // workaround for linux bug + // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/269946 + if (m_fd == -1 && (mode & no_buffer) && errno == EINVAL) + { + mode &= ~no_buffer; + m_fd = ::open(path.c_str() + , mode_array[mode & rw_mask] +#ifdef O_NOATIME + | no_atime_flag[(mode & no_atime) >> 4] +#endif + + , permissions); + } +#endif + +#ifdef O_NOATIME + // O_NOATIME is not allowed for files we don't own + // so, if we get EPERM when we try to open with it + // try again without O_NOATIME + if (m_fd == -1 && (mode & no_atime) && errno == EPERM) + { + mode &= ~no_atime; + m_fd = ::open(path.c_str() + , mode_array[mode & rw_mask] + | no_buffer_flag[(mode & no_buffer) >> 2] + , permissions); + } +#endif + if (m_fd == -1) + { + ec.assign(errno, get_posix_category()); + TORRENT_ASSERT(ec); + return false; + } + + // The purpose of the lock_file flag is primarily to prevent other + // processes from corrupting files that are being used by libtorrent. + // the posix file locking mechanism does not prevent others from + // accessing files, unless they also attempt to lock the file. That's + // why the SETLK mechanism is not used here. + +#ifdef DIRECTIO_ON + // for solaris + if (mode & no_buffer) + { + int yes = 1; + directio(m_fd, DIRECTIO_ON); + } +#endif + +#ifdef F_NOCACHE + // for BSD/Mac + if (mode & no_buffer) + { + int yes = 1; + fcntl(m_fd, F_NOCACHE, &yes); + } +#endif + +#ifdef POSIX_FADV_RANDOM + if (mode & random_access) + { + // disable read-ahead + posix_fadvise(m_fd, 0, 0, POSIX_FADV_RANDOM); + } +#endif + +#endif + m_open_mode = mode; + + TORRENT_ASSERT(is_open()); + return true; + } + + bool file::is_open() const + { +#ifdef TORRENT_WINDOWS + return m_file_handle != INVALID_HANDLE_VALUE; +#else + return m_fd != -1; +#endif + } + + int file::pos_alignment() const + { + // on linux and windows, file offsets needs + // to be aligned to the disk sector size +#if defined TORRENT_LINUX + if (m_sector_size == 0) + { + struct statvfs fs; + if (fstatvfs(m_fd, &fs) == 0) + m_sector_size = fs.f_bsize; + else + m_sector_size = 4096; + } + return m_sector_size; +#elif defined TORRENT_WINDOWS + if (m_sector_size == 0) + { + DWORD sectors_per_cluster; + DWORD bytes_per_sector; + DWORD free_clusters; + DWORD total_clusters; +#if TORRENT_USE_WSTRING +#define GetDiskFreeSpace_ GetDiskFreeSpaceW + wchar_t backslash = L'\\'; +#else +#define GetDiskFreeSpace_ GetDiskFreeSpaceA + char backslash = '\\'; +#endif + if (GetDiskFreeSpace_(m_path.substr(0, m_path.find_first_of(backslash)+1).c_str() + , §ors_per_cluster, &bytes_per_sector + , &free_clusters, &total_clusters)) + { + m_sector_size = bytes_per_sector; + m_cluster_size = sectors_per_cluster * bytes_per_sector; + } + else + { + // make a conservative guess + m_sector_size = 512; + m_cluster_size = 4096; + } + } + return m_sector_size; +#else + return 1; +#endif + } + + int file::buf_alignment() const + { +#if defined TORRENT_WINDOWS + init_file(); + return m_page_size; +#else + return pos_alignment(); +#endif + } + + int file::size_alignment() const + { +#if defined TORRENT_WINDOWS + init_file(); + return m_page_size; +#else + return pos_alignment(); +#endif + } + +#ifdef TORRENT_WINDOWS + bool is_sparse(HANDLE file, bool overlapped) + { + LARGE_INTEGER file_size; + if (!GetFileSizeEx(file, &file_size)) + return false; + + overlapped_t ol; + if (ol.ol.hEvent == NULL) return false; + +#ifdef TORRENT_MINGW +typedef struct _FILE_ALLOCATED_RANGE_BUFFER { + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; +} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; +#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) +#endif + FILE_ALLOCATED_RANGE_BUFFER in; + in.FileOffset.QuadPart = 0; + in.Length.QuadPart = file_size.QuadPart; + + FILE_ALLOCATED_RANGE_BUFFER out[2]; + + DWORD returned_bytes = 0; + BOOL ret = DeviceIoControl(file, FSCTL_QUERY_ALLOCATED_RANGES, (void*)&in, sizeof(in) + , out, sizeof(out), &returned_bytes, overlapped ? &ol.ol : NULL); + + if (overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + { + error_code ec; + returned_bytes = ol.wait(file, ec); + if (ec) return true; + } + else if (ret == FALSE) + { + int error = GetLastError(); + return true; + } + + // if we have more than one range in the file, we're sparse + if (returned_bytes != sizeof(FILE_ALLOCATED_RANGE_BUFFER)) { + return true; + } + + return (in.Length.QuadPart != out[0].Length.QuadPart); + } +#endif + + void file::close() + { +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX + m_sector_size = 0; +#endif + +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) return; + + // if this file is open for writing, has the sparse + // flag set, but there are no sparse regions, unset + // the flag + int rw_mode = m_open_mode & rw_mask; + bool use_overlapped = (m_open_mode & no_buffer) != 0; + if ((rw_mode != read_only) + && (m_open_mode & sparse) + && !is_sparse(m_file_handle, use_overlapped)) + { + overlapped_t ol; + // according to MSDN, clearing the sparse flag of a file only + // works on windows vista and later +#ifdef TORRENT_MINGW + typedef struct _FILE_SET_SPARSE_BUFFER { + BOOLEAN SetSparse; + } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER; +#endif + DWORD temp; + FILE_SET_SPARSE_BUFFER b; + b.SetSparse = FALSE; + BOOL ret = ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, &b, sizeof(b) + , 0, 0, &temp, use_overlapped ? &ol.ol : NULL); + error_code ec; + if (use_overlapped && ret == FALSE && GetLastError() == ERROR_IO_PENDING) + { + ol.wait(m_file_handle, ec); + } + } + + CloseHandle(m_file_handle); + m_file_handle = INVALID_HANDLE_VALUE; + m_path.clear(); +#else + if (m_fd == -1) return; + ::close(m_fd); + m_fd = -1; +#endif + m_open_mode = 0; + } + + // defined in storage.cpp + int bufs_size(file::iovec_t const* bufs, int num_bufs); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + + int file::m_page_size = 0; + + void file::init_file() + { + if (m_page_size != 0) return; + + m_page_size = page_size(); + } + +#endif + + void file::hint_read(size_type file_offset, int len) + { +#if defined POSIX_FADV_WILLNEED + posix_fadvise(m_fd, file_offset, len, POSIX_FADV_WILLNEED); +#elif defined F_RDADVISE + radvisory r; + r.ra_offset = file_offset; + r.ra_count = len; + fcntl(m_fd, F_RDADVISE, &r); +#else + // TODO: is there any way to pre-fetch data from a file on windows? +#endif + } + + size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) + { +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec = error_code(ERROR_INVALID_HANDLE, get_system_category()); + return -1; + } +#else + if (m_fd == -1) + { + ec = error_code(EBADF, get_system_category()); + return -1; + } +#endif + TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write); + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(is_open()); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + // make sure m_page_size is initialized + init_file(); +#endif + +#ifdef TORRENT_DEBUG + if (m_open_mode & no_buffer) + { + bool eof = false; + int size = 0; + // when opened in no_buffer mode, the file_offset must + // be aligned to pos_alignment() + TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0); + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0); + // every buffer must be a multiple of the page size + // except for the last one + TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1); + if ((i->iov_len & (size_alignment()-1)) != 0) eof = true; + size += i->iov_len; + } + error_code code; + if (eof) + { + size_type fsize = get_size(code); + if (code) printf("get_size: %s\n", code.message().c_str()); + if (file_offset + size < fsize) + { + printf("offset: %d size: %d get_size: %d\n", int(file_offset), int(size), int(fsize)); + TORRENT_ASSERT(false); + } + } + } +#endif + +#ifdef TORRENT_WINDOWS + + DWORD ret = 0; + + // since the ReadFileScatter requires the file to be opened + // with no buffering, and no buffering requires page aligned + // buffers, open the file in non-buffered mode in case the + // buffer is not aligned. Most of the times the buffer should + // be aligned though + + if ((m_open_mode & no_buffer) == 0) + { + // this means the buffer base or the buffer size is not aligned + // to the page size. Use a regular file for this operation. + + LARGE_INTEGER offs; + offs.QuadPart = file_offset; + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + DWORD intermediate = 0; + if (ReadFile(m_file_handle, (char*)i->iov_base + , (DWORD)i->iov_len, &intermediate, 0) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + ret += intermediate; + } + return ret; + } + + int size = bufs_size(bufs, num_bufs); + // number of pages for the read. round up + int num_pages = (size + m_page_size - 1) / m_page_size; + // allocate array of FILE_SEGMENT_ELEMENT for ReadFileScatter + FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1); +#ifdef __GNUC__ + // MingW seems to have issues with 64 bit wide pointers + // (PVOID64) and only assign the low 32 bits. Therefore, make + // sure the other 32 bits are cleared out + memset(segment_array, 0, (num_pages + 1) * sizeof(FILE_SEGMENT_ELEMENT)); +#endif + FILE_SEGMENT_ELEMENT* cur_seg = segment_array; + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + for (int k = 0; k < int(i->iov_len); k += m_page_size) + { + cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k)); + ++cur_seg; + } + } + // terminate the array + cur_seg->Buffer = 0; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + ol.Internal = 0; + ol.InternalHigh = 0; + ol.OffsetHigh = DWORD(file_offset >> 32); + ol.Offset = DWORD(file_offset & 0xffffffff); + ol.hEvent = CreateEvent(0, true, false, 0); + if (ol.hEvent == NULL) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + ret += size; + size = num_pages * m_page_size; + if (ReadFileScatter(m_file_handle, segment_array, size, 0, &ol) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_IO_PENDING +#ifdef ERROR_CANT_WAIT + && last_error != ERROR_CANT_WAIT +#endif +) + { + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + DWORD num_read; + if (GetOverlappedResult(m_file_handle, &ol, &num_read, false) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_HANDLE_EOF) + { +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + } + if (num_read < ret) ret = num_read; + } + CloseHandle(ol.hEvent); + return ret; + +#else // TORRENT_WINDOWS + + size_type ret = lseek(m_fd, file_offset, SEEK_SET); + if (ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } +#if TORRENT_USE_READV + + ret = 0; + while (num_bufs > 0) + { + int nbufs = (std::min)(num_bufs, TORRENT_IOV_MAX); + int tmp_ret = 0; +#ifdef TORRENT_LINUX + bool aligned = false; + int size = 0; + // if we're not opened in no-buffer mode, we don't need alignment + if ((m_open_mode & no_buffer) == 0) aligned = true; + if (!aligned) + { + size = bufs_size(bufs, nbufs); + if ((size & (size_alignment()-1)) == 0) aligned = true; + } + if (aligned) +#endif // TORRENT_LINUX + { + tmp_ret = ::readv(m_fd, bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp_ret; + } +#ifdef TORRENT_LINUX + else + { + file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, nbufs); + memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * nbufs); + iovec_t& last = temp_bufs[nbufs-1]; + last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size; + tmp_ret = ::readv(m_fd, temp_bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += (std::min)(tmp_ret, size); + } +#endif // TORRENT_LINUX + + num_bufs -= nbufs; + bufs += nbufs; + } + + return ret; + +#else // TORRENT_USE_READV + + ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int tmp = read(m_fd, i->iov_base, i->iov_len); + if (tmp < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp; + if (tmp < i->iov_len) break; + } + return ret; + +#endif // TORRENT_USE_READV + +#endif // TORRENT_WINDOWS + } + + size_type file::writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) + { +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec = error_code(ERROR_INVALID_HANDLE, get_system_category()); + return -1; + } +#else + if (m_fd == -1) + { + ec = error_code(EBADF, get_system_category()); + return -1; + } +#endif + TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write); + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(is_open()); + +#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG + // make sure m_page_size is initialized + init_file(); +#endif + +#ifdef TORRENT_DEBUG + if (m_open_mode & no_buffer) + { + bool eof = false; + int size = 0; + // when opened in no_buffer mode, the file_offset must + // be aligned to pos_alignment() + TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0); + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0); + // every buffer must be a multiple of the page size + // except for the last one + TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1); + if ((i->iov_len & (size_alignment()-1)) != 0) eof = true; + size += i->iov_len; + } + error_code code; + if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code)); + } +#endif + +#ifdef TORRENT_WINDOWS + + DWORD ret = 0; + + // since the ReadFileScatter requires the file to be opened + // with no buffering, and no buffering requires page aligned + // buffers, open the file in non-buffered mode in case the + // buffer is not aligned. Most of the times the buffer should + // be aligned though + + if ((m_open_mode & no_buffer) == 0) + { + // this means the buffer base or the buffer size is not aligned + // to the page size. Use a regular file for this operation. + + LARGE_INTEGER offs; + offs.QuadPart = file_offset; + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + DWORD intermediate = 0; + if (WriteFile(m_file_handle, (char const*)i->iov_base + , (DWORD)i->iov_len, &intermediate, 0) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + ret += intermediate; + } + return ret; + } + + int size = bufs_size(bufs, num_bufs); + // number of pages for the write. round up + int num_pages = (size + m_page_size - 1) / m_page_size; + // allocate array of FILE_SEGMENT_ELEMENT for WriteFileGather + FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1); +#ifdef __GNUC__ + // MingW seems to have issues with 64 bit wide pointers + // (PVOID64) and only assign the low 32 bits. Therefore, make + // sure the other 32 bits are cleared out + memset(segment_array, 0, (num_pages + 1) * sizeof(FILE_SEGMENT_ELEMENT)); +#endif + FILE_SEGMENT_ELEMENT* cur_seg = segment_array; + + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + for (int k = 0; k < int(i->iov_len); k += m_page_size) + { + cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k)); + ++cur_seg; + } + } + // terminate the array + cur_seg->Buffer = 0; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + ol.Internal = 0; + ol.InternalHigh = 0; + ol.OffsetHigh = DWORD(file_offset >> 32); + ol.Offset = DWORD(file_offset & 0xffffffff); + ol.hEvent = CreateEvent(0, true, false, 0); + if (ol.hEvent == NULL) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + + ret += size; + size_type file_size = 0; + + if ((size & (m_page_size-1)) != 0) + { + // if size is not an even multiple, this must be the tail + // of the file. + + file_size = file_offset + size; + size = num_pages * m_page_size; + } + + if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0) + { + DWORD last_error = GetLastError(); + if (last_error != ERROR_IO_PENDING +#ifdef ERROR_CANT_WAIT + && last_error != ERROR_CANT_WAIT +#endif + ) + { + TORRENT_ASSERT(last_error != ERROR_BAD_ARGUMENTS); + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (WaitForSingleObject(ol.hEvent, INFINITE) == WAIT_FAILED) + { + ec.assign(GetLastError(), get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + DWORD num_written; + if (GetOverlappedResult(m_file_handle, &ol, &num_written, false) == 0) + { + DWORD last_error = GetLastError(); +#ifdef ERROR_CANT_WAIT + TORRENT_ASSERT(last_error != ERROR_CANT_WAIT); +#endif + ec.assign(last_error, get_system_category()); + CloseHandle(ol.hEvent); + return -1; + } + if (num_written < ret) ret = num_written; + } + CloseHandle(ol.hEvent); + if (file_size > 0) set_size(file_size, ec); + return ret; +#else + size_type ret = lseek(m_fd, file_offset, SEEK_SET); + if (ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + +#if TORRENT_USE_WRITEV + + ret = 0; + while (num_bufs > 0) + { + int nbufs = (std::min)(num_bufs, TORRENT_IOV_MAX); + int tmp_ret = 0; +#ifdef TORRENT_LINUX + bool aligned = false; + int size = 0; + // if we're not opened in no-buffer mode, we don't need alignment + if ((m_open_mode & no_buffer) == 0) aligned = true; + if (!aligned) + { + size = bufs_size(bufs, nbufs); + if ((size & (size_alignment()-1)) == 0) aligned = true; + } + if (aligned) +#endif + { + tmp_ret = ::writev(m_fd, bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp_ret; + } +#ifdef TORRENT_LINUX + else + { + file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, nbufs); + memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * nbufs); + iovec_t& last = temp_bufs[nbufs-1]; + last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment(); + tmp_ret = ::writev(m_fd, temp_bufs, nbufs); + if (tmp_ret < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + if (ftruncate(m_fd, file_offset + size) < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += (std::min)(tmp_ret, size); + } +#endif // TORRENT_LINUX + + num_bufs -= nbufs; + bufs += nbufs; + } + + return ret; + +#else // TORRENT_USE_WRITEV + + ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int tmp = write(m_fd, i->iov_base, i->iov_len); + if (tmp < 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + ret += tmp; + if (tmp < i->iov_len) break; + } + return ret; + +#endif // TORRENT_USE_WRITEV + +#endif // TORRENT_WINDOWS + } + + size_type file::phys_offset(size_type offset) + { +#ifdef FIEMAP_EXTENT_UNKNOWN + // for documentation of this feature + // http://lwn.net/Articles/297696/ + struct + { + struct fiemap fiemap; + struct fiemap_extent extent; + } fm; + + memset(&fm, 0, sizeof(fm)); + fm.fiemap.fm_start = offset; + fm.fiemap.fm_length = size_alignment(); + // this sounds expensive + fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC; + fm.fiemap.fm_extent_count = 1; + + if (ioctl(m_fd, FS_IOC_FIEMAP, &fm) == -1) + return 0; + + if (fm.fiemap.fm_mapped_extents != 1) + return 0; + + if (fm.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) + return 0; + + // the returned extent is not guaranteed to start + // at the requested offset, adjust for that in + // case they differ + TORRENT_ASSERT(offset >= fm.fiemap.fm_extents[0].fe_logical); + return fm.fiemap.fm_extents[0].fe_physical + (offset - fm.fiemap.fm_extents[0].fe_logical); + +#elif defined F_LOG2PHYS + // for documentation of this feature + // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/fcntl.2.html + + log2phys l; + size_type ret = lseek(m_fd, offset, SEEK_SET); + if (ret < 0) return 0; + if (fcntl(m_fd, F_LOG2PHYS, &l) == -1) return 0; + return l.l2p_devoffset; +#elif defined TORRENT_WINDOWS + // for documentation of this feature + // http://msdn.microsoft.com/en-us/library/aa364572(VS.85).aspx + STARTING_VCN_INPUT_BUFFER in; + RETRIEVAL_POINTERS_BUFFER out; + DWORD out_bytes; + + // query cluster size + pos_alignment(); + in.StartingVcn.QuadPart = offset / m_cluster_size; + int cluster_offset = int(in.StartingVcn.QuadPart % m_cluster_size); + + if (DeviceIoControl(m_file_handle, FSCTL_GET_RETRIEVAL_POINTERS, &in + , sizeof(in), &out, sizeof(out), &out_bytes, 0) == 0) + { + DWORD error = GetLastError(); + TORRENT_ASSERT(error != ERROR_INVALID_PARAMETER); + + // insufficient buffer error is expected, but we're + // only interested in the first extent anyway + if (error != ERROR_MORE_DATA) return 0; + } + if (out_bytes < sizeof(out)) return 0; + if (out.ExtentCount == 0) return 0; + if (out.Extents[0].Lcn.QuadPart == (LONGLONG)-1) return 0; + TORRENT_ASSERT(in.StartingVcn.QuadPart >= out.StartingVcn.QuadPart); + return (out.Extents[0].Lcn.QuadPart + + (in.StartingVcn.QuadPart - out.StartingVcn.QuadPart)) + * m_cluster_size + cluster_offset; +#endif + return 0; + } + +#ifdef TORRENT_WINDOWS + bool get_manage_volume_privs() + { + typedef BOOL (WINAPI *OpenProcessToken_t)( + HANDLE ProcessHandle, + DWORD DesiredAccess, + PHANDLE TokenHandle); + + typedef BOOL (WINAPI *LookupPrivilegeValue_t)( + LPCSTR lpSystemName, + LPCSTR lpName, + PLUID lpLuid); + + typedef BOOL (WINAPI *AdjustTokenPrivileges_t)( + HANDLE TokenHandle, + BOOL DisableAllPrivileges, + PTOKEN_PRIVILEGES NewState, + DWORD BufferLength, + PTOKEN_PRIVILEGES PreviousState, + PDWORD ReturnLength); + + static OpenProcessToken_t pOpenProcessToken = NULL; + static LookupPrivilegeValue_t pLookupPrivilegeValue = NULL; + static AdjustTokenPrivileges_t pAdjustTokenPrivileges = NULL; + static bool failed_advapi = false; + + if (pOpenProcessToken == NULL && !failed_advapi) + { + HMODULE advapi = LoadLibraryA("advapi32"); + if (advapi == NULL) + { + failed_advapi = true; + return false; + } + pOpenProcessToken = (OpenProcessToken_t)GetProcAddress(advapi, "OpenProcessToken"); + pLookupPrivilegeValue = (LookupPrivilegeValue_t)GetProcAddress(advapi, "LookupPrivilegeValueA"); + pAdjustTokenPrivileges = (AdjustTokenPrivileges_t)GetProcAddress(advapi, "AdjustTokenPrivileges"); + if (pOpenProcessToken == NULL + || pLookupPrivilegeValue == NULL + || pAdjustTokenPrivileges == NULL) + { + failed_advapi = true; + return false; + } + } + + HANDLE token; + if (!pOpenProcessToken(GetCurrentProcess() + , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + return false; + + TOKEN_PRIVILEGES privs; + if (!pLookupPrivilegeValue(NULL, "SeManageVolumePrivilege" + , &privs.Privileges[0].Luid)) + { + CloseHandle(token); + return false; + } + + privs.PrivilegeCount = 1; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + bool ret = pAdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL) + && GetLastError() == ERROR_SUCCESS; + + CloseHandle(token); + + return ret; + } + + void set_file_valid_data(HANDLE f, boost::int64_t size) + { + typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE, LONGLONG); + static SetFileValidData_t pSetFileValidData = NULL; + static bool failed_kernel32 = false; + + if (pSetFileValidData == NULL && !failed_kernel32) + { + HMODULE k32 = LoadLibraryA("kernel32"); + if (k32 == NULL) + { + failed_kernel32 = true; + return; + } + pSetFileValidData = (SetFileValidData_t)GetProcAddress(k32, "SetFileValidData"); + if (pSetFileValidData == NULL) + { + failed_kernel32 = true; + return; + } + } + + TORRENT_ASSERT(pSetFileValidData); + + // we don't necessarily expect to have enough + // privilege to do this, so ignore errors. + pSetFileValidData(f, size); + } +#endif + + bool file::set_size(size_type s, error_code& ec) + { + TORRENT_ASSERT(is_open()); + TORRENT_ASSERT(s >= 0); + +#ifdef TORRENT_WINDOWS + + if ((m_open_mode & no_buffer) && (s & (size_alignment()-1)) != 0) + { + // the file is opened in unbuffered mode, and the size is not + // aligned to the required cluster size. Use NtSetInformationFile + +#define FileEndOfFileInformation 20 +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) (!((x) & 0x80000000)) +#endif + + // for NtSetInformationFile, see: + // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtSetInformationFile.html + + typedef DWORD _NTSTATUS; + typedef _NTSTATUS (NTAPI * NtSetInformationFile_t)(HANDLE file, PULONG_PTR iosb, PVOID data, ULONG len, ULONG file_info_class); + + static NtSetInformationFile_t NtSetInformationFile = 0; + static bool failed_ntdll = false; + + if (NtSetInformationFile == 0 && !failed_ntdll) + { + HMODULE nt = LoadLibraryA("ntdll"); + if (nt) + { + NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt, "NtSetInformationFile"); + if (NtSetInformationFile == 0) failed_ntdll = true; + } + else failed_ntdll = true; + } + + if (!failed_ntdll && NtSetInformationFile) + { + ULONG_PTR Iosb[2]; + LARGE_INTEGER fsize; + fsize.QuadPart = s; + _NTSTATUS st = NtSetInformationFile(m_file_handle + , Iosb, &fsize, sizeof(fsize), FileEndOfFileInformation); + if (!NT_SUCCESS(st)) + { + ec.assign(INVALID_SET_FILE_POINTER, get_system_category()); + return false; + } + + if ((m_open_mode & sparse) == 0) + set_file_valid_data(m_file_handle, s); + + return true; + } + + // couldn't find ntdll or NtSetFileInformation function + // and the file is opened in unbuffered mode! There's + // nothing we can do! (short of re-opening the file, but + // that introduces all sorts of nasty race conditions) + return false; + } + + LARGE_INTEGER offs; + LARGE_INTEGER cur_size; + if (GetFileSizeEx(m_file_handle, &cur_size) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + offs.QuadPart = s; + // only set the file size if it's not already at + // the right size. We don't want to update the + // modification time if we don't have to + if (cur_size.QuadPart != s) + { + if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + if (::SetEndOfFile(m_file_handle) == FALSE) + { + ec.assign(GetLastError(), get_system_category()); + return false; + } + } + + if ((m_open_mode & sparse) == 0) + { +#if TORRENT_USE_WSTRING + typedef DWORD (WINAPI *GetCompressedFileSize_t)(LPCWSTR lpFileName, LPDWORD lpFileSizeHigh); +#else + typedef DWORD (WINAPI *GetCompressedFileSize_t)(LPCSTR lpFileName, LPDWORD lpFileSizeHigh); +#endif + + static GetCompressedFileSize_t GetCompressedFileSize_ = NULL; + + static bool failed_kernel32 = false; + + if ((GetCompressedFileSize_ == NULL) && !failed_kernel32) + { + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { +#if TORRENT_USE_WSTRING + GetCompressedFileSize_ = (GetCompressedFileSize_t)GetProcAddress(kernel32, "GetCompressedFileSizeW"); +#else + GetCompressedFileSize_ = (GetCompressedFileSize_t)GetProcAddress(kernel32, "GetCompressedFileSizeA"); +#endif + } + else + { + failed_kernel32 = true; + } + } + + offs.QuadPart = 0; + if (GetCompressedFileSize_) + { + // only allocate the space if the file + // is not fully allocated + DWORD high_dword = 0; + offs.LowPart = GetCompressedFileSize_(m_path.c_str(), &high_dword); + offs.HighPart = high_dword; + if (offs.LowPart == INVALID_FILE_SIZE) + { + ec.assign(GetLastError(), get_system_category()); + if (ec) return false; + } + } + + if (offs.QuadPart != s) + { + // if the user has permissions, avoid filling + // the file with zeroes, but just fill it with + // garbage instead + set_file_valid_data(m_file_handle, s); + } + } +#else // NON-WINDOWS + struct stat st; + if (fstat(m_fd, &st) != 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + + // only truncate the file if it doesn't already + // have the right size. We don't want to update + if (st.st_size != s && ftruncate(m_fd, s) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + + // if we're not in sparse mode, allocate the storage + // but only if the number of allocated blocks for the file + // is less than the file size. Otherwise we would just + // update the modification time of the file for no good + // reason. + if ((m_open_mode & sparse) == 0 + && st.st_blocks < (s + st.st_blksize - 1) / st.st_blksize) + { + // How do we know that the file is already allocated? + // if we always try to allocate the space, we'll update + // the modification time without actually changing the file + // but if we don't do anything if the file size is +#ifdef F_PREALLOCATE + fstore_t f = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, s, 0}; + if (fcntl(m_fd, F_PREALLOCATE, &f) < 0) + { + if (errno != ENOSPC) + { + ec.assign(errno, get_posix_category()); + return false; + } + // ok, let's try to allocate non contiguous space then + fstore_t f = {F_ALLOCATEALL, F_PEOFPOSMODE, 0, s, 0}; + if (fcntl(m_fd, F_PREALLOCATE, &f) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + } +#endif // F_PREALLOCATE + +#ifdef F_ALLOCSP64 + flock64 fl64; + fl64.l_whence = SEEK_SET; + fl64.l_start = 0; + fl64.l_len = s; + if (fcntl(native_handle(), F_ALLOCSP64, &fl64) < 0) + { + ec.assign(errno, get_posix_category()); + return false; + } + +#endif // F_ALLOCSP64 + +#if defined TORRENT_LINUX || TORRENT_HAS_FALLOCATE + int ret; +#endif + +#if defined TORRENT_LINUX + ret = my_fallocate(m_fd, 0, 0, s); + // if we return 0, everything went fine + // the fallocate call succeeded + if (ret == 0) return true; + // otherwise, something went wrong. If the error + // is ENOSYS, just keep going and do it the old-fashioned + // way. If fallocate failed with some other error, it + // probably means the user should know about it, error out + // and report it. + if (errno != ENOSYS && errno != EOPNOTSUPP && errno != EINVAL) + { + ec.assign(errno, get_posix_category()); + return false; + } +#endif // TORRENT_LINUX + +#if TORRENT_HAS_FALLOCATE + // if fallocate failed, we have to use posix_fallocate + // which can be painfully slow + // if you get a compile error here, you might want to + // define TORRENT_HAS_FALLOCATE to 0. + ret = posix_fallocate(m_fd, 0, s); + // posix_allocate fails with EINVAL in case the underlying + // filesystem does bot support this operation + if (ret != 0 && ret != EINVAL) + { + ec.assign(ret, get_posix_category()); + return false; + } +#endif // TORRENT_HAS_FALLOCATE + } +#endif // TORRENT_WINDOWS + return true; + } + + size_type file::get_size(error_code& ec) const + { +#ifdef TORRENT_WINDOWS + LARGE_INTEGER file_size; + if (!GetFileSizeEx(m_file_handle, &file_size)) + { + ec.assign(GetLastError(), get_system_category()); + return -1; + } + return file_size.QuadPart; +#else + struct stat fs; + if (fstat(m_fd, &fs) != 0) + { + ec.assign(errno, get_posix_category()); + return -1; + } + return fs.st_size; +#endif + } + + size_type file::sparse_end(size_type start) const + { +#ifdef TORRENT_WINDOWS +#ifdef TORRENT_MINGW +typedef struct _FILE_ALLOCATED_RANGE_BUFFER { + LARGE_INTEGER FileOffset; + LARGE_INTEGER Length; +} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; +#define FSCTL_QUERY_ALLOCATED_RANGES ((0x9 << 16) | (1 << 14) | (51 << 2) | 3) +#endif + FILE_ALLOCATED_RANGE_BUFFER buffer; + DWORD bytes_returned = 0; + FILE_ALLOCATED_RANGE_BUFFER in; + error_code ec; + size_type file_size = get_size(ec); + if (ec) return start; + + if (m_open_mode & no_buffer) + { + boost::uint64_t mask = size_alignment()-1; + in.FileOffset.QuadPart = start & (~mask); + in.Length.QuadPart = ((file_size + mask) & ~mask) - in.FileOffset.QuadPart; + TORRENT_ASSERT((in.Length.QuadPart & mask) == 0); + } + else + { + in.FileOffset.QuadPart = start; + in.Length.QuadPart = file_size - start; + } + + if (!DeviceIoControl(m_file_handle, FSCTL_QUERY_ALLOCATED_RANGES + , &in, sizeof(FILE_ALLOCATED_RANGE_BUFFER) + , &buffer, sizeof(FILE_ALLOCATED_RANGE_BUFFER), &bytes_returned, 0)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return start; + } + + // if there are no allocated regions within the rest + // of the file, return the end of the file + if (bytes_returned == 0) return file_size; + + // assume that this range overlaps the start of the + // region we were interested in, and that start actually + // resides in an allocated region. + if (buffer.FileOffset.QuadPart < start) return start; + + // return the offset to the next allocated region + return buffer.FileOffset.QuadPart; + +#elif defined SEEK_DATA + // this is supported on solaris + size_type ret = lseek(m_fd, start, SEEK_DATA); + if (ret < 0) return start; + return start; +#else + return start; +#endif + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file_pool.cpp b/apps/Launcher/ext/libtorrent/src/file_pool.cpp new file mode 100644 index 0000000000..a53f2e9374 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file_pool.cpp @@ -0,0 +1,326 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include "libtorrent/assert.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/file_storage.hpp" // for file_entry + +namespace libtorrent +{ + + file_pool::file_pool(int size) + : m_size(size) + , m_low_prio_io(true) +#if TORRENT_CLOSE_MAY_BLOCK + , m_stop_thread(false) + , m_closer_thread(boost::bind(&file_pool::closer_thread_fun, this)) +#endif + { + } + + file_pool::~file_pool() + { +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l(m_closer_mutex); + m_stop_thread = true; + l.unlock(); + // wait for hte closer thread to finish closing all files + m_closer_thread.join(); +#endif + } + +#if TORRENT_CLOSE_MAY_BLOCK + void file_pool::closer_thread_fun() + { + for (;;) + { + mutex::scoped_lock l(m_closer_mutex); + if (m_stop_thread) + { + l.unlock(); + m_queued_for_close.clear(); + return; + } + + // find a file that doesn't have any other threads referencing + // it. Since only those files can be closed in this thead + std::vector >::iterator i = std::find_if( + m_queued_for_close.begin(), m_queued_for_close.end() + , boost::bind(&file::refcount, boost::bind(&boost::intrusive_ptr::get, _1)) == 1); + + if (i == m_queued_for_close.end()) + { + l.unlock(); + // none of the files are ready to be closed yet + // because they're still in use by other threads + // hold off for a while + sleep(100); + } + else + { + // ok, first pull the file out of the queue, release the mutex + // (since closing the file may block) and then close it. + boost::intrusive_ptr f = *i; + m_queued_for_close.erase(i); + l.unlock(); + f->close(); + } + } + } +#endif + +#ifdef TORRENT_WINDOWS + void set_low_priority(boost::intrusive_ptr const& f) + { + // file prio is only supported on vista and up + // so load the functions dynamically + typedef enum _FILE_INFO_BY_HANDLE_CLASS { + FileBasicInfo, + FileStandardInfo, + FileNameInfo, + FileRenameInfo, + FileDispositionInfo, + FileAllocationInfo, + FileEndOfFileInfo, + FileStreamInfo, + FileCompressionInfo, + FileAttributeTagInfo, + FileIdBothDirectoryInfo, + FileIdBothDirectoryRestartInfo, + FileIoPriorityHintInfo, + FileRemoteProtocolInfo, + MaximumFileInfoByHandleClass + } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; + + typedef enum _PRIORITY_HINT { + IoPriorityHintVeryLow = 0, + IoPriorityHintLow, + IoPriorityHintNormal, + MaximumIoPriorityHintType + } PRIORITY_HINT; + + typedef struct _FILE_IO_PRIORITY_HINT_INFO { + PRIORITY_HINT PriorityHint; + } FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO; + + typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); + static SetFileInformationByHandle_t SetFileInformationByHandle = NULL; + + static bool failed_kernel_load = false; + + if (failed_kernel_load) return; + + if (SetFileInformationByHandle == NULL) + { + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32 == NULL) + { + failed_kernel_load = true; + return; + } + + SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle"); + if (SetFileInformationByHandle == NULL) + { + failed_kernel_load = true; + return; + } + } + + TORRENT_ASSERT(SetFileInformationByHandle); + + FILE_IO_PRIORITY_HINT_INFO io_hint; + io_hint.PriorityHint = IoPriorityHintLow; + SetFileInformationByHandle(f->native_handle(), + FileIoPriorityHintInfo, &io_hint, sizeof(io_hint)); + } +#endif // TORRENT_WINDOWS + + boost::intrusive_ptr file_pool::open_file(void* st, std::string const& p + , int file_index, file_storage const& fs, int m, error_code& ec) + { + TORRENT_ASSERT(st != 0); + TORRENT_ASSERT(is_complete(p)); + TORRENT_ASSERT((m & file::rw_mask) == file::read_only + || (m & file::rw_mask) == file::read_write); + mutex::scoped_lock l(m_mutex); + file_set::iterator i = m_files.find(std::make_pair(st, file_index)); + if (i != m_files.end()) + { + lru_file_entry& e = i->second; + e.last_use = time_now(); + + if (e.key != st && ((e.mode & file::rw_mask) != file::read_only + || (m & file::rw_mask) != file::read_only)) + { + // this means that another instance of the storage + // is using the exact same file. +#if BOOST_VERSION >= 103500 + ec = errors::file_collision; +#endif + return boost::intrusive_ptr(); + } + + e.key = st; + // if we asked for a file in write mode, + // and the cached file is is not opened in + // write mode, re-open it + if ((((e.mode & file::rw_mask) != file::read_write) + && ((m & file::rw_mask) == file::read_write)) + || (e.mode & file::no_buffer) != (m & file::no_buffer) + || (e.mode & file::random_access) != (m & file::random_access)) + { + // close the file before we open it with + // the new read/write privilages + TORRENT_ASSERT(e.file_ptr->refcount() == 1); + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l(m_closer_mutex); + m_queued_for_close.push_back(e.file_ptr); + l.unlock(); + e.file_ptr = new file; +#else + e.file_ptr->close(); +#endif + std::string full_path = fs.file_path(file_index, p); + if (!e.file_ptr->open(full_path, m, ec)) + { + m_files.erase(i); + return boost::intrusive_ptr(); + } +#ifdef TORRENT_WINDOWS + if (m_low_prio_io) + set_low_priority(e.file_ptr); +#endif + TORRENT_ASSERT(e.file_ptr->is_open()); + e.mode = m; + } + TORRENT_ASSERT((e.mode & file::no_buffer) == (m & file::no_buffer)); + return e.file_ptr; + } + // the file is not in our cache + if ((int)m_files.size() >= m_size) + { + // the file cache is at its maximum size, close + // the least recently used (lru) file from it + remove_oldest(); + } + lru_file_entry e; + e.file_ptr.reset(new (std::nothrow)file); + if (!e.file_ptr) + { + ec = error_code(ENOMEM, get_posix_category()); + return e.file_ptr; + } + std::string full_path = fs.file_path(file_index, p); + if (!e.file_ptr->open(full_path, m, ec)) + return boost::intrusive_ptr(); +#ifdef TORRENT_WINDOWS + if (m_low_prio_io) + set_low_priority(e.file_ptr); +#endif + e.mode = m; + e.key = st; + m_files.insert(std::make_pair(std::make_pair(st, file_index), e)); + TORRENT_ASSERT(e.file_ptr->is_open()); + return e.file_ptr; + } + + void file_pool::remove_oldest() + { + file_set::iterator i = std::min_element(m_files.begin(), m_files.end() + , boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _1)) + < boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _2))); + if (i == m_files.end()) return; + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l_(m_closer_mutex); + m_queued_for_close.push_back(i->second.file_ptr); + l_.unlock(); +#endif + m_files.erase(i); + } + + void file_pool::release(void* st, int file_index) + { + mutex::scoped_lock l(m_mutex); + file_set::iterator i = m_files.find(std::make_pair(st, file_index)); + if (i == m_files.end()) return; + +#if TORRENT_CLOSE_MAY_BLOCK + mutex::scoped_lock l2(m_closer_mutex); + m_queued_for_close.push_back(i->second.file_ptr); + l2.unlock(); +#endif + m_files.erase(i); + } + + // closes files belonging to the specified + // storage. If 0 is passed, all files are closed + void file_pool::release(void* st) + { + mutex::scoped_lock l(m_mutex); + if (st == 0) + { + m_files.clear(); + return; + } + + for (file_set::iterator i = m_files.begin(); + i != m_files.end();) + { + if (i->second.key == st) + m_files.erase(i++); + else + ++i; + } + } + + void file_pool::resize(int size) + { + TORRENT_ASSERT(size > 0); + + if (size == m_size) return; + mutex::scoped_lock l(m_mutex); + m_size = size; + if (int(m_files.size()) <= m_size) return; + + // close the least recently used files + while (int(m_files.size()) > m_size) + remove_oldest(); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/file_storage.cpp b/apps/Launcher/ext/libtorrent/src/file_storage.cpp new file mode 100644 index 0000000000..a70a7fb6d7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/file_storage.cpp @@ -0,0 +1,831 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/file_storage.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/file.hpp" +#include "libtorrent/utf8.hpp" +#include +#include +#include + +namespace libtorrent +{ + file_storage::file_storage() + : m_total_size(0) + , m_num_pieces(0) + , m_piece_length(0) + {} + + file_storage::~file_storage() {} + + // even though this copy constructor and the copy assignment + // operator are identical to what the compiler would have + // generated, they are put here to explicitly make them part + // of libtorrent and properly exported by the .dll. + file_storage::file_storage(file_storage const& f) + : m_files(f.m_files) + , m_file_hashes(f.m_file_hashes) + , m_symlinks(f.m_symlinks) + , m_mtime(f.m_mtime) + , m_file_base(f.m_file_base) + , m_paths(f.m_paths) + , m_name(f.m_name) + , m_total_size(f.m_total_size) + , m_num_pieces(f.m_num_pieces) + , m_piece_length(f.m_piece_length) + { + } + + file_storage& file_storage::operator=(file_storage const& f) + { + m_files = f.m_files; + m_file_hashes = f.m_file_hashes; + m_symlinks = f.m_symlinks; + m_mtime = f.m_mtime; + m_file_base = f.m_file_base; + m_paths = f.m_paths; + m_name = f.m_name; + m_total_size = f.m_total_size; + m_num_pieces = f.m_num_pieces; + m_piece_length = f.m_piece_length; + return *this; + } + + void file_storage::reserve(int num_files) + { + m_files.reserve(num_files); + } + + int file_storage::piece_size(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < num_pieces()); + if (index == num_pieces()-1) + { + size_type size_except_last = num_pieces() - 1; + size_except_last *= size_type(piece_length()); + size_type size = total_size() - size_except_last; + TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(size <= piece_length()); + return int(size); + } + else + return piece_length(); + } + + void file_storage::update_path_index(internal_file_entry& e) + { + std::string fname = e.filename(); + if (is_complete(fname)) + { + e.path_index = -2; + return; + } + std::string parent = parent_path(fname); + + if (parent.empty()) + { + e.path_index = -1; + return; + } + + if (parent.size() >= m_name.size() + && parent.compare(0, m_name.size(), m_name) == 0 + && (parent.size() == m_name.size() +#ifdef TORRENT_WINDOWS + || parent[m_name.size()] == '\\' +#endif + || parent[m_name.size()] == '/' + )) + { + parent.erase(parent.begin(), parent.begin() + m_name.size() + + (m_name.size() == parent.size()?0:1)); + e.no_root_dir = false; + } + else + { + e.no_root_dir = true; + } + + // do we already have this path in the path list? + std::vector::reverse_iterator p + = std::find(m_paths.rbegin(), m_paths.rend(), parent); + + if (p == m_paths.rend()) + { + // no, we don't. add it + e.path_index = m_paths.size(); + m_paths.push_back(parent); + } + else + { + // yes we do. use it + e.path_index = p.base() - m_paths.begin() - 1; + } + e.set_name(filename(e.filename()).c_str()); + } + + file_entry::file_entry(): offset(0), size(0), file_base(0) + , mtime(0), pad_file(false), hidden_attribute(false) + , executable_attribute(false) + , symlink_attribute(false) + {} + + file_entry::~file_entry() {} + + internal_file_entry::~internal_file_entry() + { + if (name_len == name_is_owned) free((void*)name); + } + + internal_file_entry::internal_file_entry(internal_file_entry const& fe) + : offset(fe.offset) + , symlink_index(fe.symlink_index) + , no_root_dir(fe.no_root_dir) + , size(fe.size) + , name_len(fe.name_len) + , pad_file(fe.pad_file) + , hidden_attribute(fe.hidden_attribute) + , executable_attribute(fe.executable_attribute) + , symlink_attribute(fe.symlink_attribute) + , name(0) + , path_index(fe.path_index) + { + set_name(fe.filename().c_str()); + } + + internal_file_entry& internal_file_entry::operator=(internal_file_entry const& fe) + { + offset = fe.offset; + size = fe.size; + path_index = fe.path_index; + symlink_index = fe.symlink_index; + pad_file = fe.pad_file; + hidden_attribute = fe.hidden_attribute; + executable_attribute = fe.executable_attribute; + symlink_attribute = fe.symlink_attribute; + no_root_dir = fe.no_root_dir; + set_name(fe.filename().c_str()); + return *this; + } + + // if borrow_chars >= 0, don't take ownership over n, just + // point to it. It points to borrow_chars number of characters. + // if borrow_chars == -1, n is a null terminated string that + // should be copied + void internal_file_entry::set_name(char const* n, bool borrow_string, int string_len) + { + TORRENT_ASSERT(string_len >= 0); + + // we have limited space in the length field. truncate string + // if it's too long + if (string_len >= name_is_owned) string_len = name_is_owned - 1; + + // free the current string, before assigning the new one + if (name_len == name_is_owned) free((void*)name); + if (n == NULL) + { + TORRENT_ASSERT(borrow_string == false); + name = NULL; + } + else if (borrow_string) + { + name = n; + name_len = string_len; + } + else + { + name = allocate_string_copy(n); + name_len = name_is_owned; + } + } + + std::string internal_file_entry::filename() const + { + if (name_len != name_is_owned) return std::string(name, name_len); + return name ? name : ""; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void file_storage::set_name(std::wstring const& n) + { + std::string utf8; + wchar_utf8(n, utf8); + m_name = utf8; + } + + void file_storage::rename_file_deprecated(int index, std::wstring const& new_filename) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + std::string utf8; + wchar_utf8(new_filename, utf8); + m_files[index].set_name(utf8.c_str()); + update_path_index(m_files[index]); + } + + void file_storage::add_file(std::wstring const& file, size_type size, int flags + , std::time_t mtime, std::string const& symlink_path) + { + std::string utf8; + wchar_utf8(file, utf8); + add_file(utf8, size, flags, mtime, symlink_path); + } + + void file_storage::rename_file(int index, std::wstring const& new_filename) + { + rename_file_deprecated(index, new_filename); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void file_storage::rename_file(int index, std::string const& new_filename) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + m_files[index].set_name(new_filename.c_str()); + update_path_index(m_files[index]); + } + + void file_storage::rename_file_borrow(int index, char const* new_filename, int len) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + m_files[index].set_name(new_filename, true, len); + } + + namespace + { + bool compare_file_offset(internal_file_entry const& lhs, internal_file_entry const& rhs) + { + return lhs.offset < rhs.offset; + } + } + +#ifndef TORRENT_NO_DEPRECATE + file_storage::iterator file_storage::file_at_offset_deprecated(size_type offset) const + { + // find the file iterator and file offset + internal_file_entry target; + target.offset = offset; + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + begin_deprecated(), end_deprecated(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != begin_deprecated()); + --file_iter; + return file_iter; + } + + file_storage::iterator file_storage::file_at_offset(size_type offset) const + { + return file_at_offset_deprecated(offset); + } +#endif + + int file_storage::file_index_at_offset(size_type offset) const + { + // find the file iterator and file offset + internal_file_entry target; + target.offset = offset; + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + m_files.begin(), m_files.end(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != m_files.begin()); + --file_iter; + return file_iter - m_files.begin(); + } + + char const* file_storage::file_name_ptr(int index) const + { + return m_files[index].name; + } + + int file_storage::file_name_len(int index) const + { + if (m_files[index].name_len == internal_file_entry::name_is_owned) + return -1; + return m_files[index].name_len; + } + + std::vector file_storage::map_block(int piece, size_type offset + , int size) const + { + TORRENT_ASSERT_PRECOND(num_files() > 0); + std::vector ret; + + if (m_files.empty()) return ret; + + // find the file iterator and file offset + internal_file_entry target; + target.offset = piece * (size_type)m_piece_length + offset; + TORRENT_ASSERT_PRECOND(size_type(target.offset + size) <= m_total_size); + TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); + + std::vector::const_iterator file_iter = std::upper_bound( + m_files.begin(), m_files.end(), target, compare_file_offset); + + TORRENT_ASSERT(file_iter != m_files.begin()); + --file_iter; + + size_type file_offset = target.offset - file_iter->offset; + for (; size > 0; file_offset -= file_iter->size, ++file_iter) + { + TORRENT_ASSERT(file_iter != m_files.end()); + if (file_offset < size_type(file_iter->size)) + { + file_slice f; + f.file_index = file_iter - m_files.begin(); + f.offset = file_offset + file_base(f.file_index); + f.size = (std::min)(boost::uint64_t(file_iter->size) - file_offset, boost::uint64_t(size)); + TORRENT_ASSERT(f.size <= size); + size -= int(f.size); + file_offset += f.size; + ret.push_back(f); + } + + TORRENT_ASSERT(size >= 0); + } + return ret; + } + + file_entry file_storage::at(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + file_entry ret; + internal_file_entry const& ife = m_files[index]; + ret.path = file_path(index); + ret.offset = ife.offset; + ret.size = ife.size; + ret.file_base = file_base(index); + ret.mtime = mtime(index); + ret.pad_file = ife.pad_file; + ret.hidden_attribute = ife.hidden_attribute; + ret.executable_attribute = ife.executable_attribute; + ret.symlink_attribute = ife.symlink_attribute; + if (ife.symlink_index != internal_file_entry::not_a_symlink) + ret.symlink_path = symlink(index); + ret.filehash = hash(index); + return ret; + } + + peer_request file_storage::map_file(int file_index, size_type file_offset + , int size) const + { + TORRENT_ASSERT_PRECOND(file_index < num_files()); + TORRENT_ASSERT_PRECOND(file_index >= 0); + TORRENT_ASSERT(m_num_pieces >= 0); + + peer_request ret; + if (file_index < 0 || file_index >= num_files()) + { + ret.piece = m_num_pieces; + ret.start = 0; + ret.length = 0; + return ret; + } + + size_type offset = file_offset + this->file_offset(file_index); + + if (offset >= total_size()) + { + ret.piece = m_num_pieces; + ret.start = 0; + ret.length = 0; + } + else + { + ret.piece = int(offset / piece_length()); + ret.start = int(offset % piece_length()); + ret.length = size; + if (offset + size > total_size()) + ret.length = int(total_size() - offset); + } + return ret; + } + + void file_storage::add_file(std::string const& file, size_type size, int flags + , std::time_t mtime, std::string const& symlink_path) + { + TORRENT_ASSERT_PRECOND(!is_complete(file)); + TORRENT_ASSERT_PRECOND(size >= 0); + if (size < 0) size = 0; + if (!has_parent_path(file)) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + TORRENT_ASSERT_PRECOND(m_files.empty()); + m_name = file; + } + else + { + if (m_files.empty()) + m_name = split_path(file).c_str(); + } + TORRENT_ASSERT_PRECOND(m_name == split_path(file).c_str()); + m_files.push_back(internal_file_entry()); + internal_file_entry& e = m_files.back(); + e.set_name(file.c_str()); + e.size = size; + e.offset = m_total_size; + e.pad_file = (flags & pad_file) != 0; + e.hidden_attribute = (flags & attribute_hidden) != 0; + e.executable_attribute = (flags & attribute_executable) != 0; + if ((flags & attribute_symlink) && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) + { + e.symlink_attribute = 1; + e.symlink_index = m_symlinks.size(); + m_symlinks.push_back(symlink_path); + } + else + e.symlink_attribute = 0; + + if (mtime) + { + if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); + m_mtime[m_files.size() - 1] = mtime; + } + + update_path_index(e); + m_total_size += size; + } + + // TODO: 2 it would be nice if file_entry::filehash could be taken into + // account as well, and if the file_storage object could actually hold + // copies of filehash + void file_storage::add_file(file_entry const& ent, char const* filehash) + { + TORRENT_ASSERT_PRECOND(ent.size >= 0); + if (!has_parent_path(ent.path)) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + TORRENT_ASSERT_PRECOND(m_files.empty()); + m_name = ent.path; + } + else + { + if (m_files.empty()) + m_name = split_path(ent.path).c_str(); + } + internal_file_entry ife(ent); + int file_index = m_files.size(); + m_files.push_back(ife); + internal_file_entry& e = m_files.back(); + e.offset = m_total_size; + m_total_size += e.size; + if (filehash) + { + if (m_file_hashes.size() < m_files.size()) m_file_hashes.resize(m_files.size()); + m_file_hashes[m_files.size() - 1] = filehash; + } + if (!ent.symlink_path.empty() && m_symlinks.size() < internal_file_entry::not_a_symlink - 1) + { + e.symlink_index = m_symlinks.size(); + m_symlinks.push_back(ent.symlink_path); + } + if (ent.mtime) + { + if (m_mtime.size() < m_files.size()) m_mtime.resize(m_files.size()); + m_mtime[m_files.size() - 1] = ent.mtime; + } + if (ent.file_base) set_file_base(file_index, ent.file_base); + update_path_index(e); + } + + sha1_hash file_storage::hash(int index) const + { + if (index >= int(m_file_hashes.size())) return sha1_hash(0); + return sha1_hash(m_file_hashes[index]); + } + + std::string const& file_storage::symlink(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); + return m_symlinks[fe.symlink_index]; + } + + time_t file_storage::mtime(int index) const + { + if (index >= int(m_mtime.size())) return 0; + return m_mtime[index]; + } + + void file_storage::set_file_base(int index, size_type off) + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); + m_file_base[index] = off; + } + + size_type file_storage::file_base(int index) const + { + if (index >= int(m_file_base.size())) return 0; + return m_file_base[index]; + } + + std::string file_storage::file_path(int index, std::string const& save_path) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + + // -2 means this is an absolute path filename + if (fe.path_index == -2) return fe.filename(); + + // -1 means no path + if (fe.path_index == -1) return combine_path(save_path, fe.filename()); + + if (fe.no_root_dir) + return combine_path(save_path + , combine_path(m_paths[fe.path_index] + , fe.filename())); + + return combine_path(save_path + , combine_path(m_name + , combine_path(m_paths[fe.path_index] + , fe.filename()))); + } + + std::string file_storage::file_name(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + internal_file_entry const& fe = m_files[index]; + return fe.filename(); + } + + size_type file_storage::file_size(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].size; + } + + bool file_storage::pad_file_at(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].pad_file; + } + + size_type file_storage::file_offset(int index) const + { + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return m_files[index].offset; + } + + int file_storage::file_flags(int index) const + { + internal_file_entry const& fe = m_files[index]; + return (fe.pad_file ? flag_pad_file : 0) + | (fe.hidden_attribute ? flag_hidden : 0) + | (fe.executable_attribute ? flag_executable : 0) + | (fe.symlink_attribute ? flag_symlink : 0); + } + +#ifndef TORRENT_NO_DEPRECATE + sha1_hash file_storage::hash(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_file_hashes.size())) return sha1_hash(0); + return sha1_hash(m_file_hashes[index]); + } + + std::string const& file_storage::symlink(internal_file_entry const& fe) const + { + TORRENT_ASSERT_PRECOND(fe.symlink_index < int(m_symlinks.size())); + return m_symlinks[fe.symlink_index]; + } + + time_t file_storage::mtime(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_mtime.size())) return 0; + return m_mtime[index]; + } + + int file_storage::file_index(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + return index; + } + + void file_storage::set_file_base(internal_file_entry const& fe, size_type off) + { + int index = &fe - &m_files[0]; + TORRENT_ASSERT_PRECOND(index >= 0 && index < int(m_files.size())); + if (int(m_file_base.size()) <= index) m_file_base.resize(index + 1, 0); + m_file_base[index] = off; + } + + size_type file_storage::file_base(internal_file_entry const& fe) const + { + int index = &fe - &m_files[0]; + if (index >= int(m_file_base.size())) return 0; + return m_file_base[index]; + } + + std::string file_storage::file_path(internal_file_entry const& fe + , std::string const& save_path) const + { + int index = &fe - &m_files[0]; + return file_path(index); + } + + std::string file_storage::file_name(internal_file_entry const& fe) const + { + return fe.filename(); + } + + size_type file_storage::file_size(internal_file_entry const& fe) const + { + return fe.size; + } + + bool file_storage::pad_file_at(internal_file_entry const& fe) const + { + return fe.pad_file; + } + + size_type file_storage::file_offset(internal_file_entry const& fe) const + { + return fe.offset; + } + + file_entry file_storage::at(file_storage::iterator i) const + { return at(i - m_files.begin()); } +#endif // TORRENT_NO_DEPRECATE + + bool compare_file_entry_size(internal_file_entry const& fe1, internal_file_entry const& fe2) + { return fe1.size < fe2.size; } + + void file_storage::reorder_file(int index, int dst) + { + TORRENT_ASSERT(index < int(m_files.size())); + TORRENT_ASSERT(dst < int(m_files.size())); + TORRENT_ASSERT(dst < index); + + std::iter_swap(m_files.begin() + index, m_files.begin() + dst); + if (!m_mtime.empty()) + { + TORRENT_ASSERT(m_mtime.size() == m_files.size()); + if (int(m_mtime.size()) < index) m_mtime.resize(index+1, 0); + std::iter_swap(m_mtime.begin() + dst, m_mtime.begin() + index); + } + if (!m_file_hashes.empty()) + { + TORRENT_ASSERT(m_file_hashes.size() == m_files.size()); + if (int(m_file_hashes.size()) < index) m_file_hashes.resize(index + 1, NULL); + std::iter_swap(m_file_hashes.begin() + dst, m_file_hashes.begin() + index); + } + if (!m_file_base.empty()) + { + TORRENT_ASSERT(m_file_base.size() == m_files.size()); + if (int(m_file_base.size()) < index) m_file_base.resize(index + 1, 0); + std::iter_swap(m_file_base.begin() + dst, m_file_base.begin() + index); + } + } + + void file_storage::optimize(int pad_file_limit, int alignment) + { + if (alignment == -1) + alignment = m_piece_length; + + size_type off = 0; + int padding_file = 0; + for (std::vector::iterator i = m_files.begin(); + i != m_files.end(); ++i) + { + if ((off % alignment) == 0) + { + // this file position is aligned, pick the largest + // available file to put here + std::vector::iterator best_match + = std::max_element(i, m_files.end() + , &compare_file_entry_size); + + if (best_match != i) + { + int index = best_match - m_files.begin(); + int cur_index = i - m_files.begin(); + reorder_file(index, cur_index); + i = m_files.begin() + cur_index; + } + } + else if (pad_file_limit >= 0 + && i->size > boost::uint32_t(pad_file_limit) + && i->pad_file == false) + { + // if we have pad files enabled, and this file is + // not piece-aligned and the file size exceeds the + // limit, and it's not a padding file itself. + // so add a padding file in front of it + int pad_size = alignment - (off % alignment); + + // find the largest file that fits in pad_size + std::vector::iterator best_match = m_files.end(); + + // if pad_file_limit is 0, it means all files are padded, there's + // no point in trying to find smaller files to use as filling + if (pad_file_limit > 0) + { + for (std::vector::iterator j = i+1; j < m_files.end(); ++j) + { + if (j->size > boost::uint32_t(pad_size)) continue; + if (best_match == m_files.end() || j->size > best_match->size) + best_match = j; + } + + if (best_match != m_files.end()) + { + // we found one + // We cannot have found i, because i->size > pad_file_limit + // which is forced to be no less than alignment. We only + // look for files <= pad_size, which never is greater than + // alignment + TORRENT_ASSERT(best_match != i); + int index = best_match - m_files.begin(); + int cur_index = i - m_files.begin(); + reorder_file(index, cur_index); + i = m_files.begin() + cur_index; + i->offset = off; + off += i->size; + continue; + } + } + + // we could not find a file that fits in pad_size + // add a padding file + // note that i will be set to point to the + // new pad file. Once we're done adding it, we need + // to increment i to point to the current file again + // first add the pad file to the end of the file list + // then swap it in place. This minimizes the amount + // of copying of internal_file_entry, which is somewhat + // expensive (until we have move semantics) + int cur_index = i - m_files.begin(); + int index = m_files.size(); + m_files.push_back(internal_file_entry()); + internal_file_entry& e = m_files.back(); + // i may have been invalidated, refresh it + i = m_files.begin() + cur_index; + e.size = pad_size; + e.offset = off; + char name[30]; + snprintf(name, sizeof(name), ".____padding_file/%d", padding_file); + std::string path = combine_path(m_name, name); + e.set_name(path.c_str()); + e.pad_file = true; + off += pad_size; + ++padding_file; + + if (!m_mtime.empty()) m_mtime.resize(index + 1, 0); + if (!m_file_hashes.empty()) m_file_hashes.resize(index + 1, NULL); + if (!m_file_base.empty()) m_file_base.resize(index + 1, 0); + + reorder_file(index, cur_index); + + TORRENT_ASSERT((off % alignment) == 0); + continue; + } + i->offset = off; + off += i->size; + } + m_total_size = off; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/gzip.cpp b/apps/Launcher/ext/libtorrent/src/gzip.cpp new file mode 100644 index 0000000000..9af00606eb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/gzip.cpp @@ -0,0 +1,268 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/assert.hpp" +#include "libtorrent/puff.hpp" +#include "libtorrent/gzip.hpp" + +#include +#include + +namespace +{ + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + struct gzip_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* gzip_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "gzip error"; + } + + std::string gzip_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "invalid gzip header", + "inflated data too large", + "available inflate data did not terminate", + "output space exhausted before completing inflate", + "invalid block type (type == 3)", + "stored block length did not match one's complement", + "dynamic block code description: too many length or distance codes", + "dynamic block code description: code lengths codes incomplete", + "dynamic block code description: repeat lengths with no first length", + "dynamic block code description: repeat more than specified lengths", + "dynamic block code description: invalid literal/length code lengths", + "dynamic block code description: invalid distance code lengths", + "invalid literal/length or distance code in fixed or dynamic block", + "distance is too far back in fixed or dynamic block", + "unknown gzip error", + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_gzip_category() + { + static gzip_error_category gzip_category; + return gzip_category; + } + + namespace gzip_errors + { + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_gzip_category()); + } + } + + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size) + { + TORRENT_ASSERT(buf != 0); + + const unsigned char* buffer = reinterpret_cast(buf); + const int total_size = size; + + // The zip header cannot be shorter than 10 bytes + if (size < 10 || buf == 0) return -1; + + // check the magic header of gzip + if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1; + + int method = buffer[2]; + int flags = buffer[3]; + + // check for reserved flag and make sure it's compressed with the correct metod + if (method != 8 || (flags & FRESERVED) != 0) return -1; + + // skip time, xflags, OS code + size -= 10; + buffer += 10; + + if (flags & FEXTRA) + { + int extra_len; + + if (size < 2) return -1; + + extra_len = (buffer[1] << 8) | buffer[0]; + + if (size < (extra_len+2)) return -1; + size -= (extra_len + 2); + buffer += (extra_len + 2); + } + + if (flags & FNAME) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FCOMMENT) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FHCRC) + { + if (size < 2) return -1; + + size -= 2; +// buffer += 2; + } + + return total_size - size; + } + + TORRENT_EXTRA_EXPORT void inflate_gzip( + char const* in + , int size + , std::vector& buffer + , int maximum_size + , error_code& ec) + { + ec.clear(); + TORRENT_ASSERT(maximum_size > 0); + + int header_len = gzip_header(in, size); + if (header_len < 0) + { + ec = gzip_errors::invalid_gzip_header; + return; + } + + // start off with 4 kilobytes and grow + // if needed + boost::uint32_t destlen = 4096; + int ret = 0; + boost::uint32_t srclen = size - header_len; + in += header_len; + + do + { + TORRENT_TRY { + buffer.resize(destlen); + } TORRENT_CATCH(std::exception& e) { + ec = errors::no_memory; + return; + } + + ret = puff((unsigned char*)&buffer[0], &destlen, (unsigned char*)in, &srclen); + + // if the destination buffer wasn't large enough, double its + // size and try again. Unless it's already at its max, in which + // case we fail + if (ret == 1) // 1: output space exhausted before completing inflate + { + if (destlen == boost::uint32_t(maximum_size)) + { + ec = gzip_errors::inflated_data_too_large; + return; + } + + destlen *= 2; + if (destlen > (unsigned int)maximum_size) + destlen = maximum_size; + } + } while (ret == 1); + + if (ret != 0) + { + switch (ret) + { + case 2: ec = gzip_errors::data_did_not_terminate; return; + case 1: ec = gzip_errors::space_exhausted; return; + case -1: ec = gzip_errors::invalid_block_type; return; + case -2: ec = gzip_errors::invalid_stored_block_length; return; + case -3: ec = gzip_errors::too_many_length_or_distance_codes; return; + case -4: ec = gzip_errors::code_lengths_codes_incomplete; return; + case -5: ec = gzip_errors::repeat_lengths_with_no_first_length; return; + case -6: ec = gzip_errors::repeat_more_than_specified_lengths; return; + case -7: ec = gzip_errors::invalid_literal_length_code_lengths; return; + case -8: ec = gzip_errors::invalid_distance_code_lengths; return; + case -9: ec = gzip_errors::invalid_literal_code_in_block; return; + case -10: ec = gzip_errors::distance_too_far_back_in_block; return; + default: ec = gzip_errors::unknown_gzip_error; return; + } + } + + if (destlen > buffer.size()) + { + ec = gzip_errors::unknown_gzip_error; + return; + } + + buffer.resize(destlen); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/hasher.cpp b/apps/Launcher/ext/libtorrent/src/hasher.cpp new file mode 100644 index 0000000000..35d0167608 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/hasher.cpp @@ -0,0 +1,136 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/hasher.hpp" + +namespace libtorrent +{ + hasher::hasher() + { +#ifdef TORRENT_USE_GCRYPT + gcry_md_open(&m_context, GCRY_MD_SHA1, 0); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); +#else + SHA1_init(&m_context); +#endif + } + + hasher::hasher(const char* data, int len) + { + TORRENT_ASSERT(data != 0); + TORRENT_ASSERT(len > 0); +#ifdef TORRENT_USE_GCRYPT + gcry_md_open(&m_context, GCRY_MD_SHA1, 0); + gcry_md_write(m_context, data, len); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); + CC_SHA1_Update(&m_context, reinterpret_cast(data), len); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); + SHA1_Update(&m_context, reinterpret_cast(data), len); +#else + SHA1_init(&m_context); + SHA1_update(&m_context, reinterpret_cast(data), len); +#endif + } + +#ifdef TORRENT_USE_GCRYPT + hasher::hasher(hasher const& h) + { + gcry_md_copy(&m_context, h.m_context); + } + + hasher& hasher::operator=(hasher const& h) + { + gcry_md_close(m_context); + gcry_md_copy(&m_context, h.m_context); + return *this; + } +#endif + + hasher& hasher::update(const char* data, int len) + { + TORRENT_ASSERT(data != 0); + TORRENT_ASSERT(len > 0); +#ifdef TORRENT_USE_GCRYPT + gcry_md_write(m_context, data, len); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Update(&m_context, reinterpret_cast(data), len); +#elif defined TORRENT_USE_OPENSSL + SHA1_Update(&m_context, reinterpret_cast(data), len); +#else + SHA1_update(&m_context, reinterpret_cast(data), len); +#endif + return *this; + } + + sha1_hash hasher::final() + { + sha1_hash digest; +#ifdef TORRENT_USE_GCRYPT + gcry_md_final(m_context); + digest.assign((const char*)gcry_md_read(m_context, 0)); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Final(digest.begin(), &m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Final(digest.begin(), &m_context); +#else + SHA1_final(digest.begin(), &m_context); +#endif + return digest; + } + + void hasher::reset() + { +#ifdef TORRENT_USE_GCRYPT + gcry_md_reset(m_context); +#elif TORRENT_USE_COMMONCRYPTO + CC_SHA1_Init(&m_context); +#elif defined TORRENT_USE_OPENSSL + SHA1_Init(&m_context); +#else + SHA1_init(&m_context); +#endif + } + +#ifdef TORRENT_USE_GCRYPT + hasher::~hasher() + { + gcry_md_close(m_context); + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_connection.cpp new file mode 100644 index 0000000000..e91204e9e9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_connection.cpp @@ -0,0 +1,932 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/http_connection.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/gzip.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/socket_type.hpp" // for async_shutdown + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#include + +namespace libtorrent { + +http_connection::http_connection(io_service& ios, connection_queue& cc + , http_handler const& handler + , bool bottled + , int max_bottled_buffer_size + , http_connect_handler const& ch + , http_filter_handler const& fh +#ifdef TORRENT_USE_OPENSSL + , boost::asio::ssl::context* ssl_ctx +#endif + ) + : m_sock(ios) +#if TORRENT_USE_I2P + , m_i2p_conn(0) +#endif + , m_read_pos(0) + , m_resolver(ios) + , m_handler(handler) + , m_connect_handler(ch) + , m_filter_handler(fh) + , m_timer(ios) + , m_last_receive(time_now()) + , m_start_time(time_now()) + , m_bottled(bottled) + , m_max_bottled_buffer_size(max_bottled_buffer_size) + , m_called(false) +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx(ssl_ctx) + , m_own_ssl_context(false) +#endif + , m_rate_limit(0) + , m_download_quota(0) + , m_limiter_timer_active(false) + , m_limiter_timer(ios) + , m_redirects(5) + , m_connection_ticket(-1) + , m_cc(cc) + , m_ssl(false) + , m_priority(0) + , m_abort(false) +{ + TORRENT_ASSERT(!m_handler.empty()); +} + +http_connection::~http_connection() +{ + TORRENT_ASSERT(m_connection_ticket == -1); +#ifdef TORRENT_USE_OPENSSL + if (m_own_ssl_context) delete m_ssl_ctx; +#endif +} + +void http_connection::get(std::string const& url, time_duration timeout, int prio + , proxy_settings const* ps, int handle_redirects, std::string const& user_agent + , address const& bind_addr +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) +{ + m_user_agent = user_agent; + + std::string protocol; + std::string auth; + std::string hostname; + std::string path; + error_code ec; + int port; + + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(url, ec); + + int default_port = protocol == "https" ? 443 : 80; + if (port == -1) port = default_port; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + if (protocol != "http" +#ifdef TORRENT_USE_OPENSSL + && protocol != "https" +#endif + ) + { + error_code ec(errors::unsupported_url_protocol); + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + TORRENT_ASSERT(prio >= 0 && prio < 3); + + bool ssl = false; + if (protocol == "https") ssl = true; + + char request[4096]; + char* end = request + sizeof(request); + char* ptr = request; + +#define APPEND_FMT(fmt) ptr += snprintf(ptr, end - ptr, fmt) +#define APPEND_FMT1(fmt, arg) ptr += snprintf(ptr, end - ptr, fmt, arg) +#define APPEND_FMT2(fmt, arg1, arg2) ptr += snprintf(ptr, end - ptr, fmt, arg1, arg2) + + // exclude ssl here, because SSL assumes CONNECT support in the + // proxy and is handled at the lower layer + if (ps && (ps->type == proxy_settings::http + || ps->type == proxy_settings::http_pw) + && !ssl) + { + // if we're using an http proxy and not an ssl + // connection, just do a regular http proxy request + APPEND_FMT1("GET %s HTTP/1.1\r\n", url.c_str()); + if (ps->type == proxy_settings::http_pw) + APPEND_FMT1("Proxy-Authorization: Basic %s\r\n", base64encode( + ps->username + ":" + ps->password).c_str()); + + hostname = ps->hostname; + port = ps->port; + + APPEND_FMT1("Host: %s", hostname.c_str()); + if (port != default_port) APPEND_FMT1(":%d\r\n", port); + else APPEND_FMT("\r\n"); + } + else + { + APPEND_FMT2("GET %s HTTP/1.1\r\n" + "Host: %s", path.c_str(), hostname.c_str()); + if (port != default_port) APPEND_FMT1(":%d\r\n", port); + else APPEND_FMT("\r\n"); + } + +// APPEND_FMT("Accept: */*\r\n"); + + if (!m_user_agent.empty()) + APPEND_FMT1("User-Agent: %s\r\n", m_user_agent.c_str()); + + if (m_bottled) + APPEND_FMT("Accept-Encoding: gzip\r\n"); + + if (!auth.empty()) + APPEND_FMT1("Authorization: Basic %s\r\n", base64encode(auth).c_str()); + + APPEND_FMT("Connection: close\r\n\r\n"); + + sendbuffer.assign(request); + m_url = url; + start(hostname, to_string(port).elems, timeout, prio + , ps, ssl, handle_redirects, bind_addr +#if TORRENT_USE_I2P + , i2p_conn +#endif + ); +} + +void http_connection::start(std::string const& hostname, std::string const& port + , time_duration timeout, int prio, proxy_settings const* ps, bool ssl, int handle_redirects + , address const& bind_addr +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) +{ + TORRENT_ASSERT(prio >= 0 && prio < 3); + + m_redirects = handle_redirects; + if (ps) m_proxy = *ps; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + m_completion_timeout = timeout; + m_read_timeout = (std::max)(seconds(5), timeout / 5); + error_code ec; + m_timer.expires_from_now(m_completion_timeout, ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + m_timer.async_wait(boost::bind(&http_connection::on_timeout + , boost::weak_ptr(me), _1)); + m_called = false; + m_parser.reset(); + m_recvbuffer.clear(); + m_read_pos = 0; + m_priority = prio; + + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + + if (m_sock.is_open() && m_hostname == hostname && m_port == port + && m_ssl == ssl && m_bind_addr == bind_addr) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_write"); +#endif + async_write(m_sock, asio::buffer(sendbuffer) + , boost::bind(&http_connection::on_write, me, _1)); + } + else + { + m_ssl = ssl; + m_bind_addr = bind_addr; + error_code ec; + if (m_sock.is_open()) m_sock.close(ec); + +#if TORRENT_USE_I2P + bool is_i2p = false; + char const* top_domain = strrchr(hostname.c_str(), '.'); + if (top_domain && strcmp(top_domain, ".i2p") == 0 && i2p_conn) + { + // this is an i2p name, we need to use the sam connection + // to do the name lookup + is_i2p = true; + m_i2p_conn = i2p_conn; + // quadruple the timeout for i2p destinations + // because i2p is sloooooow + m_completion_timeout *= 4; + m_read_timeout *= 4; + } +#endif + +#if TORRENT_USE_I2P + if (is_i2p && i2p_conn->proxy().type != proxy_settings::i2p_proxy) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, error_code(errors::no_i2p_router, get_libtorrent_category()), (char*)0, 0)); + return; + } +#endif + + proxy_settings const* proxy = ps; +#if TORRENT_USE_I2P + if (is_i2p) proxy = &i2p_conn->proxy(); +#endif + + // in this case, the upper layer is assumed to have taken + // care of the proxying already. Don't instantiate the socket + // with this proxy + if (proxy && (proxy->type == proxy_settings::http + || proxy->type == proxy_settings::http_pw) + && !ssl) + { + proxy = 0; + } + proxy_settings null_proxy; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + if (m_ssl) + { + if (m_ssl_ctx == 0) + { + m_ssl_ctx = new (std::nothrow) boost::asio::ssl::context( + m_resolver.get_io_service(), asio::ssl::context::sslv23_client); + if (m_ssl_ctx) + { + m_own_ssl_context = true; + error_code ec; + m_ssl_ctx->set_verify_mode(asio::ssl::context::verify_none, ec); + TORRENT_ASSERT(!ec); + } + } + userdata = m_ssl_ctx; + } +#endif + instantiate_connection(m_resolver.get_io_service() + , proxy ? *proxy : null_proxy, m_sock, userdata); + + if (m_bind_addr != address_v4::any()) + { + error_code ec; + m_sock.open(m_bind_addr.is_v4()?tcp::v4():tcp::v6(), ec); + m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec); + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + } + + setup_ssl_hostname(m_sock, hostname, ec); + if (ec) + { + m_resolver.get_io_service().post(boost::bind(&http_connection::callback + , me, ec, (char*)0, 0)); + return; + } + +#if TORRENT_USE_I2P + if (is_i2p) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_i2p_resolve"); +#endif + i2p_conn->async_name_lookup(hostname.c_str(), boost::bind(&http_connection::on_i2p_resolve + , me, _1, _2)); + } + else +#endif + if (ps && ps->proxy_hostnames + && (ps->type == proxy_settings::socks5 + || ps->type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_port = port; + m_endpoints.push_back(tcp::endpoint(address(), atoi(port.c_str()))); + queue_connect(); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_resolve"); +#endif + m_endpoints.clear(); + tcp::resolver::query query(hostname, port); + m_resolver.async_resolve(query, boost::bind(&http_connection::on_resolve + , me, _1, _2)); + } + m_hostname = hostname; + m_port = port; + } +} + +void http_connection::on_connect_timeout() +{ + TORRENT_ASSERT(m_connection_ticket > -1); + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + error_code ec; + m_sock.close(ec); +} + +void http_connection::on_timeout(boost::weak_ptr p + , error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_timeout"); +#endif + boost::shared_ptr c = p.lock(); + if (!c) return; + + if (e == asio::error::operation_aborted) return; + + if (c->m_abort) return; + + ptime now = time_now_hires(); + + if (c->m_start_time + c->m_completion_timeout < now + || c->m_last_receive + c->m_read_timeout < now) + { + if (c->m_connection_ticket > -1 && !c->m_endpoints.empty()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + error_code ec; + async_shutdown(c->m_sock, c); + c->m_timer.expires_at((std::min)( + c->m_last_receive + c->m_read_timeout + , c->m_start_time + c->m_completion_timeout), ec); + c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); + } + else + { + c->callback(asio::error::timed_out); + c->close(true); + } + return; + } + + if (!c->m_sock.is_open()) return; +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_timeout"); +#endif + error_code ec; + c->m_timer.expires_at((std::min)( + c->m_last_receive + c->m_read_timeout + , c->m_start_time + c->m_completion_timeout), ec); + c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); +} + +void http_connection::close(bool force) +{ + if (m_abort) return; + + error_code ec; + m_timer.cancel(ec); + m_resolver.cancel(); + m_limiter_timer.cancel(ec); + + if (force) + m_sock.close(ec); + else + async_shutdown(m_sock, shared_from_this()); + + m_hostname.clear(); + m_port.clear(); + m_handler.clear(); + m_abort = true; +} + +#if TORRENT_USE_I2P +void http_connection::on_i2p_resolve(error_code const& e + , char const* destination) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_i2p_resolve"); +#endif + if (e) + { + callback(e); + close(); + return; + } + +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASSERT(m_ssl == false); + TORRENT_ASSERT(m_sock.get()); + TORRENT_ASSERT(m_sock.get()->get()); + m_sock.get()->get()->set_destination(destination); + m_sock.get()->get()->set_command(i2p_stream::cmd_connect); + m_sock.get()->get()->set_session_id(m_i2p_conn->session_id()); +#else + m_sock.get()->set_destination(destination); + m_sock.get()->set_command(i2p_stream::cmd_connect); + m_sock.get()->set_session_id(m_i2p_conn->session_id()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_connect"); +#endif + m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect + , shared_from_this(), _1)); +} +#endif + +void http_connection::on_resolve(error_code const& e + , tcp::resolver::iterator i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_resolve"); +#endif + if (e) + { + boost::shared_ptr me(shared_from_this()); + + callback(e); + close(); + return; + } + TORRENT_ASSERT(i != tcp::resolver::iterator()); + + std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1)); + + if (m_filter_handler) m_filter_handler(*this, m_endpoints); + if (m_endpoints.empty()) + { + close(); + return; + } + + // The following statement causes msvc to crash (ICE). Since it's not + // necessary in the vast majority of cases, just ignore the endpoint + // order for windows +#if !defined _MSC_VER || _MSC_VER > 1310 + // sort the endpoints so that the ones with the same IP version as our + // bound listen socket are first. So that when contacting a tracker, + // we'll talk to it from the same IP that we're listening on + if (m_bind_addr != address_v4::any()) + std::partition(m_endpoints.begin(), m_endpoints.end() + , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) + == m_bind_addr.is_v4()); +#endif + + queue_connect(); +} + +void http_connection::queue_connect() +{ + TORRENT_ASSERT(!m_endpoints.empty()); + tcp::endpoint target = m_endpoints.front(); + m_endpoints.pop_front(); + + m_cc.enqueue(boost::bind(&http_connection::connect, shared_from_this(), _1, target) + , boost::bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_read_timeout, m_priority); +} + +void http_connection::connect(int ticket, tcp::endpoint target_address) +{ + if (ticket == -1) + { + close(); + return; + } + + m_connection_ticket = ticket; + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it +#ifdef TORRENT_USE_OPENSSL + if (m_ssl) + { + TORRENT_ASSERT(m_sock.get >()); + m_sock.get >()->next_layer().set_dst_name(m_hostname); + } + else +#endif + { + TORRENT_ASSERT(m_sock.get()); + m_sock.get()->set_dst_name(m_hostname); + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_connect"); +#endif + m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect + , shared_from_this(), _1)); +} + +void http_connection::on_connect(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_connect"); +#endif + if (m_connection_ticket >= 0) + { + m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + } + + m_last_receive = time_now_hires(); + m_start_time = m_last_receive; + if (!e) + { + if (m_connect_handler) m_connect_handler(*this); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_write"); +#endif + async_write(m_sock, asio::buffer(sendbuffer) + , boost::bind(&http_connection::on_write, shared_from_this(), _1)); + } + else if (!m_endpoints.empty() && !m_abort) + { + // The connection failed. Try the next endpoint in the list. + error_code ec; + m_sock.close(ec); + queue_connect(); + } + else + { + boost::shared_ptr me(shared_from_this()); + callback(e); + close(); + } +} + +void http_connection::callback(error_code e, char* data, int size) +{ + if (m_bottled && m_called) return; + + std::vector buf; + if (data && m_bottled && m_parser.header_finished()) + { + size = m_parser.collapse_chunk_headers((char*)data, size); + + std::string const& encoding = m_parser.header("content-encoding"); + if ((encoding == "gzip" || encoding == "x-gzip") && size > 0 && data) + { + error_code ec; + inflate_gzip(data, size, buf, m_max_bottled_buffer_size, ec); + + if (ec) + { + if (m_handler) m_handler(ec, m_parser, data, size, *this); + close(); + return; + } + size = int(buf.size()); + data = size == 0 ? 0 : &buf[0]; + } + + // if we completed the whole response, no need + // to tell the user that the connection was closed by + // the server or by us. Just clear any error + if (m_parser.finished()) e.clear(); + } + m_called = true; + error_code ec; + m_timer.cancel(ec); + if (m_handler) m_handler(e, m_parser, data, size, *this); +} + +void http_connection::on_write(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_write"); +#endif + + if (e == asio::error::operation_aborted) return; + + if (e) + { + boost::shared_ptr me(shared_from_this()); + callback(e); + close(); + return; + } + + if (m_abort) return; + + std::string().swap(sendbuffer); + m_recvbuffer.resize(4096); + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + on_assign_bandwidth(error_code()); + } + return; + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_read(error_code const& e + , std::size_t bytes_transferred) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_read"); +#endif + + if (m_rate_limit) + { + m_download_quota -= bytes_transferred; + TORRENT_ASSERT(m_download_quota >= 0); + } + + if (e == asio::error::operation_aborted) return; + + if (m_abort) return; + + // keep ourselves alive even if the callback function + // deletes this object + boost::shared_ptr me(shared_from_this()); + + // when using the asio SSL wrapper, it seems like + // we get the shut_down error instead of EOF + if (e == asio::error::eof || e == asio::error::shut_down) + { + error_code ec = asio::error::eof; + TORRENT_ASSERT(bytes_transferred == 0); + char* data = 0; + std::size_t size = 0; + if (m_bottled && m_parser.header_finished()) + { + data = &m_recvbuffer[0] + m_parser.body_start(); + size = m_parser.get_body().left(); + } + callback(ec, data, size); + close(); + return; + } + + if (e) + { + TORRENT_ASSERT(bytes_transferred == 0); + callback(e); + close(); + return; + } + + m_read_pos += bytes_transferred; + TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size())); + + if (m_bottled || !m_parser.header_finished()) + { + libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] + , &m_recvbuffer[0] + m_read_pos); + bool error = false; + m_parser.incoming(rcv_buf, error); + if (error) + { + // HTTP parse error + error_code ec = errors::http_parse_error; + callback(ec, 0, 0); + return; + } + + // having a nonempty path means we should handle redirects + if (m_redirects && m_parser.header_finished()) + { + int code = m_parser.status_code(); + + if (is_redirect(code)) + { + // attempt a redirect + std::string const& location = m_parser.header("location"); + if (location.empty()) + { + // missing location header + callback(error_code(errors::http_missing_location)); + close(); + return; + } + + error_code ec; + // it would be nice to gracefully shut down SSL here + // but then we'd have to do all the reconnect logic + // in its handler. For now, just kill the connection. +// async_shutdown(m_sock, shared_from_this()); + m_sock.close(ec); + + std::string url = resolve_redirect_location(m_url, location); + get(url, m_completion_timeout, m_priority, &m_proxy, m_redirects - 1 + , m_user_agent, m_bind_addr +#if TORRENT_USE_I2P + , m_i2p_conn +#endif + ); + return; + } + + m_redirects = 0; + } + + if (!m_bottled && m_parser.header_finished()) + { + if (m_read_pos > m_parser.body_start()) + callback(e, &m_recvbuffer[0] + m_parser.body_start() + , m_read_pos - m_parser.body_start()); + m_read_pos = 0; + m_last_receive = time_now_hires(); + } + else if (m_bottled && m_parser.finished()) + { + error_code ec; + m_timer.cancel(ec); + callback(e, &m_recvbuffer[0] + m_parser.body_start(), m_parser.get_body().left()); + } + } + else + { + TORRENT_ASSERT(!m_bottled); + callback(e, &m_recvbuffer[0], m_read_pos); + m_read_pos = 0; + m_last_receive = time_now_hires(); + } + + // if we've hit the limit, double the buffer size + if (int(m_recvbuffer.size()) == m_read_pos) + m_recvbuffer.resize((std::min)(m_read_pos * 2, m_max_bottled_buffer_size)); + + if (m_read_pos == m_max_bottled_buffer_size) + { + // if we've reached the size limit, terminate the connection and + // report the error + callback(error_code(boost::system::errc::file_too_large, generic_category())); + close(); + return; + } + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + on_assign_bandwidth(error_code()); + } + return; + } + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , me, _1, _2)); +} + +void http_connection::on_assign_bandwidth(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("http_connection::on_assign_bandwidth"); +#endif + if ((e == asio::error::operation_aborted + && m_limiter_timer_active) + || !m_sock.is_open()) + { + callback(asio::error::eof); + return; + } + m_limiter_timer_active = false; + if (e) return; + + if (m_download_quota > 0) return; + + m_download_quota = m_rate_limit / 4; + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (amount_to_read > m_download_quota) + amount_to_read = m_download_quota; + + if (!m_sock.is_open()) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_read"); +#endif + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , boost::bind(&http_connection::on_read + , shared_from_this(), _1, _2)); + + error_code ec; + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250), ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); +} + +void http_connection::rate_limit(int limit) +{ + if (!m_sock.is_open()) return; + + if (!m_limiter_timer_active) + { + error_code ec; + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250), ec); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("http_connection::on_assign_bandwidth"); +#endif + m_limiter_timer.async_wait(boost::bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); + } + m_rate_limit = limit; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_parser.cpp b/apps/Launcher/ext/libtorrent/src/http_parser.cpp new file mode 100644 index 0000000000..84b7af4bf5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_parser.cpp @@ -0,0 +1,542 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" // for parse_url_components + +using namespace libtorrent; + +namespace libtorrent +{ + + bool is_ok_status(int http_status) + { + return http_status == 206 // partial content + || http_status == 200 // OK + || (http_status >= 300 // redirect + && http_status < 400); + } + + bool is_redirect(int http_status) + { + return http_status >= 300 + && http_status < 400; + } + + std::string resolve_redirect_location(std::string referrer + , std::string location) + { + if (location.empty()) return referrer; + + error_code ec; + using boost::tuples::ignore; + boost::tie(ignore, ignore, ignore, ignore, ignore) + = parse_url_components(location, ec); + + // if location is a full URL, just return it + if (!ec) return location; + + // otherwise it's likely to be just the path, or a relative path + std::string url = referrer; + + if (location[0] == '/') + { + // it's an absolute path. replace the path component of + // referrer with location + + // 8 is to skip the ur;l scheme://. We want the first slash + // that's part of the path. + std::size_t i = url.find_first_of('/', 8); + if (i == std::string::npos) + return location; + url.resize(i); + url += location; + } + else + { + // some web servers send out relative paths + // in the location header. + // remove the leaf filename + std::size_t i = url.find_last_of('/'); + if (i == std::string::npos) + return location; + + url.resize(i); + + if ((url.empty() || url[url.size()-1] != '/') + && (location.empty() || location[0] != '/')) + url += '/'; + url += location; + } + return url; + } + + http_parser::~http_parser() {} + + http_parser::http_parser(int flags) + : m_recv_pos(0) + , m_status_code(-1) + , m_content_length(-1) + , m_range_start(-1) + , m_range_end(-1) + , m_state(read_status) + , m_recv_buffer(0, 0) + , m_body_start_pos(0) + , m_connection_close(false) + , m_chunked_encoding(false) + , m_finished(false) + , m_cur_chunk_end(-1) + , m_chunk_header_size(0) + , m_partial_chunk_header(0) + , m_flags(flags) + {} + + boost::tuple http_parser::incoming( + buffer::const_interval recv_buffer, bool& error) + { + TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left()); + boost::tuple ret(0, 0); + int start_pos = m_recv_buffer.left(); + + // early exit if there's nothing new in the receive buffer + if (start_pos == recv_buffer.left()) return ret; + m_recv_buffer = recv_buffer; + + if (m_state == error_state) + { + error = true; + return ret; + } + + char const* pos = recv_buffer.begin + m_recv_pos; + +restart_response: + + if (m_state == read_status) + { + TORRENT_ASSERT(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + // if we don't have a full line yet, wait. + if (newline == recv_buffer.end) + { + boost::get<1>(ret) += m_recv_buffer.left() - start_pos; + return ret; + } + + if (newline == pos) + { + m_state = error_state; + error = true; + return ret; + } + + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + + char const* line = pos; + ++newline; + int incoming = int(newline - pos); + m_recv_pos += incoming; + boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); + pos = newline; + + m_protocol = read_until(line, ' ', line_end); + if (m_protocol.substr(0, 5) == "HTTP/") + { + m_status_code = atoi(read_until(line, ' ', line_end).c_str()); + m_server_message = read_until(line, '\r', line_end); + + // HTTP 1.0 always closes the connection after + // each request + if (m_protocol == "HTTP/1.0") m_connection_close = true; + } + else + { + m_method = m_protocol; + std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower); + // the content length is assumed to be 0 for requests + m_content_length = 0; + m_protocol.clear(); + m_path = read_until(line, ' ', line_end); + m_protocol = read_until(line, ' ', line_end); + m_status_code = 0; + } + m_state = read_header; + start_pos = pos - recv_buffer.begin; + } + + if (m_state == read_header) + { + TORRENT_ASSERT(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + std::string line; + + while (newline != recv_buffer.end && m_state == read_header) + { + // if the LF character is preceeded by a CR + // charachter, don't copy it into the line string. + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + line.assign(pos, line_end); + ++newline; + m_recv_pos += newline - pos; + pos = newline; + + std::string::size_type separator = line.find(':'); + if (separator == std::string::npos) + { + if (m_status_code == 100) + { + // for 100 Continue, we need to read another response header + // before reading the body + m_state = read_status; + goto restart_response; + } + // this means we got a blank line, + // the header is finished and the body + // starts. + m_state = read_body; + // if this is a request (not a response) + // we're done once we reach the end of the headers +// if (!m_method.empty()) m_finished = true; + // the HTTP header should always be < 2 GB + TORRENT_ASSERT(m_recv_pos < INT_MAX); + m_body_start_pos = int(m_recv_pos); + break; + } + + std::string name = line.substr(0, separator); + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + ++separator; + // skip whitespace + while (separator < line.size() + && (line[separator] == ' ' || line[separator] == '\t')) + ++separator; + std::string value = line.substr(separator, std::string::npos); + m_header.insert(std::make_pair(name, value)); + + if (name == "content-length") + { + m_content_length = strtoll(value.c_str(), 0, 10); + } + else if (name == "connection") + { + m_connection_close = string_begins_no_case("close", value.c_str()); + } + else if (name == "content-range") + { + bool success = true; + char const* ptr = value.c_str(); + + // apparently some web servers do not send the "bytes" + // in their content-range. Don't treat it as an error + // if we can't find it, just assume the byte counters + // start immediately + if (string_begins_no_case("bytes ", ptr)) ptr += 6; + char* end; + m_range_start = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + else if (*end != '-') success = false; + else + { + ptr = end + 1; + m_range_end = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + } + + if (!success || m_range_end < m_range_start) + { + m_state = error_state; + error = true; + return ret; + } + // the http range is inclusive + m_content_length = m_range_end - m_range_start + 1; + } + else if (name == "transfer-encoding") + { + m_chunked_encoding = string_begins_no_case("chunked", value.c_str()); + } + + TORRENT_ASSERT(m_recv_pos <= recv_buffer.left()); + newline = std::find(pos, recv_buffer.end, '\n'); + } + boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); + } + + if (m_state == read_body) + { + int incoming = recv_buffer.end - pos; + + if (m_chunked_encoding && (m_flags & dont_parse_chunks) == 0) + { + if (m_cur_chunk_end == -1) + m_cur_chunk_end = m_body_start_pos; + + while (m_cur_chunk_end <= m_recv_pos + incoming && !m_finished && incoming > 0) + { + size_type payload = m_cur_chunk_end - m_recv_pos; + if (payload > 0) + { + TORRENT_ASSERT(payload < INT_MAX); + m_recv_pos += payload; + boost::get<0>(ret) += int(payload); + incoming -= int(payload); + } + buffer::const_interval buf(recv_buffer.begin + m_cur_chunk_end, recv_buffer.end); + size_type chunk_size; + int header_size; + if (parse_chunk_header(buf, &chunk_size, &header_size)) + { + if (chunk_size > 0) + { + std::pair chunk_range(m_cur_chunk_end + header_size + , m_cur_chunk_end + header_size + chunk_size); + m_chunked_ranges.push_back(chunk_range); + } + m_cur_chunk_end += header_size + chunk_size; + if (chunk_size == 0) + { + m_finished = true; + TORRENT_ASSERT(m_content_length < 0 || m_recv_pos - m_body_start_pos + - m_chunk_header_size == m_content_length); + } + header_size -= m_partial_chunk_header; + m_partial_chunk_header = 0; +// fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" +// " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" +// " content-length = %d\n" +// , buf.left(), int(chunk_size), header_size, 1, incoming, int(m_recv_pos) +// , m_cur_chunk_end, int(m_content_length)); + } + else + { + m_partial_chunk_header += incoming; + header_size = incoming; + +// fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" +// " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" +// " content-length = %d\n" +// , buf.left(), int(chunk_size), header_size, 0, incoming, int(m_recv_pos) +// , m_cur_chunk_end, int(m_content_length)); + } + m_chunk_header_size += header_size; + m_recv_pos += header_size; + boost::get<1>(ret) += header_size; + incoming -= header_size; + } + if (incoming > 0) + { + m_recv_pos += incoming; + boost::get<0>(ret) += incoming; +// incoming = 0; + } + } + else + { + size_type payload_received = m_recv_pos - m_body_start_pos + incoming; + if (payload_received > m_content_length + && m_content_length >= 0) + { + TORRENT_ASSERT(m_content_length - m_recv_pos + m_body_start_pos < INT_MAX); + incoming = int(m_content_length - m_recv_pos + m_body_start_pos); + } + + TORRENT_ASSERT(incoming >= 0); + m_recv_pos += incoming; + boost::get<0>(ret) += incoming; + } + + if (m_content_length >= 0 + && !m_chunked_encoding + && m_recv_pos - m_body_start_pos >= m_content_length) + { + m_finished = true; + } + } + return ret; + } + + bool http_parser::parse_chunk_header(buffer::const_interval buf + , size_type* chunk_size, int* header_size) + { + char const* pos = buf.begin; + + // ignore one optional new-line. This is since each chunk + // is terminated by a newline. we're likely to see one + // before the actual header. + + if (pos < buf.end && pos[0] == '\r') ++pos; + if (pos < buf.end && pos[0] == '\n') ++pos; + if (pos == buf.end) return false; + + char const* newline = std::find(pos, buf.end, '\n'); + if (newline == buf.end) return false; + ++newline; + + // the chunk header is a single line, a hex length of the + // chunk followed by an optional semi-colon with a comment + // in case the length is 0, the stream is terminated and + // there are extra tail headers, which is terminated by an + // empty line + + // first, read the chunk length + *chunk_size = strtoll(pos, 0, 16); + if (*chunk_size != 0) + { + *header_size = newline - buf.begin; + // the newline alone is two bytes + TORRENT_ASSERT(newline - buf.begin > 2); + return true; + } + + // this is the terminator of the stream. Also read headers + std::map tail_headers; + pos = newline; + newline = std::find(pos, buf.end, '\n'); + + std::string line; + while (newline != buf.end) + { + // if the LF character is preceeded by a CR + // charachter, don't copy it into the line string. + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + line.assign(pos, line_end); + ++newline; + pos = newline; + + std::string::size_type separator = line.find(':'); + if (separator == std::string::npos) + { + // this means we got a blank line, + // the header is finished and the body + // starts. + *header_size = newline - buf.begin; + + // the newline alone is two bytes + TORRENT_ASSERT(newline - buf.begin > 2); + + // we were successfull in parsing the headers. + // add them to the headers in the parser + for (std::map::const_iterator i = tail_headers.begin(); + i != tail_headers.end(); ++i) + m_header.insert(std::make_pair(i->first, i->second)); + + return true; + } + + std::string name = line.substr(0, separator); + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + ++separator; + // skip whitespace + while (separator < line.size() + && (line[separator] == ' ' || line[separator] == '\t')) + ++separator; + std::string value = line.substr(separator, std::string::npos); + tail_headers.insert(std::make_pair(name, value)); +// fprintf(stderr, "tail_header: %s: %s\n", name.c_str(), value.c_str()); + + newline = std::find(pos, buf.end, '\n'); + } + return false; + } + + buffer::const_interval http_parser::get_body() const + { + TORRENT_ASSERT(m_state == read_body); + size_type last_byte = m_chunked_encoding && !m_chunked_ranges.empty() + ? (std::min)(m_chunked_ranges.back().second, m_recv_pos) + : m_content_length < 0 + ? m_recv_pos : (std::min)(m_body_start_pos + m_content_length, m_recv_pos); + + TORRENT_ASSERT(last_byte >= m_body_start_pos); + return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos + , m_recv_buffer.begin + last_byte); + } + + void http_parser::reset() + { + m_method.clear(); + m_recv_pos = 0; + m_body_start_pos = 0; + m_status_code = -1; + m_content_length = -1; + m_range_start = -1; + m_range_end = -1; + m_finished = false; + m_state = read_status; + m_recv_buffer.begin = 0; + m_recv_buffer.end = 0; + m_header.clear(); + m_chunked_encoding = false; + m_chunked_ranges.clear(); + m_cur_chunk_end = -1; + m_chunk_header_size = 0; + m_partial_chunk_header = 0; + } + + int http_parser::collapse_chunk_headers(char* buffer, int size) const + { + if (!chunked_encoding()) return size; + + // go through all chunks and compact them + // since we're bottled, and the buffer is our after all + // it's OK to mutate it + char* write_ptr = (char*)buffer; + // the offsets in the array are from the start of the + // buffer, not start of the body, so subtract the size + // of the HTTP header from them + int offset = body_start(); + std::vector > const& c = chunks(); + for (std::vector >::const_iterator i = c.begin() + , end(c.end()); i != end; ++i) + { + TORRENT_ASSERT(i->second - i->first < INT_MAX); + TORRENT_ASSERT(i->second - offset <= size); + int len = int(i->second - i->first); + if (i->first - offset + len > size) len = size - int(i->first) + offset; + memmove(write_ptr, buffer + i->first - offset, len); + write_ptr += len; + } + size = write_ptr - buffer; + return size; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp new file mode 100644 index 0000000000..a39fbedd7d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_seed_connection.cpp @@ -0,0 +1,461 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/http_seed_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + http_seed_connection::http_seed_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : web_connection_base(ses, t, s, remote, web) + , m_url(web.url) + , m_response_left(0) + , m_chunk_pos(0) + , m_partial_chunk_header(0) + { + INVARIANT_CHECK; + + if (!ses.settings().report_web_seed_downloads) + ignore_stats(true); + + shared_ptr tor = t.lock(); + TORRENT_ASSERT(tor); + int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size(); + + // multiply with the blocks per piece since that many requests are + // merged into one http request + max_out_request_queue(ses.settings().urlseed_pipeline_size + * blocks_per_piece); + + prefer_whole_pieces(1); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** http_seed_connection"); +#endif + } + + void http_seed_connection::disconnect(error_code const& ec, int error) + { + boost::shared_ptr t = associated_torrent().lock(); + peer_connection::disconnect(ec, error); + if (t) t->disconnect_web_seed(this); + } + + boost::optional + http_seed_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + piece_block_progress ret; + + peer_request const& pr = m_requests.front(); + ret.piece_index = pr.piece; + if (!m_parser.header_finished()) + { + ret.bytes_downloaded = 0; + } + else + { + int receive_buffer_size = receive_buffer().left() - m_parser.body_start(); + // TODO: 1 in chunked encoding mode, this assert won't hold. + // the chunk headers should be subtracted from the receive_buffer_size + TORRENT_ASSERT_VAL(receive_buffer_size <= t->block_size(), receive_buffer_size); + ret.bytes_downloaded = t->block_size() - receive_buffer_size; + } + // this is used to make sure that the block_index stays within + // bounds. If the entire piece is downloaded, the block_index + // would otherwise point to one past the end + int correction = ret.bytes_downloaded ? -1 : 0; + ret.block_index = (pr.start + ret.bytes_downloaded + correction) / t->block_size(); + ret.full_block_bytes = t->block_size(); + const int last_piece = t->torrent_file().num_pieces() - 1; + if (ret.piece_index == last_piece && ret.block_index + == t->torrent_file().piece_size(last_piece) / t->block_size()) + ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); + return ret; + } + + void http_seed_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + // http_seeds don't support requesting more than one piece + // at a time + TORRENT_ASSERT(r.length <= t->torrent_file().piece_size(r.piece)); + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + const int piece_size = t->torrent_file().piece_length(); + peer_request pr; + while (size > 0) + { + int request_offset = r.start + r.length - size; + pr.start = request_offset % piece_size; + pr.length = (std::min)(block_size, size); + pr.piece = r.piece + request_offset / piece_size; + m_requests.push_back(pr); + size -= pr.length; + } + + proxy_settings const& ps = m_ses.proxy(); + bool using_proxy = (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) && !m_ssl; + + request += "GET "; + request += using_proxy ? m_url : m_path; + request += "?info_hash="; + request += escape_string((char const*)&t->torrent_file().info_hash()[0], 20); + request += "&piece="; + request += to_string(r.piece).elems; + + // if we're requesting less than an entire piece we need to + // add ranges + if (r.start > 0 || r.length != t->torrent_file().piece_size(r.piece)) + { + request += "&ranges="; + request += to_string(r.start).elems; + request += "-"; + // ranges are inclusive, just like HTTP + request += to_string(r.start + r.length - 1).elems; + } + + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\n\r\n"; + m_first_request = false; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> %s", request.c_str()); +#endif + + send_buffer(request.c_str(), request.size(), message_type_request); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void http_seed_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** http_seed_connection error: %s", error.message().c_str()); +#endif + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + for (;;) + { + buffer::const_interval recv_buffer = receive_buffer(); + + if (bytes_transferred == 0) break; + TORRENT_ASSERT(recv_buffer.left() > 0); + + TORRENT_ASSERT(!m_requests.empty()); + if (m_requests.empty()) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_error, 2); + return; + } + + peer_request front_request = m_requests.front(); + + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + bool parse_error = false; + int protocol = 0; + int payload = 0; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, parse_error); + m_statistics.received_bytes(0, protocol); + bytes_transferred -= protocol; +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + if (payload > front_request.length) payload = front_request.length; +#endif + + if (parse_error) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_parse_error, 2); + return; + } + + TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + TORRENT_ASSERT(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + + // if the status code is not one of the accepted ones, abort + if (!is_ok_status(m_parser.status_code())) + { + int retry_time = atoi(m_parser.header("retry-after").c_str()); + if (retry_time <= 0) retry_time = 5 * 60; + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + + std::string error_msg = to_string(m_parser.status_code()).elems + + (" " + m_parser.message()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url() + , error_msg)); + } + m_statistics.received_bytes(0, bytes_transferred); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); + return; + } + if (!m_parser.header_finished()) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + } + + // we just completed reading the header + if (!header_finished) + { + if (is_redirect(m_parser.status_code())) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + m_statistics.received_bytes(0, bytes_transferred); + + if (location.empty()) + { + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::missing_location, 2); + return; + } + + // add the redirected url and remove the current one + t->add_web_seed(location, web_seed_entry::http_seed); + t->remove_web_seed(this); + disconnect(errors::redirecting, 2); + return; + } + + std::string const& server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + + m_response_left = atol(m_parser.header("content-length").c_str()); + if (m_response_left == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::no_content_length, 2); + return; + } + if (m_response_left != front_request.length) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + disconnect(errors::invalid_range, 2); + return; + } + m_body_start = m_parser.body_start(); + } + + recv_buffer.begin += m_body_start; + + // ========================= + // === CHUNKED ENCODING === + // ========================= + while (m_parser.chunked_encoding() + && m_chunk_pos >= 0 + && m_chunk_pos < recv_buffer.left()) + { + int header_size = 0; + size_type chunk_size = 0; + buffer::const_interval chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.begin[0] == '\r' || is_hex(chunk_start.begin, 1)); + bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); + if (!ret) + { + TORRENT_ASSERT(bytes_transferred >= size_t(chunk_start.left() - m_partial_chunk_header)); + bytes_transferred -= chunk_start.left() - m_partial_chunk_header; + m_statistics.received_bytes(0, chunk_start.left() - m_partial_chunk_header); + m_partial_chunk_header = chunk_start.left(); + if (bytes_transferred == 0) return; + break; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** parsed chunk: %d header_size: %d", chunk_size, header_size); +#endif + TORRENT_ASSERT(bytes_transferred >= size_t(header_size - m_partial_chunk_header)); + bytes_transferred -= header_size - m_partial_chunk_header; + + m_statistics.received_bytes(0, header_size - m_partial_chunk_header); + m_partial_chunk_header = 0; + TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); + // cut out the chunk header from the receive buffer + TORRENT_ASSERT(m_chunk_pos + m_body_start < INT_MAX); + cut_receive_buffer(header_size, t->block_size() + 1024, int(m_chunk_pos + m_body_start)); + recv_buffer = receive_buffer(); + recv_buffer.begin += m_body_start; + m_chunk_pos += chunk_size; + if (chunk_size == 0) + { + TORRENT_ASSERT(receive_buffer().left() < m_chunk_pos + m_body_start + 1 + || receive_buffer()[int(m_chunk_pos + m_body_start)] == 'H' + || (m_parser.chunked_encoding() && receive_buffer()[int(m_chunk_pos + m_body_start)] == '\r')); + m_chunk_pos = -1; + } + } + } + + int payload = bytes_transferred; + if (payload > m_response_left) payload = int(m_response_left); + if (payload > front_request.length) payload = front_request.length; + m_statistics.received_bytes(payload, 0); + incoming_piece_fragment(payload); + m_response_left -= payload; + + if (m_parser.status_code() == 503) + { + if (!m_parser.finished()) return; + + int retry_time = atol(std::string(recv_buffer.begin, recv_buffer.end).c_str()); + if (retry_time <= 0) retry_time = 60; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** retrying in %d seconds", retry_time); +#endif + + m_statistics.received_bytes(0, bytes_transferred); + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); + return; + } + + + // we only received the header, no data + if (recv_buffer.left() == 0) break; + + if (recv_buffer.left() < front_request.length) break; + + // if the response is chunked, we need to receive the last + // terminating chunk and the tail headers before we can proceed + if (m_parser.chunked_encoding() && m_chunk_pos >= 0) break; + + m_requests.pop_front(); + incoming_piece(front_request, recv_buffer.begin); + if (associated_torrent().expired()) return; + + int size_to_cut = m_body_start + front_request.length; + TORRENT_ASSERT(receive_buffer().left() < size_to_cut + 1 + || receive_buffer()[size_to_cut] == 'H' + || (m_parser.chunked_encoding() && receive_buffer()[size_to_cut] == '\r')); + + cut_receive_buffer(size_to_cut, t->block_size() + 1024); + if (m_response_left == 0) m_chunk_pos = 0; + else m_chunk_pos -= front_request.length; + bytes_transferred -= payload; + m_body_start = 0; + if (m_response_left > 0) continue; + TORRENT_ASSERT(m_response_left == 0); + m_parser.reset(); + } + } + + void http_seed_connection::get_specific_peer_info(peer_info& p) const + { + web_connection_base::get_specific_peer_info(p); + p.flags |= peer_info::local_connection; + p.connection_type = peer_info::http_seed; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_stream.cpp b/apps/Launcher/ext/libtorrent/src/http_stream.cpp new file mode 100644 index 0000000000..0a84d84e19 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_stream.cpp @@ -0,0 +1,175 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/http_stream.hpp" +#include "libtorrent/escape_string.hpp" // for base64encode +#include "libtorrent/socket_io.hpp" + +namespace libtorrent +{ + + void http_stream::name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &http_stream::connected, this, _1, h)); + } + + void http_stream::connected(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + if (m_no_connect) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + // send CONNECT + std::back_insert_iterator > p(m_buffer); + std::string endpoint; + if (!m_hostname.empty()) + { + endpoint = m_hostname + ':' + to_string(m_remote_endpoint.port()).elems; + } + else + { + endpoint = print_endpoint(m_remote_endpoint); + } + write_string("CONNECT " + endpoint + " HTTP/1.0\r\n", p); + if (!m_user.empty()) + { + write_string("Proxy-Authorization: Basic " + base64encode( + m_user + ":" + m_password) + "\r\n", p); + } + write_string("\r\n", p); + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake1, this, _1, h)); + } + + void http_stream::handshake1(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + // read one byte from the socket + m_buffer.resize(1); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + + void http_stream::handshake2(error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + int read_pos = m_buffer.size(); + // look for \n\n and \r\n\r\n + // both of which means end of http response header + bool found_end = false; + if (m_buffer[read_pos - 1] == '\n' && read_pos > 2) + { + if (m_buffer[read_pos - 2] == '\n') + { + found_end = true; + } + else if (read_pos > 4 + && m_buffer[read_pos - 2] == '\r' + && m_buffer[read_pos - 3] == '\n' + && m_buffer[read_pos - 4] == '\r') + { + found_end = true; + } + } + + if (found_end) + { + m_buffer.push_back(0); + char* status = std::strchr(&m_buffer[0], ' '); + if (status == 0) + { + (*h)(asio::error::operation_not_supported); + error_code ec; + close(ec); + return; + } + + status++; + int code = std::atoi(status); + if (code != 200) + { + (*h)(asio::error::operation_not_supported); + error_code ec; + close(ec); + return; + } + + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + // read another byte from the socket + m_buffer.resize(read_pos + 1); + async_read(m_sock, asio::buffer(&m_buffer[0] + read_pos, 1) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp b/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp new file mode 100644 index 0000000000..70eb3ea58b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/http_tracker_connection.cpp @@ -0,0 +1,544 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "libtorrent/gzip.hpp" +#include "libtorrent/socket_io.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_local + +using namespace libtorrent; + +namespace libtorrent +{ +#if TORRENT_USE_I2P + // defined in torrent_info.cpp + bool is_i2p_url(std::string const& url); +#endif + + http_tracker_connection::http_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl const& ses + , proxy_settings const& ps + , std::string const& auth +#if TORRENT_USE_I2P + , i2p_connection* i2p_conn +#endif + ) + : tracker_connection(man, req, ios, c) + , m_man(man) + , m_ses(ses) + , m_ps(ps) + , m_cc(cc) + , m_ios(ios) +#if TORRENT_USE_I2P + , m_i2p_conn(i2p_conn) +#endif + {} + + void http_tracker_connection::start() + { + // TODO: 0 support authentication (i.e. user name and password) in the URL + std::string url = tracker_req().url; + + if (tracker_req().kind == tracker_request::scrape_request) + { + // find and replace "announce" with "scrape" + // in request + + std::size_t pos = url.find("announce"); + if (pos == std::string::npos) + { + tracker_connection::fail(error_code(errors::scrape_not_available)); + return; + } + url.replace(pos, 8, "scrape"); + } + +#if TORRENT_USE_I2P + bool i2p = is_i2p_url(url); +#else + static const bool i2p = false; +#endif + + session_settings const& settings = m_ses.settings(); + + // if request-string already contains + // some parameters, append an ampersand instead + // of a question mark + size_t arguments_start = url.find('?'); + if (arguments_start != std::string::npos) + url += "&"; + else + url += "?"; + + if (tracker_req().kind == tracker_request::announce_request) + { + const char* event_string[] = {"completed", "started", "stopped", "paused"}; + + char str[1024]; + const bool stats = tracker_req().send_stats; + snprintf(str, sizeof(str) + , "info_hash=%s" + "&peer_id=%s" + "&port=%d" + "&uploaded=%" PRId64 + "&downloaded=%" PRId64 + "&left=%" PRId64 + "&corrupt=%" PRId64 + "&key=%X" + "%s%s" // event + "&numwant=%d" + "&compact=1" + "&no_peer_id=1" + , escape_string((const char*)&tracker_req().info_hash[0], 20).c_str() + , escape_string((const char*)&tracker_req().pid[0], 20).c_str() + // the i2p tracker seems to verify that the port is not 0, + // even though it ignores it otherwise + , i2p ? 1 : tracker_req().listen_port + , stats ? tracker_req().uploaded : 0 + , stats ? tracker_req().downloaded : 0 + , stats ? tracker_req().left : 0 + , stats ? tracker_req().corrupt : 0 + , tracker_req().key + , (tracker_req().event != tracker_request::none) ? "&event=" : "" + , (tracker_req().event != tracker_request::none) ? event_string[tracker_req().event - 1] : "" + , tracker_req().num_want); + url += str; +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_ses.get_pe_settings().in_enc_policy != pe_settings::disabled) + url += "&supportcrypto=1"; +#endif + if (stats && m_ses.settings().report_redundant_bytes) + { + url += "&redundant="; + url += to_string(tracker_req().redundant).elems; + } + if (!tracker_req().trackerid.empty()) + { + std::string id = tracker_req().trackerid; + url += "&trackerid="; + url += escape_string(id.c_str(), id.length()); + } + +#if TORRENT_USE_I2P + if (i2p) + { + url += "&ip="; + url += escape_string(m_i2p_conn->local_endpoint().c_str() + , m_i2p_conn->local_endpoint().size()); + url += ".i2p"; + } + else +#endif + if (!m_ses.settings().anonymous_mode) + { + if (!settings.announce_ip.empty()) + { + url += "&ip=" + escape_string( + settings.announce_ip.c_str(), settings.announce_ip.size()); + } + else if (m_ses.settings().announce_double_nat + && is_local(m_ses.listen_address())) + { + // only use the global external listen address here + // if it turned out to be on a local network + // since otherwise the tracker should use our + // source IP to determine our origin + url += "&ip=" + print_address(m_ses.listen_address()); + } + } + } + + m_tracker_connection.reset(new http_connection(m_ios, m_cc + , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4) + , true, settings.max_http_recv_buffer_size + , boost::bind(&http_tracker_connection::on_connect, self(), _1) + , boost::bind(&http_tracker_connection::on_filter, self(), _1, _2) +#ifdef TORRENT_USE_OPENSSL + , tracker_req().ssl_ctx +#endif + )); + + int timeout = tracker_req().event==tracker_request::stopped + ?settings.stop_tracker_timeout + :settings.tracker_completion_timeout; + + m_tracker_connection->get(url, seconds(timeout) + , tracker_req().event == tracker_request::stopped ? 2 : 1 + , &m_ps, 5, settings.anonymous_mode ? "" : settings.user_agent + , bind_interface() +#if TORRENT_USE_I2P + , m_i2p_conn +#endif + ); + + // the url + 100 estimated header size + sent_bytes(url.size() + 100); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("==> TRACKER_REQUEST [ url: %s ]", url.c_str()); + } +#endif + } + + void http_tracker_connection::close() + { + if (m_tracker_connection) + { + m_tracker_connection->close(); + m_tracker_connection.reset(); + } + tracker_connection::close(); + } + + void http_tracker_connection::on_filter(http_connection& c, std::list& endpoints) + { + if (tracker_req().apply_ip_filter == false) return; + + // remove endpoints that are filtered by the IP filter + for (std::list::iterator i = endpoints.begin(); + i != endpoints.end();) + { + if (m_ses.m_ip_filter.access(i->address()) == ip_filter::blocked) + i = endpoints.erase(i); + else + ++i; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("*** TRACKER_FILTER"); + } +#endif + if (endpoints.empty()) + fail(error_code(errors::banned_by_ip_filter)); + } + + void http_tracker_connection::on_connect(http_connection& c) + { + error_code ec; + tcp::endpoint ep = c.socket().remote_endpoint(ec); + m_tracker_ip = ep.address(); + boost::shared_ptr cb = requester(); + if (cb) cb->m_tracker_address = ep; + } + + void http_tracker_connection::on_response(error_code const& ec + , http_parser const& parser, char const* data, int size) + { + // keep this alive + boost::intrusive_ptr me(this); + + if (ec && ec != asio::error::eof) + { + fail(ec); + return; + } + + if (!parser.header_finished()) + { + fail(asio::error::eof); + return; + } + + if (parser.status_code() != 200) + { + fail(error_code(parser.status_code(), get_http_category()) + , parser.status_code(), parser.message().c_str()); + return; + } + + if (ec && ec != asio::error::eof) + { + fail(ec, parser.status_code()); + return; + } + + received_bytes(size + parser.body_start()); + + // handle tracker response + lazy_entry e; + error_code ecode; + int res = lazy_bdecode(data, data + size, e, ecode); + + if (res == 0 && e.type() == lazy_entry::dict_t) + { + parse(parser.status_code(), e); + } + else + { + fail(ecode, parser.status_code()); + } + close(); + } + + bool http_tracker_connection::extract_peer_info(lazy_entry const& info, peer_entry& ret) + { + // extract peer id (if any) + if (info.type() != lazy_entry::dict_t) + { + fail(error_code(errors::invalid_peer_dict)); + return false; + } + lazy_entry const* i = info.dict_find_string("peer id"); + if (i != 0 && i->string_length() == 20) + { + std::copy(i->string_ptr(), i->string_ptr()+20, ret.pid.begin()); + } + else + { + // if there's no peer_id, just initialize it to a bunch of zeroes + std::fill_n(ret.pid.begin(), 20, 0); + } + + // extract ip + i = info.dict_find_string("ip"); + if (i == 0) + { + fail(error_code(errors::invalid_tracker_response)); + return false; + } + ret.ip = i->string_value(); + + // extract port + i = info.dict_find_int("port"); + if (i == 0) + { + fail(error_code(errors::invalid_tracker_response)); + return false; + } + ret.port = (unsigned short)i->int_value(); + + return true; + } + + void http_tracker_connection::parse(int status_code, lazy_entry const& e) + { + boost::shared_ptr cb = requester(); + if (!cb) return; + + int interval = int(e.dict_find_int_value("interval", 0)); + int min_interval = int(e.dict_find_int_value("min interval", 30)); + + // if no interval is specified, default to 30 minutes + if (interval == 0) interval = 1800; + + std::string trackerid; + lazy_entry const* tracker_id = e.dict_find_string("tracker id"); + if (tracker_id) + trackerid = tracker_id->string_value(); + // parse the response + lazy_entry const* failure = e.dict_find_string("failure reason"); + if (failure) + { + fail(error_code(errors::tracker_failure), status_code + , failure->string_value().c_str(), interval, min_interval); + return; + } + + lazy_entry const* warning = e.dict_find_string("warning message"); + if (warning) + cb->tracker_warning(tracker_req(), warning->string_value()); + + std::vector peer_list; + + if (tracker_req().kind == tracker_request::scrape_request) + { + std::string ih = tracker_req().info_hash.to_string(); + + lazy_entry const* files = e.dict_find_dict("files"); + if (files == 0) + { + fail(error_code(errors::invalid_files_entry), -1, "" + , interval, min_interval); + return; + } + + lazy_entry const* scrape_data = files->dict_find_dict(ih); + if (scrape_data == 0) + { + fail(error_code(errors::invalid_hash_entry), -1, "" + , interval, min_interval); + return; + } + + int complete = int(scrape_data->dict_find_int_value("complete", -1)); + int incomplete = int(scrape_data->dict_find_int_value("incomplete", -1)); + int downloaded = int(scrape_data->dict_find_int_value("downloaded", -1)); + int downloaders = int(scrape_data->dict_find_int_value("downloaders", -1)); + cb->tracker_scrape_response(tracker_req(), complete + , incomplete, downloaded, downloaders); + return; + } + + lazy_entry const* peers_ent = e.dict_find("peers"); + if (peers_ent && peers_ent->type() == lazy_entry::string_t) + { + char const* peers = peers_ent->string_ptr(); + int len = peers_ent->string_length(); + for (int i = 0; i < len; i += 6) + { + if (len - i < 6) break; + + peer_entry p; + p.pid.clear(); + error_code ec; + p.ip = detail::read_v4_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); + if (ec) continue; + peer_list.push_back(p); + } + } + else if (peers_ent && peers_ent->type() == lazy_entry::list_t) + { + int len = peers_ent->list_size(); + for (int i = 0; i < len; ++i) + { + peer_entry p; + if (!extract_peer_info(*peers_ent->list_at(i), p)) return; + peer_list.push_back(p); + } + } + else + { + peers_ent = 0; + } + +#if TORRENT_USE_IPV6 + lazy_entry const* ipv6_peers = e.dict_find_string("peers6"); + if (ipv6_peers) + { + char const* peers = ipv6_peers->string_ptr(); + int len = ipv6_peers->string_length(); + for (int i = 0; i < len; i += 18) + { + if (len - i < 18) break; + + peer_entry p; + p.pid.clear(); + error_code ec; + p.ip = detail::read_v6_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); + if (ec) continue; + peer_list.push_back(p); + } + } + else + { + ipv6_peers = 0; + } +#else + lazy_entry const* ipv6_peers = 0; +#endif + + // if we didn't receive any peers. We don't care if we're stopping anyway + if (peers_ent == 0 && ipv6_peers == 0 + && tracker_req().event != tracker_request::stopped) + { + fail(error_code(errors::invalid_peers_entry), -1, "" + , interval, min_interval); + return; + } + + + // look for optional scrape info + address external_ip; + + lazy_entry const* ip_ent = e.dict_find_string("external ip"); + if (ip_ent) + { + char const* p = ip_ent->string_ptr(); + if (ip_ent->string_length() == int(address_v4::bytes_type().size())) + external_ip = detail::read_v4_address(p); +#if TORRENT_USE_IPV6 + else if (ip_ent->string_length() == int(address_v6::bytes_type().size())) + external_ip = detail::read_v6_address(p); +#endif + } + + int complete = int(e.dict_find_int_value("complete", -1)); + int incomplete = int(e.dict_find_int_value("incomplete", -1)); + int downloaded = int(e.dict_find_int_value("downloaded", -1)); + + std::list
ip_list; + if (m_tracker_connection) + { + error_code ec; + ip_list.push_back(m_tracker_connection->socket().remote_endpoint(ec).address()); + std::list const& epts = m_tracker_connection->endpoints(); + for (std::list::const_iterator i = epts.begin() + , end(epts.end()); i != end; ++i) + { + ip_list.push_back(i->address()); + } + } + + cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, peer_list + , interval, min_interval, complete, incomplete, downloaded, external_ip, trackerid); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp b/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp new file mode 100644 index 0000000000..35c64a63f3 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/i2p_stream.cpp @@ -0,0 +1,524 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/i2p_stream.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/string_util.hpp" + +#if TORRENT_USE_I2P + +#include + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +namespace libtorrent +{ + + struct i2p_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "i2p error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* messages[] = + { + "no error", + "parse failed", + "cannot reach peer", + "i2p error", + "invalid key", + "invalid id", + "timeout", + "key not found", + "duplicated id" + }; + + if (ev < 0 || ev >= i2p_error::num_errors) return "unknown error"; + return messages[ev]; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + + boost::system::error_category& get_i2p_category() + { + static i2p_error_category i2p_category; + return i2p_category; + } + + namespace i2p_error + { + boost::system::error_code make_error_code(i2p_error_code e) + { + return error_code(e, get_i2p_category()); + } + } + + i2p_connection::i2p_connection(io_service& ios) + : m_state(sam_idle) + , m_io_service(ios) + {} + + i2p_connection::~i2p_connection() + {} + + void i2p_connection::close(error_code& e) + { + if (m_sam_socket) m_sam_socket->close(e); + } + + void i2p_connection::open(proxy_settings const& s, i2p_stream::handler_type const& handler) + { + // we already seem to have a session to this SAM router + if (m_sam_router.hostname == s.hostname + && m_sam_router.port == s.port + && m_sam_socket + && (is_open() || m_state == sam_connecting)) return; + + m_sam_router = s; + m_sam_router.type = proxy_settings::i2p_proxy; + + if (m_sam_router.hostname.empty()) return; + + m_state = sam_connecting; + + char tmp[20]; + std::generate(tmp, tmp + sizeof(tmp), &std::rand); + m_session_id.resize(sizeof(tmp)*2); + to_hex(tmp, 20, &m_session_id[0]); + + m_sam_socket.reset(new i2p_stream(m_io_service)); + m_sam_socket->set_proxy(m_sam_router.hostname, m_sam_router.port); + m_sam_socket->set_command(i2p_stream::cmd_create_session); + m_sam_socket->set_session_id(m_session_id.c_str()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::on_sam_connect"); +#endif + m_sam_socket->async_connect(tcp::endpoint() + , boost::bind(&i2p_connection::on_sam_connect, this, _1, handler, m_sam_socket)); + } + + void i2p_connection::on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h, boost::shared_ptr) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::on_sam_connect"); +#endif + m_state = sam_idle; + + if (ec) + { + h(ec); + return; + } + + do_name_lookup("ME", boost::bind(&i2p_connection::set_local_endpoint, this, _1, _2, h)); + } + + void i2p_connection::set_local_endpoint(error_code const& ec, char const* dest + , i2p_stream::handler_type const& h) + { + if (!ec && dest != 0) + m_i2p_local_endpoint = dest; + else + m_i2p_local_endpoint.clear(); + + h(ec); + } + + void i2p_connection::async_name_lookup(char const* name + , i2p_connection::name_lookup_handler handler) + { + if (m_state == sam_idle && m_name_lookup.empty() && is_open()) + do_name_lookup(name, handler); + else + m_name_lookup.push_back(std::make_pair(std::string(name), handler)); + } + + void i2p_connection::do_name_lookup(std::string const& name + , name_lookup_handler const& handler) + { + TORRENT_ASSERT(m_state == sam_idle); + m_state = sam_name_lookup; + m_sam_socket->set_name_lookup(name.c_str()); + boost::shared_ptr h(new i2p_stream::handler_type( + boost::bind(&i2p_connection::on_name_lookup, this, _1, handler, m_sam_socket))); + m_sam_socket->send_name_lookup(h); + } + + void i2p_connection::on_name_lookup(error_code const& ec + , name_lookup_handler handler, boost::shared_ptr) + { + m_state = sam_idle; + + std::string name = m_sam_socket->name_lookup(); + if (!m_name_lookup.empty()) + { + std::pair& nl = m_name_lookup.front(); + do_name_lookup(nl.first, nl.second); + m_name_lookup.pop_front(); + } + + if (ec) + { + handler(ec, 0); + return; + } + + handler(ec, name.c_str()); + } + + i2p_stream::i2p_stream(io_service& io_service) + : proxy_base(io_service) + , m_id(0) + , m_command(cmd_create_session) + , m_state(0) + { +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; +#endif + } + + i2p_stream::~i2p_stream() + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_magic == 0x1337); + m_magic = 0; +#endif + } + +// TODO: move this to proxy_base and use it in all proxies + bool i2p_stream::handle_error(error_code const& e, boost::shared_ptr const& h) + { + TORRENT_ASSERT(m_magic == 0x1337); + if (!e) return false; +// fprintf(stderr, "i2p error \"%s\"\n", e.message().c_str()); + (*h)(e); + return true; + } + + void i2p_stream::do_connect(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::connected"); +#endif + m_sock.async_connect(i->endpoint(), boost::bind( + &i2p_stream::connected, this, _1, h)); + } + + void i2p_stream::connected(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::connected"); +#endif + if (handle_error(e, h)) return; + + // send hello command + m_state = read_hello_response; + static const char cmd[] = "HELLO VERSION MIN=3.0 MAX=3.0\n"; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, sizeof(cmd) - 1) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); +// fprintf(stderr, ">>> %s", cmd); + } + + void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::start_read_line"); +#endif + if (handle_error(e, h)) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + m_buffer.resize(1); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + } + + void i2p_stream::read_line(error_code const& e, boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); +#if defined TORRENT_ASIO_DEBUGGING + complete_async("i2p_stream::read_line"); +#endif + if (handle_error(e, h)) return; + + int read_pos = m_buffer.size(); + + // look for \n which means end of the response + if (m_buffer[read_pos - 1] != '\n') + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + // read another byte from the socket + m_buffer.resize(read_pos + 1); + async_read(m_sock, asio::buffer(&m_buffer[read_pos], 1) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + return; + } + m_buffer[read_pos - 1] = 0; + + if (m_command == cmd_incoming) + { + // this is the line containing the destination + // of the incoming connection in an accept call + m_dest = &m_buffer[0]; + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + error_code invalid_response(i2p_error::parse_failed + , get_i2p_category()); + + // null-terminate the string and parse it + m_buffer.push_back(0); + char* ptr = &m_buffer[0]; + char* next = ptr; + + char const* expect1 = 0; + char const* expect2 = 0; + + switch (m_state) + { + case read_hello_response: + expect1 = "HELLO"; + expect2 = "REPLY"; + break; + case read_connect_response: + case read_accept_response: + expect1 = "STREAM"; + expect2 = "STATUS"; + break; + case read_session_create_response: + expect1 = "SESSION"; + expect2 = "STATUS"; + break; + case read_name_lookup_response: + expect1 = "NAMING"; + expect2 = "REPLY"; + break; + } + +// fprintf(stderr, "<<< %s\n", &m_buffer[0]); + ptr = string_tokenize(next, ' ', &next); + if (ptr == 0 || expect1 == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } + ptr = string_tokenize(next, ' ', &next); + if (ptr == 0 || expect2 == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; } + + int result = 0; +// char const* message = 0; +// float version = 3.0f; + + for(;;) + { + char* name = string_tokenize(next, '=', &next); + if (name == 0) break; +// fprintf(stderr, "name=\"%s\"\n", name); + char* ptr = string_tokenize(next, ' ', &next); + if (ptr == 0) { handle_error(invalid_response, h); return; } +// fprintf(stderr, "value=\"%s\"\n", ptr); + + if (strcmp("RESULT", name) == 0) + { + if (strcmp("OK", ptr) == 0) + result = i2p_error::no_error; + else if (strcmp("CANT_REACH_PEER", ptr) == 0) + result = i2p_error::cant_reach_peer; + else if (strcmp("I2P_ERROR", ptr) == 0) + result = i2p_error::i2p_error; + else if (strcmp("INVALID_KEY", ptr) == 0) + result = i2p_error::invalid_key; + else if (strcmp("INVALID_ID", ptr) == 0) + result = i2p_error::invalid_id; + else if (strcmp("TIMEOUT", ptr) == 0) + result = i2p_error::timeout; + else if (strcmp("KEY_NOT_FOUND", ptr) == 0) + result = i2p_error::key_not_found; + else if (strcmp("DUPLICATED_ID", ptr) == 0) + result = i2p_error::duplicated_id; + else + result = i2p_error::num_errors; // unknown error + } + else if (strcmp("MESSAGE", name) == 0) + { +// message = ptr; + } + else if (strcmp("VERSION", name) == 0) + { +// version = float(atof(ptr)); + } + else if (strcmp("VALUE", name) == 0) + { + m_name_lookup = ptr; + } + else if (strcmp("DESTINATION", name) == 0) + { + m_dest = ptr; + } + } + + if (result != i2p_error::no_error) + { + error_code ec(result, get_i2p_category()); + handle_error(ec, h); + return; + } + + switch (m_state) + { + case read_hello_response: + switch (m_command) + { + case cmd_create_session: + send_session_create(h); + break; + case cmd_accept: + send_accept(h); + break; + case cmd_connect: + send_connect(h); + break; + default: + (*h)(e); + std::vector().swap(m_buffer); + } + break; + case read_connect_response: + case read_session_create_response: + case read_name_lookup_response: + (*h)(e); + std::vector().swap(m_buffer); + break; + case read_accept_response: + // the SAM bridge is waiting for an incoming + // connection. + // wait for one more line containing + // the destination of the remote peer + m_command = cmd_incoming; + m_buffer.resize(1); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::read_line"); +#endif + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&i2p_stream::read_line, this, _1, h)); + break; + } + + return; + } + + void i2p_stream::send_connect(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_connect_response; + char cmd[1024]; + int size = snprintf(cmd, sizeof(cmd), "STREAM CONNECT ID=%s DESTINATION=%s\n" + , m_id, m_dest.c_str()); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_accept(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_accept_response; + char cmd[400]; + int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_session_create(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_session_create_response; + char cmd[400]; + int size = snprintf(cmd, sizeof(cmd), "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT\n" + , m_id); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } + + void i2p_stream::send_name_lookup(boost::shared_ptr h) + { + TORRENT_ASSERT(m_magic == 0x1337); + m_state = read_name_lookup_response; + char cmd[1024]; + int size = snprintf(cmd, sizeof(cmd), "NAMING LOOKUP NAME=%s\n", m_name_lookup.c_str()); +// fprintf(stderr, ">>> %s", cmd); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("i2p_stream::start_read_line"); +#endif + async_write(m_sock, asio::buffer(cmd, size) + , boost::bind(&i2p_stream::start_read_line, this, _1, h)); + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/identify_client.cpp b/apps/Launcher/ext/libtorrent/src/identify_client.cpp new file mode 100644 index 0000000000..a5583dfb50 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/identify_client.cpp @@ -0,0 +1,428 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/identify_client.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/string_util.hpp" + +namespace +{ + + using namespace libtorrent; + + int decode_digit(char c) + { + if (is_digit(c)) return c - '0'; + return unsigned(c) - 'A' + 10; + } + + // takes a peer id and returns a valid boost::optional + // object if the peer id matched the azureus style encoding + // the returned fingerprint contains information about the + // client's id + boost::optional parse_az_style(const peer_id& id) + { + fingerprint ret("..", 0, 0, 0, 0); + + if (id[0] != '-' || !is_print(id[1]) || (id[2] < '0') + || (id[3] < '0') || (id[4] < '0') + || (id[5] < '0') || (id[6] < '0') + || id[7] != '-') + return boost::optional(); + + ret.name[0] = id[1]; + ret.name[1] = id[2]; + ret.major_version = decode_digit(id[3]); + ret.minor_version = decode_digit(id[4]); + ret.revision_version = decode_digit(id[5]); + ret.tag_version = decode_digit(id[6]); + + return boost::optional(ret); + } + + // checks if a peer id can possibly contain a shadow-style + // identification + boost::optional parse_shadow_style(const peer_id& id) + { + fingerprint ret("..", 0, 0, 0, 0); + + if (!is_alpha(id[0]) && !is_digit(id[0])) + return boost::optional(); + + if (std::equal(id.begin()+4, id.begin()+6, "--")) + { + if ((id[1] < '0') || (id[2] < '0') + || (id[3] < '0')) + return boost::optional(); + ret.major_version = decode_digit(id[1]); + ret.minor_version = decode_digit(id[2]); + ret.revision_version = decode_digit(id[3]); + } + else + { + if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127) + return boost::optional(); + ret.major_version = id[1]; + ret.minor_version = id[2]; + ret.revision_version = id[3]; + } + + ret.name[0] = id[0]; + ret.name[1] = 0; + + ret.tag_version = 0; + return boost::optional(ret); + } + + // checks if a peer id can possibly contain a mainline-style + // identification + boost::optional parse_mainline_style(const peer_id& id) + { + char ids[21]; + std::copy(id.begin(), id.end(), ids); + ids[20] = 0; + fingerprint ret("..", 0, 0, 0, 0); + ret.name[1] = 0; + ret.tag_version = 0; + if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version + , &ret.revision_version) != 4 + || !is_print(ret.name[0])) + return boost::optional(); + + return boost::optional(ret); + } + + struct map_entry + { + char const* id; + char const* name; + }; + + // only support BitTorrentSpecification + // must be ordered alphabetically + map_entry name_map[] = + { + {"A", "ABC"} + , {"AG", "Ares"} + , {"AR", "Arctic Torrent"} + , {"AT", "Artemis"} + , {"AV", "Avicora"} + , {"AX", "BitPump"} + , {"AZ", "Azureus"} + , {"A~", "Ares"} + , {"BB", "BitBuddy"} + , {"BC", "BitComet"} + , {"BE", "baretorrent"} + , {"BF", "Bitflu"} + , {"BG", "BTG"} + , {"BL", "BitBlinder"} + , {"BP", "BitTorrent Pro"} + , {"BR", "BitRocket"} + , {"BS", "BTSlave"} + , {"BT", "BitTorrent"} + , {"BU", "BigUp"} + , {"BW", "BitWombat"} + , {"BX", "BittorrentX"} + , {"CD", "Enhanced CTorrent"} + , {"CT", "CTorrent"} + , {"DE", "Deluge"} + , {"DP", "Propagate Data Client"} + , {"EB", "EBit"} + , {"ES", "electric sheep"} + , {"FC", "FileCroc"} + , {"FT", "FoxTorrent"} + , {"FX", "Freebox BitTorrent"} + , {"GS", "GSTorrent"} + , {"HK", "Hekate"} + , {"HL", "Halite"} + , {"HN", "Hydranode"} + , {"IL", "iLivid"} + , {"KG", "KGet"} + , {"KT", "KTorrent"} + , {"LC", "LeechCraft"} + , {"LH", "LH-ABC"} + , {"LK", "Linkage"} + , {"LP", "lphant"} + , {"LT", "libtorrent"} + , {"LW", "Limewire"} + , {"M", "Mainline"} + , {"ML", "MLDonkey"} + , {"MO", "Mono Torrent"} + , {"MP", "MooPolice"} + , {"MR", "Miro"} + , {"MT", "Moonlight Torrent"} + , {"NX", "Net Transport"} + , {"O", "Osprey Permaseed"} + , {"OS", "OneSwarm"} + , {"OT", "OmegaTorrent"} + , {"PD", "Pando"} + , {"Q", "BTQueue"} + , {"QD", "QQDownload"} + , {"QT", "Qt 4"} + , {"R", "Tribler"} + , {"RT", "Retriever"} + , {"RZ", "RezTorrent"} + , {"S", "Shadow"} + , {"SB", "Swiftbit"} + , {"SD", "Xunlei"} + , {"SK", "spark"} + , {"SN", "ShareNet"} + , {"SS", "SwarmScope"} + , {"ST", "SymTorrent"} + , {"SZ", "Shareaza"} + , {"S~", "Shareaza (beta)"} + , {"T", "BitTornado"} + , {"TB", "Torch"} + , {"TL", "Tribler"} + , {"TN", "Torrent.NET"} + , {"TR", "Transmission"} + , {"TS", "TorrentStorm"} + , {"TT", "TuoTu"} + , {"U", "UPnP"} + , {"UL", "uLeecher"} + , {"UM", "uTorrent Mac"} + , {"UT", "uTorrent"} + , {"VG", "Vagaa"} + , {"WT", "BitLet"} + , {"WY", "FireTorrent"} + , {"XF", "Xfplay"} + , {"XL", "Xunlei"} + , {"XS", "XSwifter"} + , {"XT", "XanTorrent"} + , {"XX", "Xtorrent"} + , {"ZT", "ZipTorrent"} + , {"lt", "rTorrent"} + , {"pX", "pHoeniX"} + , {"qB", "qBittorrent"} + , {"st", "SharkTorrent"} + }; + + struct generic_map_entry + { + int offset; + char const* id; + char const* name; + }; + // non-standard names + generic_map_entry generic_mappings[] = + { + {0, "Deadman Walking-", "Deadman"} + , {5, "Azureus", "Azureus 2.0.3.2"} + , {0, "DansClient", "XanTorrent"} + , {4, "btfans", "SimpleBT"} + , {0, "PRC.P---", "Bittorrent Plus! II"} + , {0, "P87.P---", "Bittorrent Plus!"} + , {0, "S587Plus", "Bittorrent Plus!"} + , {0, "martini", "Martini Man"} + , {0, "Plus---", "Bittorrent Plus"} + , {0, "turbobt", "TurboBT"} + , {0, "a00---0", "Swarmy"} + , {0, "a02---0", "Swarmy"} + , {0, "T00---0", "Teeweety"} + , {0, "BTDWV-", "Deadman Walking"} + , {2, "BS", "BitSpirit"} + , {0, "Pando-", "Pando"} + , {0, "LIME", "LimeWire"} + , {0, "btuga", "BTugaXP"} + , {0, "oernu", "BTugaXP"} + , {0, "Mbrst", "Burst!"} + , {0, "PEERAPP", "PeerApp"} + , {0, "Plus", "Plus!"} + , {0, "-Qt-", "Qt"} + , {0, "exbc", "BitComet"} + , {0, "DNA", "BitTorrent DNA"} + , {0, "-G3", "G3 Torrent"} + , {0, "-FG", "FlashGet"} + , {0, "-ML", "MLdonkey"} + , {0, "-MG", "Media Get"} + , {0, "XBT", "XBT"} + , {0, "OP", "Opera"} + , {2, "RS", "Rufus"} + , {0, "AZ2500BT", "BitTyrant"} + , {0, "btpd/", "BitTorrent Protocol Daemon"} + , {0, "TIX", "Tixati"} + , {0, "QVOD", "Qvod"} + }; + + bool compare_id(map_entry const& lhs, map_entry const& rhs) + { + return lhs.id[0] < rhs.id[0] + || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1])); + } + + std::string lookup(fingerprint const& f) + { + char identity[200]; + + const int size = sizeof(name_map)/sizeof(name_map[0]); + map_entry tmp = {f.name, ""}; + map_entry* i = + std::lower_bound(name_map, name_map + size + , tmp, &compare_id); + +#ifndef NDEBUG + for (int i = 1; i < size; ++i) + { + TORRENT_ASSERT(compare_id(name_map[i-1] + , name_map[i])); + } +#endif + + char temp[3]; + char const* name = 0; + if (i < name_map + size && std::equal(f.name, f.name + 2, i->id)) + { + name = i->name; + } + else + { + // if we don't have this client in the list + // just use the one or two letter code + memcpy(temp, f.name, 2); + temp[2] = 0; + name = temp; + } + + int num_chars = snprintf(identity, sizeof(identity), "%s %u.%u.%u", name + , f.major_version, f.minor_version, f.revision_version); + + if (f.tag_version != 0) + { + snprintf(identity + num_chars, sizeof(identity) - num_chars + , ".%u", f.tag_version); + } + + return identity; + } + + bool find_string(unsigned char const* id, char const* search) + { + return std::equal(search, search + std::strlen(search), id); + } +} + +namespace libtorrent +{ + + boost::optional client_fingerprint(peer_id const& p) + { + // look for azureus style id + boost::optional f; + f = parse_az_style(p); + if (f) return f; + + // look for shadow style id + f = parse_shadow_style(p); + if (f) return f; + + // look for mainline style id + f = parse_mainline_style(p); + if (f) return f; + return f; + } + + std::string identify_client(peer_id const& p) + { + peer_id::const_iterator PID = p.begin(); + boost::optional f; + + if (p.is_all_zeros()) return "Unknown"; + + // ---------------------- + // non standard encodings + // ---------------------- + + int num_generic_mappings = sizeof(generic_mappings) / sizeof(generic_mappings[0]); + + for (int i = 0; i < num_generic_mappings; ++i) + { + generic_map_entry const& e = generic_mappings[i]; + if (find_string(PID + e.offset, e.id)) return e.name; + } + + if (find_string(PID, "-BOW") && PID[7] == '-') + return "Bits on Wheels " + std::string((char const*)PID + 4, (char const*)PID + 7); + + + if (find_string(PID, "eX")) + { + std::string user((char const*)PID + 2, (char const*)PID + 14); + return std::string("eXeem ('") + user.c_str() + "')"; + } + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97")) + return "Experimental 3.2.1b2"; + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Experimental 3.1"; + + + // look for azureus style id + f = parse_az_style(p); + if (f) return lookup(*f); + + // look for shadow style id + f = parse_shadow_style(p); + if (f) return lookup(*f); + + // look for mainline style id + f = parse_mainline_style(p); + if (f) return lookup(*f); + + + if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Generic"; + + std::string unknown("Unknown ["); + for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i) + { + unknown += is_print(char(*i))?*i:'.'; + } + unknown += "]"; + return unknown; + } + +} diff --git a/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp b/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp new file mode 100644 index 0000000000..18619e34eb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/instantiate_connection.cpp @@ -0,0 +1,141 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include +#include + +namespace libtorrent +{ + TORRENT_EXPORT bool instantiate_connection(io_service& ios + , proxy_settings const& ps, socket_type& s + , void* ssl_context + , utp_socket_manager* sm + , bool peer_connection) + { + if (sm) + { + utp_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + str->set_impl(sm->new_utp_socket(str)); + } +#if TORRENT_USE_I2P + else if (ps.type == proxy_settings::i2p_proxy) + { + // it doesn't make any sense to try ssl over i2p + TORRENT_ASSERT(ssl_context == 0); + s.instantiate(ios); + s.get()->set_proxy(ps.hostname, ps.port); + } +#endif + else if (ps.type == proxy_settings::none + || (peer_connection && !ps.proxy_peer_connections)) + { +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + } + else +#endif + { + s.instantiate(ios); + } + } + else if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + http_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + + str->set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::http_pw) + str->set_username(ps.username, ps.password); + } + else if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw + || ps.type == proxy_settings::socks4) + { + socks5_stream* str; +#ifdef TORRENT_USE_OPENSSL + if (ssl_context) + { + s.instantiate >(ios, ssl_context); + str = &s.get >()->next_layer(); + } + else +#endif + { + s.instantiate(ios); + str = s.get(); + } + str->set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + str->set_username(ps.username, ps.password); + if (ps.type == proxy_settings::socks4) + str->set_version(4); + } + else + { + TORRENT_ASSERT_VAL(false, ps.type); + return false; + } + return true; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/ip_filter.cpp b/apps/Launcher/ext/libtorrent/src/ip_filter.cpp new file mode 100644 index 0000000000..c481fea9cb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ip_filter.cpp @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2005-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/ip_filter.hpp" +#include + + +namespace libtorrent +{ + void ip_filter::add_rule(address first, address last, int flags) + { + if (first.is_v4()) + { + TORRENT_ASSERT(last.is_v4()); + m_filter4.add_rule(first.to_v4().to_bytes(), last.to_v4().to_bytes(), flags); + } +#if TORRENT_USE_IPV6 + else if (first.is_v6()) + { + TORRENT_ASSERT(last.is_v6()); + m_filter6.add_rule(first.to_v6().to_bytes(), last.to_v6().to_bytes(), flags); + } +#endif + else + TORRENT_ASSERT(false); + } + + int ip_filter::access(address const& addr) const + { + if (addr.is_v4()) + return m_filter4.access(addr.to_v4().to_bytes()); +#if TORRENT_USE_IPV6 + TORRENT_ASSERT(addr.is_v6()); + return m_filter6.access(addr.to_v6().to_bytes()); +#else + return 0; +#endif + } + + ip_filter::filter_tuple_t ip_filter::export_filter() const + { +#if TORRENT_USE_IPV6 + return boost::make_tuple(m_filter4.export_filter() + , m_filter6.export_filter()); +#else + return m_filter4.export_filter(); +#endif + } + + void port_filter::add_rule(boost::uint16_t first, boost::uint16_t last, int flags) + { + m_filter.add_rule(first, last, flags); + } + + int port_filter::access(boost::uint16_t port) const + { + return m_filter.access(port); + } +/* + void ip_filter::print() const + { + for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i) + { + std::cout << i->start.as_string() << " " << i->access << "\n"; + } + } +*/ +} + diff --git a/apps/Launcher/ext/libtorrent/src/ip_voter.cpp b/apps/Launcher/ext/libtorrent/src/ip_voter.cpp new file mode 100644 index 0000000000..7092f3848c --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ip_voter.cpp @@ -0,0 +1,194 @@ +/* + +Copyright (c) 2013-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/ip_voter.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_any() etc. +#include "libtorrent/socket_io.hpp" // for hash_address +#include "libtorrent/random.hpp" // for random() +#include "libtorrent/time.hpp" // for time_now() + +#include + +namespace libtorrent +{ + ip_voter::ip_voter() + : m_total_votes(0) + , m_valid_external(false) + , m_last_rotate(time_now()) + { + } + + // returns true if our external IP changed + bool ip_voter::maybe_rotate() + { + ptime now = time_now(); + + // if we have more than or equal to 50 votes, + // we rotate. Also, if it's been more than 5 minutes + // and we have at least one vote, we also rotate. + // this is the inverse condition, since this is the case + // were we exit, without rotating + if (m_total_votes < 50 + && (now - m_last_rotate < minutes(5) || m_total_votes == 0) + && m_valid_external) + return false; + + // this shouldn't really happen if we have at least one + // vote. + if (m_external_addresses.empty()) return false; + + // if there's just one vote, go with that + std::vector::iterator i; + if (m_external_addresses.size() == 1) + { + // avoid flapping. We need more votes to change our mind on the + // external IP + if (m_external_addresses[0].num_votes < 2) return false; + } + else + { + // find the top two votes. + std::partial_sort(m_external_addresses.begin() + , m_external_addresses.begin() + 2, m_external_addresses.end()); + + // if we don't have enough of a majority voting for the winning + // IP, don't rotate. This avoids flapping + if (m_external_addresses[0].num_votes * 2 / 3 <= m_external_addresses[1].num_votes) + return false; + } + + i = m_external_addresses.begin(); + + bool ret = m_external_address != i->addr; + m_external_address = i->addr; + + m_external_address_voters.clear(); + m_total_votes = 0; + m_external_addresses.clear(); + m_last_rotate = now; + m_valid_external = true; + return ret; + } + + bool ip_voter::cast_vote(address const& ip + , int source_type, address const& source) + { + if (is_any(ip)) return false; + if (is_local(ip)) return false; + if (is_loopback(ip)) return false; + + // don't trust source that aren't connected to us + // on a different address family than the external + // IP they claim we have + if (ip.is_v4() != source.is_v4()) return false; + + // this is the key to use for the bloom filters + // it represents the identity of the voter + sha1_hash k; + hash_address(source, k); + + // do we already have an entry for this external IP? + std::vector::iterator i = std::find_if(m_external_addresses.begin() + , m_external_addresses.end(), boost::bind(&external_ip_t::addr, _1) == ip); + + if (i == m_external_addresses.end()) + { + // each IP only gets to add a new IP once + if (m_external_address_voters.find(k)) return maybe_rotate(); + + if (m_external_addresses.size() > 40) + { + if (random() % 100 < 50) + return maybe_rotate(); + + // use stable sort here to maintain the fifo-order + // of the entries with the same number of votes + // this will sort in ascending order, i.e. the lowest + // votes first. Also, the oldest are first, so this + // is a sort of weighted LRU. + std::stable_sort(m_external_addresses.begin(), m_external_addresses.end()); + + // erase the last element, since it is one of the + // ones with the fewest votes + m_external_addresses.erase(m_external_addresses.end() - 1); + } + m_external_addresses.push_back(external_ip_t()); + i = m_external_addresses.end() - 1; + i->addr = ip; + } + // add one more vote to this external IP + if (!i->add_vote(k, source_type)) return maybe_rotate(); + ++m_total_votes; + + if (m_valid_external) return maybe_rotate(); + + i = std::min_element(m_external_addresses.begin(), m_external_addresses.end()); + TORRENT_ASSERT(i != m_external_addresses.end()); + + if (i->addr == m_external_address) return maybe_rotate(); + + if (m_external_address != address_v4()) + { + // we have a temporary external address. As soon as we have + // more than 25 votes, consider deciding which one to settle for + return (m_total_votes >= 25) ? maybe_rotate() : false; + } + + m_external_address = i->addr; + + return true; + } + + bool ip_voter::external_ip_t::add_vote(sha1_hash const& k, int type) + { + sources |= type; + if (voters.find(k)) return false; + voters.set(k); + ++num_votes; + return true; + } + + bool external_ip::cast_vote(address const& ip, int source_type, address const& source) + { + return m_vote_group[ip.is_v6()].cast_vote(ip, source_type, source); + } + + address external_ip::external_address(address const& ip) const + { + address ext = m_vote_group[ip.is_v6()].external_address(); +#if TORRENT_USE_IPV6 + if (ip.is_v6() && ext == address_v4()) return address_v6(); +#endif + return ext; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp new file mode 100644 index 0000000000..1eb2588386 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/dht_tracker.cpp @@ -0,0 +1,766 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include +#endif + +#include +#include +#include +#include + +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/traversal_algorithm.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/kademlia/msg.hpp" + +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/escape_string.hpp" + +using boost::ref; +using libtorrent::dht::node_impl; +using libtorrent::dht::node_id; +using libtorrent::dht::packet_t; +using libtorrent::dht::msg; +using namespace libtorrent::detail; + +enum +{ + key_refresh = 5 // generate a new write token key every 5 minutes +}; + +namespace +{ + const int tick_period = 1; // minutes +} + +namespace libtorrent { namespace dht +{ + + void incoming_error(entry& e, char const* msg); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int g_az_message_input = 0; + int g_ut_message_input = 0; + int g_lt_message_input = 0; + int g_mp_message_input = 0; + int g_gr_message_input = 0; + int g_mo_message_input = 0; + int g_unknown_message_input = 0; + + int g_announces = 0; + int g_failed_announces = 0; +#endif + + void intrusive_ptr_add_ref(dht_tracker const* c) + { + TORRENT_ASSERT(c != 0); + TORRENT_ASSERT(c->m_refs >= 0); + ++c->m_refs; + } + + void intrusive_ptr_release(dht_tracker const* c) + { + TORRENT_ASSERT(c != 0); + TORRENT_ASSERT(c->m_refs > 0); + if (--c->m_refs == 0) + delete c; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::string parse_dht_client(lazy_entry const& e) + { + lazy_entry const* ver = e.dict_find_string("v"); + if (!ver) return "generic"; + std::string const& client = ver->string_value(); + if (client.size() < 2) + { + ++g_unknown_message_input; + return client; + } + else if (std::equal(client.begin(), client.begin() + 2, "Az")) + { + ++g_az_message_input; + return "Azureus"; + } + else if (std::equal(client.begin(), client.begin() + 2, "UT")) + { + ++g_ut_message_input; + return "uTorrent"; + } + else if (std::equal(client.begin(), client.begin() + 2, "LT")) + { + ++g_lt_message_input; + return "libtorrent"; + } + else if (std::equal(client.begin(), client.begin() + 2, "MP")) + { + ++g_mp_message_input; + return "MooPolice"; + } + else if (std::equal(client.begin(), client.begin() + 2, "GR")) + { + ++g_gr_message_input; + return "GetRight"; + } + else if (std::equal(client.begin(), client.begin() + 2, "MO")) + { + ++g_mo_message_input; + return "Mono Torrent"; + } + else + { + ++g_unknown_message_input; + return client; + } + } +#endif + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DEFINE_LOG(dht_tracker) +#endif + + node_id extract_node_id(lazy_entry const* e) + { + if (e == 0 || e->type() != lazy_entry::dict_t) return (node_id::min)(); + lazy_entry const* nid = e->dict_find_string("node-id"); + if (nid == 0 || nid->string_length() != 20) return (node_id::min)(); + return node_id(node_id(nid->string_ptr())); + } + + node_id extract_node_id(entry const* e) + { + if (e == 0 || e->type() != entry::dictionary_t) return (node_id::min)(); + entry const* nid = e->find_key("node-id"); + if (nid == 0 || nid->type() != entry::string_t || nid->string().length() != 20) + return (node_id::min)(); + return node_id(node_id(nid->string().c_str())); + } + + // class that puts the networking and the kademlia node in a single + // unit and connecting them together. + dht_tracker::dht_tracker(libtorrent::aux::session_impl& ses, rate_limited_udp_socket& sock + , dht_settings const& settings, entry const* state) + : m_dht(&ses, this, settings, extract_node_id(state) + , ses.external_address().external_address(address_v4()), &ses) + , m_sock(sock) + , m_last_new_key(time_now() - minutes(key_refresh)) + , m_timer(sock.get_io_service()) + , m_connection_timer(sock.get_io_service()) + , m_refresh_timer(sock.get_io_service()) + , m_settings(settings) + , m_refresh_bucket(160) + , m_abort(false) + , m_host_resolver(sock.get_io_service()) + , m_sent_bytes(0) + , m_received_bytes(0) + , m_refs(0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_counter = 0; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + g_announces = 0; + g_failed_announces = 0; + m_total_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; + + // turns on and off individual components' logging + +// rpc_log().enable(false); +// node_log().enable(false); +// traversal_log().enable(false); +// dht_tracker_log.enable(false); + + TORRENT_LOG(dht_tracker) << "starting DHT tracker with node id: " << m_dht.nid(); +#endif + for (int i = 0; i < num_ban_nodes; ++i) + { + m_ban_nodes[i].count = 0; + m_ban_nodes[i].limit = min_time(); + } + } + + dht_tracker::~dht_tracker() {} + + // defined in node.cpp + extern void nop(); + + void dht_tracker::start(entry const& bootstrap + , find_data::nodes_callback const& f) + { + std::vector initial_nodes; + + if (bootstrap.type() == entry::dictionary_t) + { + TORRENT_TRY { + if (entry const* nodes = bootstrap.find_key("nodes")) + read_endpoint_list(nodes, initial_nodes); + } TORRENT_CATCH(std::exception&) {} + } + + error_code ec; + m_timer.expires_from_now(seconds(1), ec); + m_timer.async_wait(boost::bind(&dht_tracker::tick, self(), _1)); + + m_connection_timer.expires_from_now(seconds(1), ec); + m_connection_timer.async_wait( + boost::bind(&dht_tracker::connection_timeout, self(), _1)); + + m_refresh_timer.expires_from_now(seconds(5), ec); + m_refresh_timer.async_wait(boost::bind(&dht_tracker::refresh_timeout, self(), _1)); + m_dht.bootstrap(initial_nodes, f); + } + + void dht_tracker::stop() + { + m_abort = true; + error_code ec; + m_timer.cancel(ec); + m_connection_timer.cancel(ec); + m_refresh_timer.cancel(ec); + m_host_resolver.cancel(); + } + + void dht_tracker::dht_status(session_status& s) + { + m_dht.status(s); + } + + void dht_tracker::network_stats(int& sent, int& received) + { + sent = m_sent_bytes; + received = m_received_bytes; + m_sent_bytes = 0; + m_received_bytes = 0; + } + + void dht_tracker::connection_timeout(error_code const& e) + { + if (e || m_abort) return; + + time_duration d = m_dht.connection_timeout(); + error_code ec; + m_connection_timer.expires_from_now(d, ec); + m_connection_timer.async_wait(boost::bind(&dht_tracker::connection_timeout, self(), _1)); + } + + void dht_tracker::refresh_timeout(error_code const& e) + { + if (e || m_abort) return; + + m_dht.tick(); + error_code ec; + m_refresh_timer.expires_from_now(seconds(5), ec); + m_refresh_timer.async_wait( + boost::bind(&dht_tracker::refresh_timeout, self(), _1)); + } + + void dht_tracker::tick(error_code const& e) + { + if (e || m_abort) return; + + error_code ec; + m_timer.expires_from_now(minutes(tick_period), ec); + m_timer.async_wait(boost::bind(&dht_tracker::tick, self(), _1)); + + ptime now = time_now(); + if (now - m_last_new_key > minutes(key_refresh)) + { + m_last_new_key = now; + m_dht.new_write_key(); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** new write key"; +#endif + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + static bool first = true; + + std::ofstream st("dht_routing_table_state.txt", std::ios_base::trunc); + m_dht.print_state(st); + + // count torrents + int torrents = m_dht.num_torrents(); + + // count peers + int peers = m_dht.num_peers(); + + std::ofstream pc("dht_stats.log", first ? std::ios_base::trunc : std::ios_base::app); + if (first) + { + first = false; + pc << "\n\n ***** starting log at " << time_now_string() << " *****\n\n" + << "minute:active nodes:passive nodes:confirmed nodes" + ":ping replies sent:ping queries recvd" + ":ping replies bytes sent:ping queries bytes recvd" + ":find_node replies sent:find_node queries recv" + ":find_node replies bytes sent:find_node queries bytes recv" + ":get_peers replies sent:get_peers queries recvd" + ":get_peers replies bytes sent:get_peers queries bytes recv" + ":announce_peer replies sent:announce_peer queries recvd" + ":announce_peer replies bytes sent:announce_peer queries bytes recv" + ":error replies sent:error queries recvd" + ":error replies bytes sent:error queries bytes recv" + ":num torrents:num peers:announces per min" + ":failed announces per min:total msgs per min" + ":az msgs per min:ut msgs per min:lt msgs per min:mp msgs per min" + ":gr msgs per min:mo msgs per min:bytes in per sec:bytes out per sec" + ":queries out bytes per sec\n\n"; + } + + int active; + int passive; + int confirmed; + boost::tie(active, passive, confirmed) = m_dht.size(); + pc << (m_counter * tick_period) + << "\t" << active + << "\t" << passive + << "\t" << confirmed; + for (int i = 0; i < 5; ++i) + pc << "\t" << (m_replies_sent[i] / float(tick_period)) + << "\t" << (m_queries_received[i] / float(tick_period)) + << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60)) + << "\t" << (m_queries_bytes_received[i] / float(tick_period*60)); + + pc << "\t" << torrents + << "\t" << peers + << "\t" << g_announces / float(tick_period) + << "\t" << g_failed_announces / float(tick_period) + << "\t" << (m_total_message_input / float(tick_period)) + << "\t" << (g_az_message_input / float(tick_period)) + << "\t" << (g_ut_message_input / float(tick_period)) + << "\t" << (g_lt_message_input / float(tick_period)) + << "\t" << (g_mp_message_input / float(tick_period)) + << "\t" << (g_gr_message_input / float(tick_period)) + << "\t" << (g_mo_message_input / float(tick_period)) + << "\t" << (m_total_in_bytes / float(tick_period*60)) + << "\t" << (m_total_out_bytes / float(tick_period*60)) + << "\t" << (m_queries_out_bytes / float(tick_period*60)) + << std::endl; + ++m_counter; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + g_announces = 0; + g_failed_announces = 0; + m_total_message_input = 0; + g_az_message_input = 0; + g_ut_message_input = 0; + g_lt_message_input = 0; + g_mp_message_input = 0; + g_gr_message_input = 0; + g_mo_message_input = 0; + g_unknown_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; +#endif + } + + void dht_tracker::announce(sha1_hash const& ih, int listen_port, int flags + , boost::function const&)> f) + { + m_dht.announce(ih, listen_port, flags, f); + } + + // these functions provide a slightly higher level + // interface to the get/put functionality in the DHT + bool get_immutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(!it.is_mutable()); + f(it); + return false; + } + + bool get_mutable_item_callback(item& it, boost::function f) + { + // the reason to wrap here is to control the return value + // since it controls whether we re-put the content + TORRENT_ASSERT(it.is_mutable()); + f(it); + return false; + } + + bool put_immutable_item_callback(item& it, boost::function f + , entry data) + { + TORRENT_ASSERT(!it.is_mutable()); + it.assign(data); + // TODO: ideally this function would be called when the + // put completes + f(); + return true; + } + + bool put_mutable_item_callback(item& it, boost::function cb) + { + cb(it); + return true; + } + + void dht_tracker::get_item(sha1_hash const& target + , boost::function cb) + { + m_dht.get_item(target, boost::bind(&get_immutable_item_callback, _1, cb)); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void dht_tracker::get_item(char const* key + , boost::function cb + , std::string salt) + { + m_dht.get_item(key, salt, boost::bind(&get_mutable_item_callback, _1, cb)); + } + + void dht_tracker::put_item(entry data + , boost::function cb) + { + std::string flat_data; + bencode(std::back_inserter(flat_data), data); + sha1_hash target = item_target_id( + std::pair(flat_data.c_str(), flat_data.size())); + + m_dht.get_item(target, boost::bind(&put_immutable_item_callback + , _1, cb, data)); + } + + void dht_tracker::put_item(char const* key + , boost::function cb, std::string salt) + { + m_dht.get_item(key, salt, boost::bind(&put_mutable_item_callback + , _1, cb)); + } + + // translate bittorrent kademlia message into the generice kademlia message + // used by the library + bool dht_tracker::incoming_packet(error_code const& ec + , udp::endpoint const& ep, char const* buf, int size) + { + if (ec) + { + if (ec == asio::error::connection_refused + || ec == asio::error::connection_reset + || ec == asio::error::connection_aborted +#ifdef WIN32 + || ec == error_code(ERROR_HOST_UNREACHABLE, get_system_category()) + || ec == error_code(ERROR_PORT_UNREACHABLE, get_system_category()) + || ec == error_code(ERROR_CONNECTION_REFUSED, get_system_category()) + || ec == error_code(ERROR_CONNECTION_ABORTED, get_system_category()) +#endif + ) + { + m_dht.unreachable(ep); + } + return false; + } + + if (size <= 20 || *buf != 'd' || buf[size-1] != 'e') return false; + + // account for IP and UDP overhead + m_received_bytes += size + (ep.address().is_v6() ? 48 : 28); + + if (m_settings.ignore_dark_internet && ep.address().is_v4()) + { + address_v4::bytes_type b = ep.address().to_v4().to_bytes(); + + // these are class A networks not available to the public + // if we receive messages from here, that seems suspicious + boost::uint8_t class_a[] = { 3, 6, 7, 9, 11, 19, 21, 22, 25 + , 26, 28, 29, 30, 33, 34, 45, 48, 51, 52, 56, 102, 104 }; + + int num = sizeof(class_a)/sizeof(class_a[0]); + if (std::find(class_a, class_a + num, b[0]) != class_a + num) + return true; + } + + node_ban_entry* match = 0; + node_ban_entry* min = m_ban_nodes; + ptime now = time_now(); + for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i) + { + if (i->src == ep.address()) + { + match = i; + break; + } + if (i->count < min->count) min = i; + else if (i->count == min->count + && i->limit < min->limit) min = i; + } + + if (match) + { + ++match->count; + if (match->count >= 50) + { + if (now < match->limit) + { + // the first time we exceed the limit, ban it for 5 minutes + if (match->count == 50) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " BANNING NODE [ ip: " + << ep << " time: " << total_milliseconds((now - match->limit) + seconds(10)) / 1000.f + << " count: " << match->count << " ]"; +#endif + match->limit = now + minutes(5); + } + // we've received 50 messages in less than 10 seconds from + // this node. Ignore it until it's silent for 5 minutes + return true; + } + + // we got 50 messages from this peer, but it was in + // more than 10 seconds. Reset the counter and the timer + match->count = 0; + match->limit = now + seconds(10); + } + } + else + { + min->count = 1; + min->limit = now + seconds(10); + min->src = ep.address(); + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_total_message_input; + m_total_in_bytes += size; +#endif + + using libtorrent::entry; + using libtorrent::bdecode; + + TORRENT_ASSERT(size > 0); + + lazy_entry e; + int pos; + error_code err; + int ret = lazy_bdecode(buf, buf + size, e, err, &pos, 10, 500); + if (ret != 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "<== " << ep << " ERROR: " + << err.message() << " pos: " << pos; +#endif + return false; + } + + libtorrent::dht::msg m(e, ep); + + if (e.type() != lazy_entry::dict_t) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "<== " << ep << " ERROR: not a dictionary: " + << print_entry(e, true); +#endif + // it's not a good idea to send invalid messages + // especially not in response to an invalid message +// entry r; +// libtorrent::dht::incoming_error(r, "message is not a dictionary"); +// send_packet(r, ep, 0); + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + parse_dht_client(e); + TORRENT_LOG(dht_tracker) << "<== " << ep << " " << print_entry(e, true); + + if (e.dict_find_string_value("y") == "q") + { + std::string cmd = e.dict_find_string_value("q"); + int cmd_idx = -1; + if (cmd == "ping") cmd_idx = 0; + else if (cmd == "find_node") cmd_idx = 1; + else if (cmd == "get_peers") cmd_idx = 2; + else if (cmd == "announce_peeer") cmd_idx = 3; + if (cmd_idx >= 0) + { + ++m_queries_received[cmd_idx]; + m_queries_bytes_received[cmd_idx] += size; + } + } +#endif + + m_dht.incoming(m); + return true; + } + + void add_node_fun(void* userdata, node_entry const& e) + { + entry* n = (entry*)userdata; + std::string node; + std::back_insert_iterator out(node); + write_endpoint(e.ep(), out); + n->list().push_back(entry(node)); + } + + entry dht_tracker::state() const + { + entry ret(entry::dictionary_t); + { + entry nodes(entry::list_t); + m_dht.m_table.for_each_node(&add_node_fun, &add_node_fun, &nodes); + bucket_t cache; + m_dht.replacement_cache(cache); + for (bucket_t::iterator i(cache.begin()) + , end(cache.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->ep(), out); + nodes.list().push_back(entry(node)); + } + if (!nodes.list().empty()) + ret["nodes"] = nodes; + } + + ret["node-id"] = m_dht.nid().to_string(); + return ret; + } + + void dht_tracker::add_node(udp::endpoint node) + { + m_dht.add_node(node); + } + + void dht_tracker::add_node(std::pair const& node) + { + char port[7]; + snprintf(port, sizeof(port), "%d", node.second); + udp::resolver::query q(node.first, port); + m_host_resolver.async_resolve(q, + boost::bind(&dht_tracker::on_name_lookup, self(), _1, _2)); + } + + void dht_tracker::on_name_lookup(error_code const& e + , udp::resolver::iterator host) + { + if (e || host == udp::resolver::iterator()) return; + add_node(host->endpoint()); + } + + void dht_tracker::add_router_node(udp::endpoint const& node) + { + m_dht.add_router_node(node); + } + + bool dht_tracker::send_packet(libtorrent::entry& e, udp::endpoint const& addr, int send_flags) + { + using libtorrent::bencode; + using libtorrent::entry; + + static char const version_str[] = {'L', 'T' + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR}; + e["v"] = std::string(version_str, version_str + 4); + + m_send_buf.clear(); + bencode(std::back_inserter(m_send_buf), e); + error_code ec; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::stringstream log_line; + lazy_entry print; + int ret = lazy_bdecode(&m_send_buf[0], &m_send_buf[0] + m_send_buf.size(), print, ec); + TORRENT_ASSERT(ret == 0); + log_line << print_entry(print, true); +#endif + + if (m_sock.send(addr, &m_send_buf[0], (int)m_send_buf.size(), ec, send_flags)) + { + if (ec) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "==> " << addr << " DROPPED (" << ec.message() << ") " << log_line.str(); +#endif + return false; + } + + // account for IP and UDP overhead + m_sent_bytes += m_send_buf.size() + (addr.address().is_v6() ? 48 : 28); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_total_out_bytes += m_send_buf.size(); + + if (e["y"].string() == "r") + { +/* + // This doesn't work. r is a dictionary with return + // values. The query type isn't part of the response + std::string cmd = e["r"].string(); + int cmd_idx = -1; + if (cmd == "ping") cmd_idx = 0; + else if (cmd == "find_node") cmd_idx = 1; + else if (cmd == "get_peers") cmd_idx = 2; + else if (cmd == "announce_peeer") cmd_idx = 3; + if (cmd_idx >= 0) + { + ++m_replies_sent[cmd_idx]; + m_replies_bytes_sent[cmd_idx] += int(m_send_buf.size()); + } +*/ + } + else if (e["y"].string() == "q") + { + m_queries_out_bytes += m_send_buf.size(); + } + TORRENT_LOG(dht_tracker) << "==> " << addr << " " << log_line.str(); +#endif + return true; + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "==> " << addr << " DROPPED " << log_line.str(); +#endif + return false; + } + } + +}} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp new file mode 100644 index 0000000000..0106bc92c1 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/find_data.cpp @@ -0,0 +1,179 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(traversal); +#endif + +using detail::read_endpoint_list; +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +void find_data_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] invalid id in response"; +#endif + return; + } + lazy_entry const* token = r->dict_find_string("token"); + if (token) + { + static_cast(m_algorithm.get())->got_write_token( + node_id(id->string_ptr()), token->string_value()); + } + + traversal_observer::reply(m); + done(); +} + +void add_entry_fun(void* userdata, node_entry const& e) +{ + traversal_algorithm* f = (traversal_algorithm*)userdata; + f->add_entry(e.id, e.ep(), observer::flag_initial); +} + +find_data::find_data( + node_impl& node + , node_id target + , nodes_callback const& ncallback) + : traversal_algorithm(node, target) + , m_nodes_callback(ncallback) + , m_done(false) +{ +} + +void find_data::start() +{ + // if the user didn't add seed-nodes manually, grab a bunch of nodes from the + // routing table + if (m_results.empty()) + m_node.m_table.for_each_node(&add_entry_fun, 0, (traversal_algorithm*)this); + + traversal_algorithm::start(); +} + +void find_data::got_write_token(node_id const& n, std::string const& write_token) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] adding write " + "token '" << to_hex(write_token) << "' under id '" << to_hex(n.to_string()) << "'"; +#endif + m_write_tokens[n] = write_token; +} + +observer_ptr find_data::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) find_data_observer(this, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +char const* find_data::name() const { return "find_data"; } + +void find_data::done() +{ + if (m_invoke_count != 0) return; + + m_done = true; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] " << name() << " DONE"; +#endif + + std::vector > results; + int num_results = m_node.m_table.bucket_size(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && num_results > 0; ++i) + { + observer_ptr const& o = *i; + if ((o->flags & observer::flag_alive) == 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] not alive: " + << o->target_ep(); +#endif + continue; + } + std::map::iterator j = m_write_tokens.find(o->id()); + if (j == m_write_tokens.end()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] no write token: " + << o->target_ep(); +#endif + continue; + } + results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second)); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] " + << o->target_ep(); +#endif + --num_results; + } + + if (m_nodes_callback) m_nodes_callback(results); + + traversal_algorithm::done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp new file mode 100644 index 0000000000..c31ede6300 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/get_item.cpp @@ -0,0 +1,283 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#if TORRENT_USE_ASSERTS +#include +#endif + +namespace libtorrent { namespace dht +{ + +void get_item::got_data(lazy_entry const* v, + char const* pk, + boost::uint64_t seq, + char const* sig) +{ + // we received data! + + std::pair salt(m_salt.c_str(), m_salt.size()); + + sha1_hash incoming_target; + if (pk) + incoming_target = item_target_id(salt, pk); + else + incoming_target = item_target_id(v->data_section()); + + if (incoming_target != m_target) return; + + if (pk && sig) + { + // this is mutable data. If it passes the signature + // check, remember it. Just keep the version with + // the highest sequence number. + if (m_data.empty() || m_data.seq() < seq) + { + if (!m_data.assign(v, salt, seq, pk, sig)) + return; + } + } + else if (m_data.empty()) + { + // this is the first time we receive data, + // and it's immutable + + m_data.assign(v); + bool put_requested = m_data_callback(m_data); + + // if we intend to put, we need to keep going + // until we find the closest nodes, since those + // are the ones we're putting to + if (put_requested) + { +#if TORRENT_USE_ASSERTS + std::vector buffer; + bencode(std::back_inserter(buffer), m_data.value()); + TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); +#endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. + m_nodes_callback = boost::bind(&get_item::put, this, _1); + } + else + { + // There can only be one true immutable item with a given id + // Now that we've got it and the user doesn't want to do a put + // there's no point in continuing to query other nodes + abort(); + } + } +} + +get_item::get_item( + node_impl& node + , node_id target + , data_callback const& dcallback) + : find_data(node, target, nodes_callback()) + , m_data_callback(dcallback) +{ +} + +get_item::get_item( + node_impl& node + , char const* pk + , std::string const& salt + , data_callback const& dcallback) + : find_data(node, item_target_id( + std::make_pair(salt.c_str(), int(salt.size())), pk) + , nodes_callback()) + , m_data_callback(dcallback) + , m_data(pk, salt) +{ +} + +char const* get_item::name() const { return "get"; } + +observer_ptr get_item::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_item_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +bool get_item::invoke(observer_ptr o) +{ + if (m_done) + { + m_invoke_count = -1; + return false; + } + + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get"; + a["target"] = m_target.to_string(); + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +void get_item::done() +{ + if (m_data.is_mutable() || m_data.empty()) + { + // for mutable data, we only call the callback at the end, + // when we've heard from everyone, to be sure we got the + // latest version of the data (i.e. highest sequence number) + bool put_requested = m_data_callback(m_data); + if (put_requested) + { +#if TORRENT_USE_ASSERTS + if (m_data.is_mutable()) + { + TORRENT_ASSERT(m_target + == item_target_id(std::pair(m_data.salt().c_str() + , m_data.salt().size()) + , m_data.pk().data())); + } + else + { + std::vector buffer; + bencode(std::back_inserter(buffer), m_data.value()); + TORRENT_ASSERT(m_target == hasher(&buffer[0], buffer.size()).final()); + } +#endif + + // this function is called when we're done, passing + // in all relevant nodes we received data from close + // to the target. + m_nodes_callback = boost::bind(&get_item::put, this, _1); + } + } + find_data::done(); +} + +// this function sends a put message to the nodes +// closest to the target. Those nodes are passed in +// as the v argument +void get_item::put(std::vector > const& v) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "sending put [ v: \"" << m_data.value() + << "\" seq: " << (m_data.is_mutable() ? m_data.seq() : -1) + << " nodes: " << v.size() << " ]" ; +#endif + + // create a dummy traversal_algorithm + boost::intrusive_ptr algo( + new traversal_algorithm(m_node, (node_id::min)())); + + // store on the first k nodes + for (std::vector >::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << " put-distance: " << (160 - distance_exp(m_target, i->first.id)); +#endif + + void* ptr = m_node.m_rpc.allocate_observer(); + if (ptr == 0) return; + + // TODO: 3 we don't support CAS errors here! we need a custom observer + observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + e["q"] = "put"; + entry& a = e["a"]; + a["v"] = m_data.value(); + a["token"] = i->second; + if (m_data.is_mutable()) + { + a["k"] = std::string(m_data.pk().data(), item_pk_len); + a["seq"] = m_data.seq(); + a["sig"] = std::string(m_data.sig().data(), item_sig_len); + if (!m_data.salt().empty()) + { + a["salt"] = m_data.salt(); + } + } + m_node.m_rpc.invoke(e, i->first.ep(), o); + } +} + +void get_item_observer::reply(msg const& m) +{ + char const* pk = NULL; + char const* sig = NULL; + boost::uint64_t seq = 0; + + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + lazy_entry const* k = r->dict_find_string("k"); + if (k && k->string_length() == item_pk_len) + pk = k->string_ptr(); + + lazy_entry const* s = r->dict_find_string("sig"); + if (s && s->string_length() == item_sig_len) + sig = s->string_ptr(); + + lazy_entry const* q = r->dict_find_int("seq"); + if (q) + seq = q->int_value(); + else if (pk && sig) + return; + + lazy_entry const* v = r->dict_find("v"); + if (v) + { + static_cast(m_algorithm.get())->got_data(v, pk, seq, sig); + } + + find_data_observer::reply(m); +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp new file mode 100644 index 0000000000..ec0a1beaf0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/get_peers.cpp @@ -0,0 +1,313 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +using detail::read_endpoint_list; +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +void get_peers_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] missing response dict"; +#endif + return; + } + + // look for peers + lazy_entry const* n = r->dict_find_list("values"); + if (n) + { + std::vector peer_list; + if (n->list_size() == 1 && n->list_at(0)->type() == lazy_entry::string_t) + { + // assume it's mainline format + char const* peers = n->list_at(0)->string_ptr(); + char const* end = peers + n->list_at(0)->string_length(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* id = r->dict_find_string("id"); + if (id && id->string_length() == 20) + { + TORRENT_LOG(traversal) + << "[" << m_algorithm.get() << "] PEERS" + << " invoke-count: " << m_algorithm->invoke_count() + << " branch-factor: " << m_algorithm->branch_factor() + << " addr: " << m.addr + << " id: " << node_id(id->string_ptr()) + << " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr())) + << " p: " << ((end - peers) / 6); + } +#endif + while (end - peers >= 6) + peer_list.push_back(read_v4_endpoint(peers)); + } + else + { + // assume it's uTorrent/libtorrent format + read_endpoint_list(n, peer_list); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* id = r->dict_find_string("id"); + if (id && id->string_length() == 20) + { + TORRENT_LOG(traversal) + << "[" << m_algorithm.get() << "] PEERS" + << " invoke-count: " << m_algorithm->invoke_count() + << " branch-factor: " << m_algorithm->branch_factor() + << " addr: " << m.addr + << " id: " << node_id(id->string_ptr()) + << " distance: " << distance_exp(m_algorithm->target(), node_id(id->string_ptr())) + << " p: " << n->list_size(); + } +#endif + } + static_cast(m_algorithm.get())->got_peers(peer_list); + } + + find_data_observer::reply(m); +} + +void get_peers::got_peers(std::vector const& peers) +{ + if (m_data_callback) m_data_callback(peers); +} + +get_peers::get_peers( + node_impl& node + , node_id target + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds) + : find_data(node, target, ncallback) + , m_data_callback(dcallback) + , m_noseeds(noseeds) +{ +} + +char const* get_peers::name() const { return "get_peers"; } + +bool get_peers::invoke(observer_ptr o) +{ + if (m_done) + { + m_invoke_count = -1; + return false; + } + + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get_peers"; + a["info_hash"] = m_target.to_string(); + if (m_noseeds) a["noseed"] = 1; + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +observer_ptr get_peers::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +obfuscated_get_peers::obfuscated_get_peers( + node_impl& node + , node_id info_hash + , data_callback const& dcallback + , nodes_callback const& ncallback + , bool noseeds) + : get_peers(node, info_hash, dcallback, ncallback, noseeds) + , m_obfuscated(true) +{ +} + +char const* obfuscated_get_peers::name() const +{ return !m_obfuscated ? get_peers::name() : "get_peers [obfuscated]"; } + +observer_ptr obfuscated_get_peers::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + if (m_obfuscated) + { + observer_ptr o(new (ptr) obfuscated_get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; + } + else + { + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; + } +} + +bool obfuscated_get_peers::invoke(observer_ptr o) +{ + if (!m_obfuscated) return get_peers::invoke(o); + + node_id id = o->id(); + int shared_prefix = 160 - distance_exp(id, m_target); + + // when we get close to the target zone in the DHT + // start using the correct info-hash, in order to + // start receiving peers + if (shared_prefix > m_node.m_table.depth() - 10) + { + m_obfuscated = false; + // clear the queried bits on all successful nodes in + // our node-list for this traversal algorithm, to + // allow the get_peers traversal to regress in case + // nodes further down end up being dead + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer* o = i->get(); + // don't re-request from nodes that didn't respond + if (o->flags & observer::flag_failed) continue; + // don't interrupt with queries that are already in-flight + if ((o->flags & observer::flag_alive) == 0) continue; + o->flags &= ~(observer::flag_queried | observer::flag_alive); + } + return get_peers::invoke(o); + } + + entry e; + e["y"] = "q"; + e["q"] = "get_peers"; + entry& a = e["a"]; + + // This logic will obfuscate the target info-hash + // we're looking up, in order to preserve more privacy + // on the DHT. This is done by only including enough + // bits in the info-hash for the node we're querying to + // give a good answer, but not more. + + // now, obfuscate the bits past shared_prefix + 3 + node_id mask = generate_prefix_mask(shared_prefix + 3); + node_id obfuscated_target = generate_random_id() & ~mask; + obfuscated_target |= m_target & mask; + a["info_hash"] = obfuscated_target.to_string(); + + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +void obfuscated_get_peers::done() +{ + if (!m_obfuscated) return get_peers::done(); + + // oops, we failed to switch over to the non-obfuscated + // mode early enough. do it now + + boost::intrusive_ptr ta(new get_peers(m_node, m_target + , m_data_callback + , m_nodes_callback + , m_noseeds)); + + // don't call these when the obfuscated_get_peers + // is done, we're passing them on to be called when + // ta completes. + m_data_callback.clear(); + m_nodes_callback.clear(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << " [" << this << "]" + << " obfuscated get_peers phase 1 done, spawning get_peers [" << ta.get() << "]"; +#endif + + int num_added = 0; + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && num_added < 16; ++i) + { + observer_ptr o = *i; + + // only add nodes whose node ID we know and that + // we know are alive + if (o->flags & observer::flag_no_id) continue; + if ((o->flags & observer::flag_alive) == 0) continue; + + ta->add_entry(o->id(), o->target_ep(), observer::flag_initial); + ++num_added; + } + + ta->start(); + + get_peers::done(); +} + +void obfuscated_get_peers_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] invalid id in response"; +#endif + return; + } + + traversal_observer::reply(m); + + done(); +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp new file mode 100644 index 0000000000..d586008870 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/item.cpp @@ -0,0 +1,224 @@ +/* + +Copyright (c) 2013, Steven Siloti +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#ifdef TORRENT_DEBUG +#include "libtorrent/lazy_entry.hpp" +#endif + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +namespace libtorrent { namespace dht +{ + +namespace +{ + enum { canonical_length = 1200 }; + int canonical_string(std::pair v, boost::uint64_t seq + , std::pair salt, char out[canonical_length]) + { + // v must be valid bencoding! +#ifdef TORRENT_DEBUG + lazy_entry e; + error_code ec; + TORRENT_ASSERT(lazy_bdecode(v.first, v.first + v.second, e, ec) == 0); +#endif + char* ptr = out; + + int left = canonical_length - (ptr - out); + if (salt.second > 0) + { + ptr += snprintf(ptr, left, "4:salt%d:", salt.second); + left = canonical_length - (ptr - out); + memcpy(ptr, salt.first, (std::min)(salt.second, left)); + ptr += (std::min)(salt.second, left); + left = canonical_length - (ptr - out); + } + ptr += snprintf(ptr, canonical_length - (ptr - out) + , "3:seqi%" PRId64 "e1:v", seq); + left = canonical_length - (ptr - out); + memcpy(ptr, v.first, (std::min)(v.second, left)); + ptr += (std::min)(v.second, left); + TORRENT_ASSERT((ptr - out) <= canonical_length); + return ptr - out; + } +} + +// calculate the target hash for an immutable item. +sha1_hash item_target_id(std::pair v) +{ + hasher h; + h.update(v.first, v.second); + return h.final(); +} + +// calculate the target hash for a mutable item. +sha1_hash item_target_id(std::pair salt + , char const* pk) +{ + hasher h; + h.update(pk, item_pk_len); + if (salt.second > 0) h.update(salt.first, salt.second); + return h.final(); +} + +bool verify_mutable_item( + std::pair v, + std::pair salt, + boost::uint64_t seq, + char const* pk, + char const* sig) +{ +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); + VALGRIND_CHECK_MEM_IS_DEFINED(sig, item_sig_len); +#endif + + char str[canonical_length]; + int len = canonical_string(v, seq, salt, str); + + return ed25519_verify((unsigned char const*)sig, + (unsigned char const*)str, + len, + (unsigned char const*)pk) == 1; +} + +// given the bencoded buffer ``v``, the salt (which is optional and may have +// a length of zero to be omitted), sequence number ``seq``, public key (32 +// bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 +// key) a signature ``sig`` is produced. The ``sig`` pointer must point to +// at least 64 bytes of available space. This space is where the signature is +// written. +void sign_mutable_item( + std::pair v, + std::pair salt, + boost::uint64_t seq, + char const* pk, + char const* sk, + char* sig) +{ +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); + VALGRIND_CHECK_MEM_IS_DEFINED(sk, item_sk_len); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); +#endif + + char str[canonical_length]; + int len = canonical_string(v, seq, salt, str); + + ed25519_sign((unsigned char*)sig, + (unsigned char const*)str, + len, + (unsigned char const*)pk, + (unsigned char const*)sk + ); +} + +item::item(char const* pk, std::string const& salt) + : m_salt(salt) + , m_seq(0) + , m_mutable(true) +{ + memcpy(m_pk.data(), pk, item_pk_len); +} + +item::item(entry const& v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk) +{ + assign(v, salt, seq, pk, sk); +} + +void item::assign(entry const& v, std::pair salt + , boost::uint64_t seq, char const* pk, char const* sk) +{ + m_value = v; + if (pk && sk) + { + char buffer[1000]; + int bsize = bencode(buffer, v); + TORRENT_ASSERT(bsize <= 1000); + sign_mutable_item(std::make_pair(buffer, bsize) + , salt, seq, pk, sk, m_sig.c_array()); + m_salt.assign(salt.first, salt.second); + memcpy(m_pk.c_array(), pk, item_pk_len); + m_seq = seq; + m_mutable = true; + } + else + m_mutable = false; +} + +bool item::assign(lazy_entry const* v + , std::pair salt + , boost::uint64_t seq, char const* pk, char const* sig) +{ + TORRENT_ASSERT(v->data_section().second <= 1000); + if (pk && sig) + { + if (!verify_mutable_item(v->data_section(), salt, seq, pk, sig)) + return false; + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); + if (salt.second > 0) + m_salt.assign(salt.first, salt.second); + else + m_salt.clear(); + m_seq = seq; + m_mutable = true; + } + else + m_mutable = false; + + m_value = *v; + return true; +} + +void item::assign(entry const& v, std::string salt, boost::uint64_t seq + , char const* pk, char const* sig) +{ + memcpy(m_pk.c_array(), pk, item_pk_len); + memcpy(m_sig.c_array(), sig, item_sig_len); + m_salt = salt; + m_seq = seq; + m_mutable = true; + m_value = v; +} + +} } // namespace libtorrent::dht diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp new file mode 100644 index 0000000000..02522a7ca9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/logging.cpp @@ -0,0 +1,55 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/kademlia/logging.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent { namespace dht +{ + log_event::log_event(log& log) + : log_(log) + { + if (log_.enabled()) + log_ << time_now_string() << " [" << log.id() << "] "; + } + + log_event::~log_event() + { + if (log_.enabled()) + { + log_ << "\n"; + log_.flush(); + } + } + +}} + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp new file mode 100644 index 0000000000..79fca13c1b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/node.cpp @@ -0,0 +1,1291 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/io.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/rpc_manager.hpp" +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/dht_observer.hpp" + +#include "libtorrent/kademlia/refresh.hpp" +#include "libtorrent/kademlia/get_peers.hpp" +#include "libtorrent/kademlia/get_item.hpp" + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +namespace libtorrent { namespace dht +{ + +void incoming_error(entry& e, char const* msg, int error_code = 203); + +using detail::write_endpoint; + +// TODO: 2 make this configurable in dht_settings +enum { announce_interval = 30 }; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(node) + +extern int g_failed_announces; +extern int g_announces; + +#endif + +// remove peers that have timed out +void purge_peers(std::set& peers) +{ + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end;) + { + // the peer has timed out + if (i->added + minutes(int(announce_interval * 1.5f)) < time_now()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "peer timed out at: " << i->addr; +#endif + peers.erase(i++); + } + else + ++i; + } +} + +void nop() {} + +node_impl::node_impl(alert_dispatcher* alert_disp + , udp_socket_interface* sock + , dht_settings const& settings, node_id nid, address const& external_address + , dht_observer* observer) + : m_settings(settings) + , m_id(nid == (node_id::min)() || !verify_id(nid, external_address) ? generate_id(external_address) : nid) + , m_table(m_id, 8, settings) + , m_rpc(m_id, m_table, sock) + , m_observer(observer) + , m_last_tracker_tick(time_now()) + , m_last_self_refresh(min_time()) + , m_post_alert(alert_disp) + , m_sock(sock) +{ + m_secret[0] = random(); + m_secret[1] = std::rand(); +} + +bool node_impl::verify_token(std::string const& token, char const* info_hash + , udp::endpoint const& addr) +{ + if (token.length() != 4) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "token of incorrect length: " << token.length(); +#endif + return false; + } + + hasher h1; + error_code ec; + std::string address = addr.address().to_string(ec); + if (ec) return false; + h1.update(&address[0], address.length()); + h1.update((char*)&m_secret[0], sizeof(m_secret[0])); + h1.update((char*)info_hash, sha1_hash::size); + + sha1_hash h = h1.final(); + if (std::equal(token.begin(), token.end(), (char*)&h[0])) + return true; + + hasher h2; + h2.update(&address[0], address.length()); + h2.update((char*)&m_secret[1], sizeof(m_secret[1])); + h2.update((char*)info_hash, sha1_hash::size); + h = h2.final(); + if (std::equal(token.begin(), token.end(), (char*)&h[0])) + return true; + return false; +} + +std::string node_impl::generate_token(udp::endpoint const& addr, char const* info_hash) +{ + std::string token; + token.resize(4); + hasher h; + error_code ec; + std::string address = addr.address().to_string(ec); + TORRENT_ASSERT(!ec); + h.update(&address[0], address.length()); + h.update((char*)&m_secret[0], sizeof(m_secret[0])); + h.update(info_hash, sha1_hash::size); + + sha1_hash hash = h.final(); + std::copy(hash.begin(), hash.begin() + 4, (char*)&token[0]); + TORRENT_ASSERT(std::equal(token.begin(), token.end(), (char*)&hash[0])); + return token; +} + +void node_impl::bootstrap(std::vector const& nodes + , find_data::nodes_callback const& f) +{ + node_id target = m_id; + make_id_secret(target); + + boost::intrusive_ptr r(new dht::bootstrap(*this, target, f)); + m_last_self_refresh = time_now(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int count = 0; +#endif + + for (std::vector::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++count; +#endif + r->add_entry(node_id(0), *i, observer::flag_initial); + } + + // make us start as far away from our node ID as possible + r->trim_seed_nodes(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "bootstrapping with " << count << " nodes"; +#endif + r->start(); +} + +int node_impl::bucket_size(int bucket) +{ + return m_table.bucket_size(bucket); +} + +void node_impl::new_write_key() +{ + m_secret[1] = m_secret[0]; + m_secret[0] = random(); +} + +void node_impl::unreachable(udp::endpoint const& ep) +{ + m_rpc.unreachable(ep); +} + +void node_impl::incoming(msg const& m) +{ + // is this a reply? + lazy_entry const* y_ent = m.message.dict_find_string("y"); + if (!y_ent || y_ent->string_length() == 0) + { + // don't respond to this obviously broken messages. We don't + // want to open up a magnification opportunity +// entry e; +// incoming_error(e, "missing 'y' entry"); +// m_sock->send_packet(e, m.addr, 0); + return; + } + + char y = *(y_ent->string_ptr()); + + lazy_entry const* ext_ip = m.message.dict_find_string("ip"); + + // backwards compatibility + if (ext_ip == NULL) + { + lazy_entry const* r = m.message.dict_find_dict("r"); + if (r) + ext_ip = r->dict_find_string("ip"); + } + +#if TORRENT_USE_IPV6 + if (ext_ip && ext_ip->string_length() >= 16) + { + // this node claims we use the wrong node-ID! + address_v6::bytes_type b; + memcpy(&b[0], ext_ip->string_ptr(), 16); + if (m_observer) + m_observer->set_external_address(address_v6(b) + , m.addr.address()); + } else +#endif + if (ext_ip && ext_ip->string_length() >= 4) + { + address_v4::bytes_type b; + memcpy(&b[0], ext_ip->string_ptr(), 4); + if (m_observer) + m_observer->set_external_address(address_v4(b) + , m.addr.address()); + } + + switch (y) + { + case 'r': + { + node_id id; + m_rpc.incoming(m, &id, m_settings); + break; + } + case 'q': + { + TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q"); + entry e; + incoming_request(m, e); + m_sock->send_packet(e, m.addr, 0); + break; + } + case 'e': + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + lazy_entry const* err = m.message.dict_find_list("e"); + if (err && err->list_size() >= 2) + { + TORRENT_LOG(node) << "INCOMING ERROR: " << err->list_string_value_at(1); + } +#endif + node_id id; + m_rpc.incoming(m, &id, m_settings); + break; + } + } +} + +namespace +{ + void announce_fun(std::vector > const& v + , node_impl& node, int listen_port, sha1_hash const& ih, int flags) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "sending announce_peer [ ih: " << ih + << " p: " << listen_port + << " nodes: " << v.size() << " ]" ; +#endif + + // create a dummy traversal_algorithm + boost::intrusive_ptr algo( + new traversal_algorithm(node, (node_id::min)())); + + // store on the first k nodes + for (std::vector >::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << " announce-distance: " << (160 - distance_exp(ih, i->first.id)); +#endif + + void* ptr = node.m_rpc.allocate_observer(); + if (ptr == 0) return; + observer_ptr o(new (ptr) announce_observer(algo, i->first.ep(), i->first.id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + e["q"] = "announce_peer"; + entry& a = e["a"]; + a["info_hash"] = ih.to_string(); + a["port"] = listen_port; + a["token"] = i->second; + a["seed"] = (flags & node_impl::flag_seed) ? 1 : 0; + if (flags & node_impl::flag_implied_port) a["implied_port"] = 1; + node.m_rpc.invoke(e, i->first.ep(), o); + } + } +} + +void node_impl::add_router_node(udp::endpoint router) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "adding router node: " << router; +#endif + m_table.add_router_node(router); +} + +void node_impl::add_node(udp::endpoint node) +{ + // ping the node, and if we get a reply, it + // will be added to the routing table + send_single_refresh(node, m_table.num_active_buckets()); +} + +void node_impl::announce(sha1_hash const& info_hash, int listen_port, int flags + , boost::function const&)> f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "announcing [ ih: " << info_hash << " p: " << listen_port << " ]" ; +#endif + // search for nodes with ids close to id or with peers + // for info-hash id. then send announce_peer to them. + + boost::intrusive_ptr ta; + if (m_settings.privacy_lookups) + { + ta.reset(new obfuscated_get_peers(*this, info_hash, f + , boost::bind(&announce_fun, _1, boost::ref(*this) + , listen_port, info_hash, flags), flags & node_impl::flag_seed)); + } + else + { + ta.reset(new get_peers(*this, info_hash, f + , boost::bind(&announce_fun, _1, boost::ref(*this) + , listen_port, info_hash, flags), flags & node_impl::flag_seed)); + } + + ta->start(); +} + +void node_impl::get_item(sha1_hash const& target + , boost::function f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "starting get for [ hash: " << target << " ]" ; +#endif + + boost::intrusive_ptr ta; + ta.reset(new dht::get_item(*this, target, f)); + ta->start(); +} + +void node_impl::get_item(char const* pk, std::string const& salt + , boost::function f) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "starting get for [ key: " + << to_hex(std::string(pk, 32)) << " ]" ; +#endif + + boost::intrusive_ptr ta; + ta.reset(new dht::get_item(*this, pk, salt, f)); + ta->start(); +} + +struct ping_observer : observer +{ + ping_observer( + boost::intrusive_ptr const& algorithm + , udp::endpoint const& ep, node_id const& id) + : observer(algorithm, ep, id) + {} + + // parses out "nodes" + virtual void reply(msg const& m) + { + flags |= flag_done; + + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + // look for nodes + lazy_entry const* n = r->dict_find_string("nodes"); + if (n) + { + char const* nodes = n->string_ptr(); + char const* end = nodes + n->string_length(); + + while (end - nodes >= 26) + { + node_id id; + std::copy(nodes, nodes + 20, id.begin()); + nodes += 20; + m_algorithm.get()->node().m_table.heard_about(id + , detail::read_v4_endpoint(nodes)); + } + } + } +}; + + +void node_impl::tick() +{ + // every now and then we refresh our own ID, just to keep + // expanding the routing table buckets closer to us. + ptime now = time_now(); + if (m_last_self_refresh + minutes(10) < now) + { + node_id target = m_id; + make_id_secret(target); + boost::intrusive_ptr r(new dht::bootstrap(*this, target + , boost::bind(&nop))); + r->start(); + m_last_self_refresh = now; + return; + } + + node_entry const* ne = m_table.next_refresh(); + if (ne == NULL) return; + + // this shouldn't happen + TORRENT_ASSERT(m_id != ne->id); + if (ne->id == m_id) return; + + int bucket = 159 - distance_exp(m_id, ne->id); + TORRENT_ASSERT(bucket < 160); + send_single_refresh(ne->ep(), bucket, ne->id); +} + +void node_impl::send_single_refresh(udp::endpoint const& ep, int bucket + , node_id const& id) +{ + TORRENT_ASSERT(id != m_id); + void* ptr = m_rpc.allocate_observer(); + if (ptr == 0) return; + + TORRENT_ASSERT(bucket >= 0); + TORRENT_ASSERT(bucket <= 159); + + // generate a random node_id within the given bucket + // TODO: 2 it would be nice to have a bias towards node-id prefixes that + // are missing in the bucket + node_id mask = generate_prefix_mask(bucket + 1); + node_id target = generate_secret_id() & ~mask; + target |= m_id & mask; + + // create a dummy traversal_algorithm + // this is unfortunately necessary for the observer + // to free itself from the pool when it's being released + boost::intrusive_ptr algo( + new traversal_algorithm(*this, (node_id::min)())); + observer_ptr o(new (ptr) ping_observer(algo, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + // use get_peers instead of find_node. We'll get nodes in the response + // either way. + e["q"] = "get_peers"; + a["info_hash"] = target.to_string(); + +// e["q"] = "find_node"; +// a["target"] = target.to_string(); + m_rpc.invoke(e, ep, o); +} + +time_duration node_impl::connection_timeout() +{ + time_duration d = m_rpc.tick(); + ptime now(time_now()); + if (now - m_last_tracker_tick < minutes(2)) return d; + m_last_tracker_tick = now; + + for (dht_immutable_table_t::iterator i = m_immutable_table.begin(); + i != m_immutable_table.end();) + { + if (i->second.last_seen + minutes(60) > now) + { + ++i; + continue; + } + free(i->second.value); + m_immutable_table.erase(i++); + } + + // look through all peers and see if any have timed out + for (table_t::iterator i = m_map.begin(), end(m_map.end()); i != end;) + { + torrent_entry& t = i->second; + node_id const& key = i->first; + ++i; + purge_peers(t.peers); + + // if there are no more peers, remove the entry altogether + if (t.peers.empty()) + { + table_t::iterator i = m_map.find(key); + if (i != m_map.end()) m_map.erase(i); + } + } + + return d; +} + +void node_impl::status(session_status& s) +{ + mutex_t::scoped_lock l(m_mutex); + + m_table.status(s); + s.dht_torrents = int(m_map.size()); + s.active_requests.clear(); + s.dht_total_allocations = m_rpc.num_allocated_observers(); + for (std::set::iterator i = m_running_requests.begin() + , end(m_running_requests.end()); i != end; ++i) + { + s.active_requests.push_back(dht_lookup()); + dht_lookup& l = s.active_requests.back(); + (*i)->status(l); + } +} + +void node_impl::lookup_peers(sha1_hash const& info_hash, entry& reply + , bool noseed, bool scrape) const +{ + if (m_post_alert) + { + alert* a = new dht_get_peers_alert(info_hash); + if (!m_post_alert->post_alert(a)) delete a; + } + + table_t::const_iterator i = m_map.lower_bound(info_hash); + if (i == m_map.end()) return; + if (i->first != info_hash) return; + + torrent_entry const& v = i->second; + + if (!v.name.empty()) reply["n"] = v.name; + + if (scrape) + { + bloom_filter<256> downloaders; + bloom_filter<256> seeds; + + for (std::set::const_iterator i = v.peers.begin() + , end(v.peers.end()); i != end; ++i) + { + sha1_hash iphash; + hash_address(i->addr.address(), iphash); + if (i->seed) seeds.set(iphash); + else downloaders.set(iphash); + } + + reply["BFpe"] = downloaders.to_string(); + reply["BFsd"] = seeds.to_string(); + } + else + { + int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); + std::set::const_iterator iter = v.peers.begin(); + entry::list_type& pe = reply["values"].list(); + std::string endpoint; + + for (int t = 0, m = 0; m < num && iter != v.peers.end(); ++iter, ++t) + { + if ((random() / float(UINT_MAX + 1.f)) * (num - t) >= num - m) continue; + if (noseed && iter->seed) continue; + endpoint.resize(18); + std::string::iterator out = endpoint.begin(); + write_endpoint(iter->addr, out); + endpoint.resize(out - endpoint.begin()); + pe.push_back(entry(endpoint)); + + ++m; + } + } + return; +} + +namespace detail +{ + void TORRENT_EXTRA_EXPORT write_nodes_entry(entry& r, nodes_t const& nodes) + { + bool ipv6_nodes = false; + entry& n = r["nodes"]; + std::back_insert_iterator out(n.string()); + for (nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { + if (!i->addr().is_v4()) + { + ipv6_nodes = true; + continue; + } + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(udp::endpoint(i->addr(), i->port()), out); + } + + if (ipv6_nodes) + { + entry& p = r["nodes2"]; + std::string endpoint; + for (nodes_t::const_iterator i = nodes.begin() + , end(nodes.end()); i != end; ++i) + { + if (!i->addr().is_v6()) continue; + endpoint.resize(18 + 20); + std::string::iterator out = endpoint.begin(); + std::copy(i->id.begin(), i->id.end(), out); + out += 20; + write_endpoint(udp::endpoint(i->addr(), i->port()), out); + endpoint.resize(out - endpoint.begin()); + p.list().push_back(entry(endpoint)); + } + } + } +} +using detail::write_nodes_entry; + +// verifies that a message has all the required +// entries and returns them in ret +bool verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry const* ret[] + , int size , char* error, int error_size) +{ + // clear the return buffer + memset(ret, 0, sizeof(ret[0]) * size); + + // when parsing child nodes, this is the stack + // of lazy_entry pointers to return to + lazy_entry const* stack[5]; + int stack_ptr = -1; + + if (msg->type() != lazy_entry::dict_t) + { + snprintf(error, error_size, "not a dictionary"); + return false; + } + ++stack_ptr; + stack[stack_ptr] = msg; + for (int i = 0; i < size; ++i) + { + key_desc_t const& k = desc[i]; + +// fprintf(stderr, "looking for %s in %s\n", k.name, print_entry(*msg).c_str()); + + ret[i] = msg->dict_find(k.name); + // none_t means any type + if (ret[i] && ret[i]->type() != k.type && k.type != lazy_entry::none_t) ret[i] = 0; + if (ret[i] == 0 && (k.flags & key_desc_t::optional) == 0) + { + // the key was not found, and it's not an optional key + snprintf(error, error_size, "missing '%s' key", k.name); + return false; + } + + if (k.size > 0 + && ret[i] + && k.type == lazy_entry::string_t) + { + bool invalid = false; + if (k.flags & key_desc_t::size_divisible) + invalid = (ret[i]->string_length() % k.size) != 0; + else + invalid = ret[i]->string_length() != k.size; + + if (invalid) + { + // the string was not of the required size + ret[i] = 0; + if ((k.flags & key_desc_t::optional) == 0) + { + snprintf(error, error_size, "invalid value for '%s'", k.name); + return false; + } + } + } + if (k.flags & key_desc_t::parse_children) + { + TORRENT_ASSERT(k.type == lazy_entry::dict_t); + + if (ret[i]) + { + ++stack_ptr; + TORRENT_ASSERT(stack_ptr < int(sizeof(stack)/sizeof(stack[0]))); + msg = ret[i]; + stack[stack_ptr] = msg; + } + else + { + // skip all children + while (i < size && (desc[i].flags & key_desc_t::last_child) == 0) ++i; + // if this assert is hit, desc is incorrect + TORRENT_ASSERT(i < size); + } + } + else if (k.flags & key_desc_t::last_child) + { + TORRENT_ASSERT(stack_ptr > 0); + // this can happen if the specification passed + // in is unbalanced. i.e. contain more last_child + // nodes than parse_children + if (stack_ptr == 0) return false; + --stack_ptr; + msg = stack[stack_ptr]; + } + } + return true; +} + +void incoming_error(entry& e, char const* msg, int error_code) +{ + e["y"] = "e"; + entry::list_type& l = e["e"].list(); + l.push_back(entry(error_code)); + l.push_back(entry(msg)); +} + +// return true of the first argument is a better canidate for removal, i.e. +// less important to keep +struct immutable_item_comparator +{ + immutable_item_comparator(node_id const& our_id) : m_our_id(our_id) {} + + bool operator() (std::pair const& lhs + , std::pair const& rhs) const + { + int l_distance = distance_exp(lhs.first, m_our_id); + int r_distance = distance_exp(rhs.first, m_our_id); + + // this is a score taking the popularity (number of announcers) and the + // fit, in terms of distance from ideal storing node, into account. + // each additional 5 announcers is worth one extra bit in the distance. + // that is, an item with 10 announcers is allowed to be twice as far + // from another item with 5 announcers, from our node ID. Twice as far + // because it gets one more bit. + return lhs.second.num_announcers / 5 - l_distance < rhs.second.num_announcers / 5 - r_distance; + } + + node_id const& m_our_id; +}; + +// build response +void node_impl::incoming_request(msg const& m, entry& e) +{ + e = entry(entry::dictionary_t); + e["y"] = "r"; + e["t"] = m.message.dict_find_string_value("t"); + + key_desc_t top_desc[] = { + {"q", lazy_entry::string_t, 0, 0}, + {"ro", lazy_entry::int_t, 0, key_desc_t::optional}, + {"a", lazy_entry::dict_t, 0, key_desc_t::parse_children}, + {"id", lazy_entry::string_t, 20, key_desc_t::last_child}, + }; + + lazy_entry const* top_level[4]; + char error_string[200]; + if (!verify_message(&m.message, top_desc, top_level, 4, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + e["ip"] = endpoint_to_bytes(m.addr); + + char const* query = top_level[0]->string_cstr(); + + lazy_entry const* arg_ent = top_level[2]; + bool read_only = top_level[1] && top_level[1]->int_value() != 0; + node_id id(top_level[3]->string_ptr()); + + // if this nodes ID doesn't match its IP, tell it what + // its IP is with an error + // don't enforce this yet + if (m_settings.enforce_node_id && !verify_id(id, m.addr.address())) + { + incoming_error(e, "invalid node ID"); + return; + } + + if (!read_only) + m_table.heard_about(id, m.addr); + + entry& reply = e["r"]; + m_rpc.add_our_id(reply); + + // mirror back the other node's external port + reply["p"] = m.addr.port(); + + if (strcmp(query, "ping") == 0) + { + // we already have 't' and 'id' in the response + // no more left to add + } + else if (strcmp(query, "get_peers") == 0) + { + key_desc_t msg_desc[] = { + {"info_hash", lazy_entry::string_t, 20, 0}, + {"noseed", lazy_entry::int_t, 0, key_desc_t::optional}, + {"scrape", lazy_entry::int_t, 0, key_desc_t::optional}, + }; + + lazy_entry const* msg_keys[3]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 3, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + reply["token"] = generate_token(m.addr, msg_keys[0]->string_ptr()); + + sha1_hash info_hash(msg_keys[0]->string_ptr()); + nodes_t n; + // always return nodes as well as peers + m_table.find_node(info_hash, n, 0); + write_nodes_entry(reply, n); + + bool noseed = false; + bool scrape = false; + if (msg_keys[1] && msg_keys[1]->int_value() != 0) noseed = true; + if (msg_keys[2] && msg_keys[2]->int_value() != 0) scrape = true; + lookup_peers(info_hash, reply, noseed, scrape); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (reply.find_key("values")) + { + TORRENT_LOG(node) << " values: " << reply["values"].list().size(); + } +#endif + } + else if (strcmp(query, "find_node") == 0) + { + key_desc_t msg_desc[] = { + {"target", lazy_entry::string_t, 20, 0}, + }; + + lazy_entry const* msg_keys[1]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 1, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + sha1_hash target(msg_keys[0]->string_ptr()); + + // TODO: 2 find_node should write directly to the response entry + nodes_t n; + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + } + else if (strcmp(query, "announce_peer") == 0) + { + key_desc_t msg_desc[] = { + {"info_hash", lazy_entry::string_t, 20, 0}, + {"port", lazy_entry::int_t, 0, 0}, + {"token", lazy_entry::string_t, 0, 0}, + {"n", lazy_entry::string_t, 0, key_desc_t::optional}, + {"seed", lazy_entry::int_t, 0, key_desc_t::optional}, + {"implied_port", lazy_entry::int_t, 0, key_desc_t::optional}, + }; + + lazy_entry const* msg_keys[6]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 6, error_string, sizeof(error_string))) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, error_string); + return; + } + + int port = int(msg_keys[1]->int_value()); + + // is the announcer asking to ignore the explicit + // listen port and instead use the source port of the packet? + if (msg_keys[5] && msg_keys[5]->int_value() != 0) + port = m.addr.port(); + + if (port < 0 || port >= 65536) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, "invalid port"); + return; + } + + sha1_hash info_hash(msg_keys[0]->string_ptr()); + + if (m_post_alert) + { + alert* a = new dht_announce_alert(m.addr.address(), port, info_hash); + if (!m_post_alert->post_alert(a)) delete a; + } + + if (!verify_token(msg_keys[2]->string_value(), msg_keys[0]->string_ptr(), m.addr)) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_failed_announces; +#endif + incoming_error(e, "invalid token"); + return; + } + + // the token was correct. That means this + // node is not spoofing its address. So, let + // the table get a chance to add it. + m_table.node_seen(id, m.addr, 0xffff); + + if (!m_map.empty() && int(m_map.size()) >= m_settings.max_torrents) + { + // we need to remove some. Remove the ones with the + // fewest peers + int num_peers = m_map.begin()->second.peers.size(); + table_t::iterator candidate = m_map.begin(); + for (table_t::iterator i = m_map.begin() + , end(m_map.end()); i != end; ++i) + { + if (int(i->second.peers.size()) > num_peers) continue; + if (i->first == info_hash) continue; + num_peers = i->second.peers.size(); + candidate = i; + } + m_map.erase(candidate); + } + torrent_entry& v = m_map[info_hash]; + + // the peer announces a torrent name, and we don't have a name + // for this torrent. Store it. + if (msg_keys[3] && v.name.empty()) + { + std::string name = msg_keys[3]->string_value(); + if (name.size() > 50) name.resize(50); + v.name = name; + } + + peer_entry peer; + peer.addr = tcp::endpoint(m.addr.address(), port); + peer.added = time_now(); + peer.seed = msg_keys[4] && msg_keys[4]->int_value(); + std::set::iterator i = v.peers.find(peer); + if (i != v.peers.end()) v.peers.erase(i++); + v.peers.insert(i, peer); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++g_announces; +#endif + } + else if (strcmp(query, "put") == 0) + { + // the first 2 entries are for both mutable and + // immutable puts + const static key_desc_t msg_desc[] = { + {"token", lazy_entry::string_t, 0, 0}, + {"v", lazy_entry::none_t, 0, 0}, + {"seq", lazy_entry::int_t, 0, key_desc_t::optional}, + // public key + {"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional}, + {"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional}, + {"cas", lazy_entry::int_t, 0, key_desc_t::optional}, + {"salt", lazy_entry::string_t, 0, key_desc_t::optional}, + }; + + // attempt to parse the message + lazy_entry const* msg_keys[7]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 7, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + // is this a mutable put? + bool mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]); + + // public key (only set if it's a mutable put) + char const* pk = NULL; + if (msg_keys[3]) pk = msg_keys[3]->string_ptr(); + + // signature (only set if it's a mutable put) + char const* sig = NULL; + if (msg_keys[4]) sig = msg_keys[4]->string_ptr(); + + // pointer and length to the whole entry + std::pair buf = msg_keys[1]->data_section(); + if (buf.second > 1000 || buf.second <= 0) + { + incoming_error(e, "message too big", 205); + return; + } + + std::pair salt(static_cast(NULL), 0); + if (msg_keys[6]) + salt = std::pair( + msg_keys[6]->string_ptr(), msg_keys[6]->string_length()); + if (salt.second > 64) + { + incoming_error(e, "salt too big", 207); + return; + } + + sha1_hash target; + if (pk) + target = item_target_id(salt, pk); + else + target = item_target_id(buf); + +// fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n" +// , mutable_put ? "mutable":"immutable" +// , to_hex(target.to_string()).c_str() +// , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : "" +// , pk ? to_hex(std::string(pk, 32)).c_str() : ""); + + // verify the write-token. tokens are only valid to write to + // specific target hashes. it must match the one we got a "get" for + if (!verify_token(msg_keys[0]->string_value(), (char const*)&target[0], m.addr)) + { + incoming_error(e, "invalid token"); + return; + } + + dht_immutable_item* f = 0; + + if (!mutable_put) + { + dht_immutable_table_t::iterator i = m_immutable_table.find(target); + if (i == m_immutable_table.end()) + { + // make sure we don't add too many items + if (int(m_immutable_table.size()) >= m_settings.max_dht_items) + { + // delete the least important one (i.e. the one + // the fewest peers are announcing, and farthest + // from our node ID) + dht_immutable_table_t::iterator j = std::min_element(m_immutable_table.begin() + , m_immutable_table.end() + , immutable_item_comparator(m_id)); + + TORRENT_ASSERT(j != m_immutable_table.end()); + free(j->second.value); + m_immutable_table.erase(j); + } + dht_immutable_item to_add; + to_add.value = (char*)malloc(buf.second); + to_add.size = buf.second; + memcpy(to_add.value, buf.first, buf.second); + + boost::tie(i, boost::tuples::ignore) = m_immutable_table.insert( + std::make_pair(target, to_add)); + } + +// fprintf(stderr, "added immutable item (%d)\n", int(m_immutable_table.size())); + + f = &i->second; + } + else + { + // mutable put, we must verify the signature + +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len); + VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); +#endif + // msg_keys[4] is the signature, msg_keys[3] is the public key + if (!verify_mutable_item(buf, salt + , msg_keys[2]->int_value(), pk, sig)) + { + incoming_error(e, "invalid signature", 206); + return; + } + + dht_mutable_table_t::iterator i = m_mutable_table.find(target); + if (i == m_mutable_table.end()) + { + // this is the case where we don't have an item in this slot + // make sure we don't add too many items + if (int(m_mutable_table.size()) >= m_settings.max_dht_items) + { + // delete the least important one (i.e. the one + // the fewest peers are announcing) + dht_mutable_table_t::iterator j = std::min_element(m_mutable_table.begin() + , m_mutable_table.end() + , boost::bind(&dht_immutable_item::num_announcers + , boost::bind(&dht_mutable_table_t::value_type::second, _1))); + TORRENT_ASSERT(j != m_mutable_table.end()); + free(j->second.value); + free(j->second.salt); + m_mutable_table.erase(j); + } + dht_mutable_item to_add; + to_add.value = (char*)malloc(buf.second); + to_add.size = buf.second; + to_add.seq = msg_keys[2]->int_value(); + to_add.salt = NULL; + to_add.salt_size = 0; + if (salt.second > 0) + { + to_add.salt = (char*)malloc(salt.second); + to_add.salt_size = salt.second; + memcpy(to_add.salt, salt.first, salt.second); + } + memcpy(to_add.sig, sig, sizeof(to_add.sig)); + TORRENT_ASSERT(sizeof(to_add.sig) == msg_keys[4]->string_length()); + memcpy(to_add.value, buf.first, buf.second); + memcpy(&to_add.key, pk, sizeof(to_add.key)); + + boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert( + std::make_pair(target, to_add)); + +// fprintf(stderr, "added mutable item (%d)\n", int(m_mutable_table.size())); + } + else + { + // this is the case where we already + dht_mutable_item* item = &i->second; + + // this is the "cas" field in the put message + // if it was specified, we MUST make sure the current sequence + // number matches the expected value before replacing it + // this is critical for avoiding race conditions when multiple + // writers are accessing the same slot + if (msg_keys[5] && item->seq != msg_keys[5]->int_value()) + { + incoming_error(e, "CAS mismatch", 301); + return; + } + + if (item->seq > boost::uint64_t(msg_keys[2]->int_value())) + { + incoming_error(e, "old sequence number", 302); + return; + } + + if (item->seq < boost::uint64_t(msg_keys[2]->int_value())) + { + if (item->size != buf.second) + { + free(item->value); + item->value = (char*)malloc(buf.second); + item->size = buf.second; + } + item->seq = msg_keys[2]->int_value(); + memcpy(item->sig, msg_keys[4]->string_ptr(), sizeof(item->sig)); + TORRENT_ASSERT(sizeof(item->sig) == msg_keys[4]->string_length()); + memcpy(item->value, buf.first, buf.second); + } + } + + f = &i->second; + } + + m_table.node_seen(id, m.addr, 0xffff); + + f->last_seen = time_now(); + + // maybe increase num_announcers if we haven't seen this IP before + sha1_hash iphash; + hash_address(m.addr.address(), iphash); + if (!f->ips.find(iphash)) + { + f->ips.set(iphash); + ++f->num_announcers; + } + } + else if (strcmp(query, "get") == 0) + { + key_desc_t msg_desc[] = { + {"seq", lazy_entry::int_t, 0, key_desc_t::optional}, + {"target", lazy_entry::string_t, 20, 0}, + }; + + // k is not used for now + + // attempt to parse the message + lazy_entry const* msg_keys[2]; + if (!verify_message(arg_ent, msg_desc, msg_keys, 2, error_string, sizeof(error_string))) + { + incoming_error(e, error_string); + return; + } + + sha1_hash target(msg_keys[1]->string_ptr()); + +// fprintf(stderr, "%s GET target: %s\n" +// , msg_keys[1] ? "mutable":"immutable" +// , to_hex(target.to_string()).c_str()); + + reply["token"] = generate_token(m.addr, msg_keys[1]->string_ptr()); + + nodes_t n; + // always return nodes as well as peers + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + + dht_immutable_table_t::iterator i = m_immutable_table.end(); + + // if the get has a sequence number it must be for a mutable item + // so don't bother searching the immutable table + if (!msg_keys[0]) + i = m_immutable_table.find(target); + + if (i != m_immutable_table.end()) + { + dht_immutable_item const& f = i->second; + reply["v"] = bdecode(f.value, f.value + f.size); + } + else + { + dht_mutable_table_t::iterator i = m_mutable_table.find(target); + if (i != m_mutable_table.end()) + { + dht_mutable_item const& f = i->second; + reply["seq"] = f.seq; + if (!msg_keys[0] || boost::uint64_t(msg_keys[0]->int_value()) < f.seq) + { + reply["v"] = bdecode(f.value, f.value + f.size); + reply["sig"] = std::string(f.sig, f.sig + sizeof(f.sig)); + reply["k"] = std::string(f.key.bytes, f.key.bytes + sizeof(f.key.bytes)); + } + } + } + } + else + { + // if we don't recognize the message but there's a + // 'target' or 'info_hash' in the arguments, treat it + // as find_node to be future compatible + lazy_entry const* target_ent = arg_ent->dict_find_string("target"); + if (target_ent == 0 || target_ent->string_length() != 20) + { + target_ent = arg_ent->dict_find_string("info_hash"); + if (target_ent == 0 || target_ent->string_length() != 20) + { + incoming_error(e, "unknown message"); + return; + } + } + + sha1_hash target(target_ent->string_ptr()); + nodes_t n; + // always return nodes as well as peers + m_table.find_node(target, n, 0); + write_nodes_entry(reply, n); + return; + } +} + + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp new file mode 100644 index 0000000000..88b1f9af9f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/node_id.cpp @@ -0,0 +1,231 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/node_entry.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_local et.al +#include "libtorrent/socket_io.hpp" // for hash_address +#include "libtorrent/random.hpp" // for random +#include "libtorrent/hasher.hpp" // for hasher + +namespace libtorrent { namespace dht +{ + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id distance(node_id const& n1, node_id const& n2) +{ + node_id ret; + node_id::iterator k = ret.begin(); + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, ++k) + { + *k = *i ^ *j; + } + return ret; +} + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref) +{ + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k) + { + boost::uint8_t lhs = (*i ^ *k); + boost::uint8_t rhs = (*j ^ *k); + if (lhs < rhs) return true; + if (lhs > rhs) return false; + } + return false; +} + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// useful for finding out which bucket a node belongs to +int distance_exp(node_id const& n1, node_id const& n2) +{ + int byte = node_id::size - 1; + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, --byte) + { + TORRENT_ASSERT(byte >= 0); + boost::uint8_t t = *i ^ *j; + if (t == 0) continue; + // we have found the first non-zero byte + // return the bit-number of the first bit + // that differs + int bit = byte * 8; + for (int b = 7; b >= 0; --b) + if (t >= (1 << b)) return bit + b; + return bit; + } + + return 0; +} + +struct static_ { static_() { std::srand((unsigned int)std::time(0)); } } static__; + +node_id generate_id_impl(address const& ip_, boost::uint32_t r) +{ + boost::uint8_t* ip = 0; + + const static boost::uint8_t v4mask[] = { 0x03, 0x0f, 0x3f, 0xff }; + const static boost::uint8_t v6mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + boost::uint8_t const* mask = 0; + int num_octets = 0; + + address_v4::bytes_type b4; +#if TORRENT_USE_IPV6 + address_v6::bytes_type b6; + if (ip_.is_v6()) + { + b6 = ip_.to_v6().to_bytes(); + ip = &b6[0]; + num_octets = 8; + mask = v6mask; + } + else +#endif + { + b4 = ip_.to_v4().to_bytes(); + ip = &b4[0]; + num_octets = 4; + mask = v4mask; + } + + for (int i = 0; i < num_octets; ++i) + ip[i] &= mask[i]; + + ip[0] |= (r & 0x7) << 5; + + // this is the crc32c (Castagnoli) polynomial + // TODO: 2 this could be optimized if SSE 4.2 is + // available. It could also be optimized given + // that we have a fixed length + boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; + crc.process_block(ip, ip + num_octets); + boost::uint32_t c = crc.checksum(); + node_id id; + + id[0] = (c >> 24) & 0xff; + id[1] = (c >> 16) & 0xff; + id[2] = ((c >> 8) & 0xf8) | (random() & 0x7); + + for (int i = 3; i < 19; ++i) id[i] = random() & 0xff; + id[19] = r & 0xff; + + return id; +} + +static boost::uint32_t secret = 0; + +void make_id_secret(node_id& in) +{ + if (secret == 0) secret = (random() % 0xfffffffe) + 1; + + boost::uint32_t rand = random(); + + // generate the last 4 bytes as a "signature" of the previous 4 bytes. This + // lets us verify whether a hash came from this function or not in the future. + hasher h((char*)&secret, 4); + h.update((char*)&rand, 4); + sha1_hash secret_hash = h.final(); + memcpy(&in[20-4], &secret_hash[0], 4); + memcpy(&in[20-8], &rand, 4); +} + +node_id generate_random_id() +{ + char r[20]; + for (int i = 0; i < 20; ++i) r[i] = random() & 0xff; + return hasher(r, 20).final(); +} + +node_id generate_secret_id() +{ + node_id ret = generate_random_id(); + make_id_secret(ret); + return ret; +} + +bool verify_secret_id(node_id const& nid) +{ + if (secret == 0) return false; + + hasher h((char*)&secret, 4); + h.update((char const*)&nid[20-8], 4); + sha1_hash secret_hash = h.final(); + return memcmp(&nid[20-4], &secret_hash[0], 4) == 0; +} + +// verifies whether a node-id matches the IP it's used from +// returns true if the node-id is OK coming from this source +// and false otherwise. +bool verify_id(node_id const& nid, address const& source_ip) +{ + // no need to verify local IPs, they would be incorrect anyway + if (is_local(source_ip)) return true; + + node_id h = generate_id_impl(source_ip, nid[19]); + return nid[0] == h[0] && nid[1] == h[1] && (nid[2] & 0xf8) == (h[2] & 0xf8); +} + +node_id generate_id(address const& ip) +{ + return generate_id_impl(ip, random()); +} + +bool matching_prefix(node_entry const& n, int mask, int prefix, int bucket_index) +{ + node_id id = n.id; + id <<= bucket_index + 1; + return (id[0] & mask) == prefix; +} + +node_id generate_prefix_mask(int bits) +{ + TORRENT_ASSERT(bits >= 0); + TORRENT_ASSERT(bits <= 160); + node_id mask(0); + int b = 0; + for (; b < bits - 7; b += 8) mask[b/8] |= 0xff; + if (bits < 160) mask[b/8] |= (0xff << (8 - (bits&7))) & 0xff; + return mask; +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp new file mode 100644 index 0000000000..dc0eb3b20d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/refresh.cpp @@ -0,0 +1,110 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(traversal); +#endif + +observer_ptr bootstrap::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) get_peers_observer(this, ep, id)); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +bool bootstrap::invoke(observer_ptr o) +{ + entry e; + e["y"] = "q"; + entry& a = e["a"]; + + e["q"] = "get_peers"; + a["info_hash"] = target().to_string(); + +// e["q"] = "find_node"; +// a["target"] = target().to_string(); + return m_node.m_rpc.invoke(e, o->target_ep(), o); +} + +bootstrap::bootstrap( + node_impl& node + , node_id target + , done_callback const& callback) + : get_peers(node, target, get_peers::data_callback(), callback, false) +{ + // make it more resilient to nodes not responding. + // we don't want to terminate early when we're bootstrapping + m_num_target_nodes *= 2; +} + +char const* bootstrap::name() const { return "bootstrap"; } + +void bootstrap::trim_seed_nodes() +{ + // when we're bootstrapping, we want to start as far away from our ID as + // possible, to cover as much as possible of the ID space. So, remove all + // nodes except for the 32 that are farthest away from us + if (m_results.size() > 32) + m_results.erase(m_results.begin(), m_results.end() - 32); +} + +void bootstrap::done() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "]" + << " bootstrap done, pinging remaining nodes"; +#endif + + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + if ((*i)->flags & observer::flag_queried) continue; + // this will send a ping + m_node.add_node((*i)->target_ep()); + } + get_peers::done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp new file mode 100644 index 0000000000..8df6cf31d4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/routing_table.cpp @@ -0,0 +1,1146 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include // std::distance() +#include // std::copy, std::remove_copy_if +#include +#include +#include +#include + +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/broadcast_socket.hpp" // for cidr_distance +#include "libtorrent/session_status.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/time.hpp" + +#include "libtorrent/invariant_check.hpp" + +using boost::uint8_t; + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(table) +#endif + +template +void erase_one(T& container, K const& key) +{ + typename T::iterator i = container.find(key); + TORRENT_ASSERT(i != container.end()); + container.erase(i); +} + +routing_table::routing_table(node_id const& id, int bucket_size + , dht_settings const& settings) + : m_settings(settings) + , m_bucket_size(bucket_size) + , m_id(id) + , m_depth(0) + , m_last_self_refresh(min_time()) +{ + m_buckets.reserve(30); +} + +int routing_table::bucket_limit(int bucket) const +{ + if (!m_settings.extended_routing_table) return m_bucket_size; + + const static int size_exceptions[] = {16, 8, 4, 2}; + if (bucket < int(sizeof(size_exceptions)/sizeof(size_exceptions[0]))) + return m_bucket_size * size_exceptions[bucket]; + return m_bucket_size; +} + +void routing_table::status(session_status& s) const +{ + int ignore; + boost::tie(s.dht_nodes, s.dht_node_cache, ignore) = size(); + s.dht_global_nodes = num_global_nodes(); + + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + dht_routing_bucket b; + b.num_nodes = i->live_nodes.size(); + b.num_replacements = i->replacements.size(); +#ifndef TORRENT_NO_DEPRECATE + b.last_active = 0; +#endif + s.dht_routing_table.push_back(b); + } +} + +boost::tuple routing_table::size() const +{ + int nodes = 0; + int replacements = 0; + int confirmed = 0; + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + nodes += i->live_nodes.size(); + for (bucket_t::const_iterator k = i->live_nodes.begin() + , end(i->live_nodes.end()); k != end; ++k) + { + if (k->confirmed()) ++confirmed; + } + + replacements += i->replacements.size(); + } + return boost::make_tuple(nodes, replacements, confirmed); +} + +size_type routing_table::num_global_nodes() const +{ + int deepest_bucket = 0; + int deepest_size = 0; + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + deepest_size = i->live_nodes.size(); // + i->replacements.size(); + if (deepest_size < m_bucket_size) break; + // this bucket is full + ++deepest_bucket; + } + + if (deepest_bucket == 0) return 1 + deepest_size; + + if (deepest_size < m_bucket_size / 2) return (size_type(1) << deepest_bucket) * m_bucket_size; + else return (size_type(2) << deepest_bucket) * deepest_size; +} + +int routing_table::depth() const +{ + if (m_depth >= int(m_buckets.size())) + m_depth = m_buckets.size() - 1; + + if (m_depth < 0) return m_depth; + + // maybe the table is deeper now? + while (m_depth < int(m_buckets.size())-1 + && int(m_buckets[m_depth+1].live_nodes.size()) >= m_bucket_size / 2) + { + ++m_depth; + } + + // maybe the table is more shallow now? + while (m_depth > 0 + && int(m_buckets[m_depth-1].live_nodes.size()) < m_bucket_size / 2) + { + --m_depth; + } + + return m_depth; +} + +#if (defined TORRENT_DHT_VERBOSE_LOGGING || defined TORRENT_DEBUG) && TORRENT_USE_IOSTREAM + +void routing_table::print_state(std::ostream& os) const +{ + os << "kademlia routing table state\n" + << "bucket_size: " << m_bucket_size << "\n" + << "global node count: " << num_global_nodes() << "\n" + << "node_id: " << m_id << "\n\n"; + + os << "number of nodes per bucket:\n"; + + int idx = 0; + + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++idx) + { + os << std::setw(2) << idx << ": "; + for (int k = 0; k < int(i->live_nodes.size()); ++k) + os << "#"; + for (int k = 0; k < int(i->replacements.size()); ++k) + os << "-"; + os << "\n"; + } + + ptime now = time_now(); + + os << "\nnodes:"; + int bucket_index = 0; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++bucket_index) + { + os << "\n=== BUCKET == " << bucket_index + << " == " << i->live_nodes.size() << "|" << i->replacements.size() + << " ===== \n"; + + int id_shift; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id_shift = bucket_index; + else + id_shift = bucket_index + 1; + + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + int bucket_size_limit = bucket_limit(bucket_index); + boost::uint32_t top_mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); + while ((top_mask & 0x80) == 0) + { + top_mask <<= 1; + ++mask_shift; + } + top_mask = (0xff << mask_shift) & 0xff; + + node_id id = j->id; + id <<= id_shift; + + os << " prefx: " << std::setw(2) << std::hex << ((id[0] & top_mask) >> mask_shift) << std::dec + << " id: " << j->id; + if (j->rtt == 0xffff) + os << " rtt: "; + else + os << " rtt: " << std::setw(4) << j->rtt; + + os << " fail: " << j->fail_count() + << " ping: " << j->pinged() + << " dist: " << std::setw(3) << distance_exp(m_id, j->id); + + if (j->last_queried == min_time()) + os << " query: "; + else + os << " query: " << std::setw(3) << total_seconds(now - j->last_queried); + + os << " ip: " << j->ep() + << "\n"; + } + } + + os << "\nnode spread per bucket:\n"; + bucket_index = 0; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i, ++bucket_index) + { + int bucket_size_limit = bucket_limit(bucket_index); + + // mask out the first 3 bits, or more depending + // on the bucket_size_limit + // we have all the lower bits set in (bucket_size_limit-1) + // but we want the left-most bits to be set. Shift it + // until the MSB is set + boost::uint32_t top_mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(bucket_size_limit > 0, bucket_size_limit); + while ((top_mask & 0x80) == 0) + { + top_mask <<= 1; + ++mask_shift; + } + top_mask = (0xff << mask_shift) & 0xff; + bucket_size_limit = (top_mask >> mask_shift) + 1; + TORRENT_ASSERT_VAL(bucket_size_limit <= 256, bucket_size_limit); + bool sub_buckets[256]; + memset(sub_buckets, 0, sizeof(sub_buckets)); + + int id_shift; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id_shift = bucket_index; + else + id_shift = bucket_index + 1; + + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + node_id id = j->id; + id <<= id_shift; + int b = (id[0] & top_mask) >> mask_shift; + TORRENT_ASSERT(b >= 0 && b < int(sizeof(sub_buckets)/sizeof(sub_buckets[0]))); + sub_buckets[b] = true; + } + + os << std::dec << std::setw(2) << bucket_index << " mask: " << std::setw(2) + << std::hex << (top_mask >> mask_shift) << ": ["; + + for (int i = 0; i < bucket_size_limit; ++i) os << (sub_buckets[i] ? "X" : " "); + os << "]\n"; + } +} + +#endif + +node_entry const* routing_table::next_refresh() +{ + // find the node with the least recent 'last_queried' field. if it's too + // recent, return false. Otherwise return a random target ID that's close to + // a missing prefix for that bucket + + node_entry* candidate = NULL; + int bucket_idx = -1; + + // this will have a bias towards pinging nodes close to us first. + int idx = m_buckets.size() - 1; + for (table_t::reverse_iterator i = m_buckets.rbegin() + , end(m_buckets.rend()); i != end; ++i, --idx) + { + for (bucket_t::iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + { + // this shouldn't happen + TORRENT_ASSERT(m_id != j->id); + if (j->id == m_id) continue; + + if (j->last_queried == min_time()) + { + bucket_idx = idx; + candidate = &*j; + goto out; + } + + if (candidate == NULL || j->last_queried < candidate->last_queried) + { + candidate = &*j; + bucket_idx = idx; + } + } + } +out: + + // make sure we don't pick the same node again next time we want to refresh + // the routing table + if (candidate) + candidate->last_queried = time_now(); + + return candidate; +} + +void routing_table::replacement_cache(bucket_t& nodes) const +{ + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + std::copy(i->replacements.begin(), i->replacements.end() + , std::back_inserter(nodes)); + } +} + +routing_table::table_t::iterator routing_table::find_bucket(node_id const& id) +{ +// TORRENT_ASSERT(id != m_id); + + int num_buckets = m_buckets.size(); + if (num_buckets == 0) + { + m_buckets.push_back(routing_table_node()); + ++num_buckets; + } + + int bucket_index = (std::min)(159 - distance_exp(m_id, id), num_buckets - 1); + TORRENT_ASSERT(bucket_index < int(m_buckets.size())); + TORRENT_ASSERT(bucket_index >= 0); + + table_t::iterator i = m_buckets.begin(); + std::advance(i, bucket_index); + return i; +} + +bool compare_ip_cidr(node_entry const& lhs, node_entry const& rhs) +{ + TORRENT_ASSERT(lhs.addr().is_v4() == rhs.addr().is_v4()); + // the number of bits in the IPs that may match. If + // more bits that this matches, something suspicious is + // going on and we shouldn't add the second one to our + // routing table + int cutoff = rhs.addr().is_v4() ? 8 : 64; + int dist = cidr_distance(lhs.addr(), rhs.addr()); + return dist <= cutoff; +} + +node_entry* routing_table::find_node(udp::endpoint const& ep + , routing_table::table_t::iterator* bucket) +{ + for (table_t::iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + for (bucket_t::iterator j = i->replacements.begin(); + j != i->replacements.end(); ++j) + { + if (j->addr() != ep.address()) continue; + if (j->port() != ep.port()) continue; + *bucket = i; + return &*j; + } + for (bucket_t::iterator j = i->live_nodes.begin(); + j != i->live_nodes.end(); ++j) + { + if (j->addr() != ep.address()) continue; + if (j->port() != ep.port()) continue; + *bucket = i; + return &*j; + } + } + *bucket = m_buckets.end(); + return 0; +} + +void routing_table::remove_node(node_entry* n + , routing_table::table_t::iterator bucket) +{ + INVARIANT_CHECK; + + if (!bucket->replacements.empty() + && n >= &bucket->replacements[0] + && n < &bucket->replacements[0] + bucket->replacements.size()) + { + int idx = n - &bucket->replacements[0]; + TORRENT_ASSERT(m_ips.count(n->a) > 0); + erase_one(m_ips, n->a); + bucket->replacements.erase(bucket->replacements.begin() + idx); + } + + if (!bucket->live_nodes.empty() + && n >= &bucket->live_nodes[0] + && n < &bucket->live_nodes[0] + bucket->live_nodes.size()) + { + int idx = n - &bucket->live_nodes[0]; + TORRENT_ASSERT(m_ips.count(n->a) > 0); + erase_one(m_ips, n->a); + bucket->live_nodes.erase(bucket->live_nodes.begin() + idx); + } +} + +bool routing_table::add_node(node_entry e) +{ + add_node_status_t s = add_node_impl(e); + if (s == failed_to_add) return false; + if (s == node_added) return true; + + while (s == need_bucket_split) + { + split_bucket(); + + // if this assert triggers a lot in the wild, we should probably + // harden our resistence towards this attack. Perhaps by never + // splitting a bucket (and discard nodes) if the two buckets above it + // are empty or close to empty + TORRENT_ASSERT(m_buckets.size() <= 50); + if (m_buckets.size() > 50) + { + // this is a sanity check. In the wild, we shouldn't see routing + // tables deeper than 26 or 27. If we get this deep, there might + // be a bug in the bucket splitting logic, or there may be someone + // playing a prank on us, spoofing node IDs. + s = add_node_impl(e); + if (s == node_added) return true; + return false; + } + + // if the new bucket still has too many nodes in it, we need to keep + // splitting + if (m_buckets.back().live_nodes.size() > bucket_limit(m_buckets.size()-1)) + continue; + + s = add_node_impl(e); + if (s == failed_to_add) return false; + if (s == node_added) return true; + } + return false; +} + +routing_table::add_node_status_t routing_table::add_node_impl(node_entry e) +{ + INVARIANT_CHECK; + + // if we already have this (IP,port), don't do anything + if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) + return failed_to_add; + + // don't add ourself + if (e.id == m_id) return failed_to_add; + + // do we already have this IP in the table? + if (m_ips.count(e.addr().to_v4().to_bytes()) > 0) + { + // this exact IP already exists in the table. It might be the case + // that the node changed IP. If pinged is true, and the port also + // matches then we assume it's in fact the same node, and just update + // the routing table + // pinged means that we have sent a message to the IP, port and received + // a response with a correct transaction ID, i.e. it is verified to not + // be the result of a poisoned routing table + + table_t::iterator existing_bucket; + node_entry* existing = find_node(e.ep(), &existing_bucket); + if (!e.pinged() || existing == 0) + { + // the new node is not pinged, or it's not an existing node + // we should ignore it, unless we allow duplicate IPs in our + // routing table + if (m_settings.restrict_routing_ips) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring node (duplicate IP): " + << e.id << " " << e.addr(); +#endif + return failed_to_add; + } + } + else if (existing && existing->id == e.id) + { + // if the node ID is the same, just update the failcount + // and be done with it + existing->timeout_count = 0; + existing->update_rtt(e.rtt); + existing->last_queried = e.last_queried; + return node_added; + } + else if (existing) + { + TORRENT_ASSERT(existing->id != e.id); + // this is the same IP and port, but with + // a new node ID. remove the old entry and + // replace it with this new ID + remove_node(existing, existing_bucket); + } + } + + table_t::iterator i = find_bucket(e.id); + bucket_t& b = i->live_nodes; + bucket_t& rb = i->replacements; + int bucket_index = std::distance(m_buckets.begin(), i); + int bucket_size_limit = bucket_limit(bucket_index); + + bucket_t::iterator j; + + // if the node already exists, we don't need it + j = std::find_if(b.begin(), b.end() + , boost::bind(&node_entry::id, _1) == e.id); + + if (j != b.end()) + { + // a new IP address just claimed this node-ID + // ignore it + if (j->addr() != e.addr() || j->port() != e.port()) + return failed_to_add; + + // we already have the node in our bucket + TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); + j->timeout_count = 0; + j->update_rtt(e.rtt); +// TORRENT_LOG(table) << "updating node: " << i->id << " " << i->addr(); + return node_added; + } + + // if this node exists in the replacement bucket. update it and + // pull it out from there. We may add it back to the replacement + // bucket, but we may also replace a node in the main bucket, now + // that we have an updated RTT + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id); + if (j != rb.end()) + { + // a new IP address just claimed this node-ID + // ignore it + if (j->addr() != e.addr() || j->port() != e.port()) + return failed_to_add; + + TORRENT_ASSERT(j->id == e.id && j->ep() == e.ep()); + j->timeout_count = 0; + j->update_rtt(e.rtt); + e = *j; + erase_one(m_ips, j->addr().to_v4().to_bytes()); + rb.erase(j); + } + + if (m_settings.restrict_routing_ips) + { + // don't allow multiple entries from IPs very close to each other + j = std::find_if(b.begin(), b.end(), boost::bind(&compare_ip_cidr, _1, e)); + if (j != b.end()) + { + // we already have a node in this bucket with an IP very + // close to this one. We know that it's not the same, because + // it claims a different node-ID. Ignore this to avoid attacks +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring node: " << e.id << " " << e.addr() + << " existing node: " + << j->id << " " << j->addr(); +#endif + return failed_to_add; + } + + j = std::find_if(rb.begin(), rb.end(), boost::bind(&compare_ip_cidr, _1, e)); + if (j != rb.end()) + { + // same thing but for the replacement bucket +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "ignoring (replacement) node: " << e.id << " " << e.addr() + << " existing node: " + << j->id << " " << j->addr(); +#endif + return failed_to_add; + } + } + + // if there's room in the main bucket, just insert it + if (int(b.size()) < bucket_size_limit) + { + if (b.empty()) b.reserve(bucket_size_limit); + b.push_back(e); + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "inserting node: " << e.id << " " << e.addr(); + return node_added; + } + + // if there is no room, we look for nodes that are not 'pinged', + // i.e. we haven't confirmed that they respond to messages. + // Then we look for nodes marked as stale + // in the k-bucket. If we find one, we can replace it. + // then we look for nodes with the same 3 bit prefix (or however + // many bits prefix the bucket size warrants). If there is no other + // node with this prefix, remove the duplicate with the highest RTT. + // as the last replacement strategy, if the node we found matching our + // bit prefix has higher RTT than the new node, replace it. + + // can we split the bucket? + // only nodes that haven't failed can split the bucket, and we can only + // split the last bucket + bool can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 159) + && e.fail_count() == 0; + + if (e.pinged() && e.fail_count() == 0) + { + // if the node we're trying to insert is considered pinged, + // we may replace other nodes that aren't pinged + + j = std::find_if(b.begin(), b.end(), boost::bind(&node_entry::pinged, _1) == false); + + if (j != b.end() && !j->pinged()) + { + // j points to a node that has not been pinged. + // Replace it with this new one + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "replacing unpinged node: " << e.id << " " << e.addr(); + return node_added; + } + + // A node is considered stale if it has failed at least one + // time. Here we choose the node that has failed most times. + // If we don't find one, place this node in the replacement- + // cache and replace any nodes that will fail in the future + // with nodes from that cache. + + j = std::max_element(b.begin(), b.end() + , boost::bind(&node_entry::fail_count, _1) + < boost::bind(&node_entry::fail_count, _2)); + TORRENT_ASSERT(j != b.end()); + + if (j->fail_count() > 0) + { + // i points to a node that has been marked + // as stale. Replace it with this new one + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "replacing stale node: " << e.id << " " << e.addr(); + return node_added; + } + + // in order to provide as few lookups as possible before finding + // the data someone is looking for, make sure there is an affinity + // towards having a good spread of node IDs in each bucket + + boost::uint32_t mask = bucket_size_limit - 1; + int mask_shift = 0; + TORRENT_ASSERT_VAL(mask > 0, mask); + while ((mask & 0x80) == 0) + { + mask <<= 1; + ++mask_shift; + } + + // in case bucket_size_limit is not an even power of 2 + mask = (0xff << mask_shift) & 0xff; + + node_id id = e.id; + // the last bucket is special, since it hasn't been split yet, it + // includes that top bit as well + if (bucket_index + 1 == m_buckets.size()) + id <<= bucket_index; + else + id <<= bucket_index + 1; + + // pick out all nodes that have the same prefix as the new node + std::vector nodes; + bool force_replace = false; + for (j = b.begin(); j != b.end(); ++j) + { + if (!matching_prefix(*j, mask, id[0] & mask, bucket_index)) continue; + nodes.push_back(j); + } + + if (!nodes.empty()) + { + j = *std::max_element(nodes.begin(), nodes.end() + , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) + < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); + } + else + { + // there is no node in this prefix-slot, there may be some + // nodes sharing a prefix. Find all nodes that do not + // have a unique prefix + + // find node entries with duplicate prefixes in O(1) + std::vector prefix(1 << (8 - mask_shift), b.end()); + TORRENT_ASSERT(int(prefix.size()) >= bucket_size_limit); + + // the begin iterator from this object is used as a placeholder + // for an occupied slot whose node has already been added to the + // duplicate nodes list. + bucket_t placeholder; + + nodes.reserve(b.size()); + for (j = b.begin(); j != b.end(); ++j) + { + node_id id = j->id; + id <<= bucket_index + 1; + int this_prefix = (id[0] & mask) >> mask_shift; + TORRENT_ASSERT(this_prefix >= 0); + TORRENT_ASSERT(this_prefix < int(prefix.size())); + if (prefix[this_prefix] != b.end()) + { + // there's already a node with this prefix. Remember both + // duplicates. + nodes.push_back(j); + + if (prefix[this_prefix] != placeholder.begin()) + { + nodes.push_back(prefix[this_prefix]); + prefix[this_prefix] = placeholder.begin(); + } + } + } + + if (!nodes.empty()) + { + // from these nodes, pick the one with the highest RTT + // and replace it + + std::vector::iterator k = std::max_element(nodes.begin(), nodes.end() + , boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _1)) + < boost::bind(&node_entry::rtt, boost::bind(&bucket_t::iterator::operator*, _2))); + + // in this case, we would really rather replace the node even if + // the new node has higher RTT, becase it fills a new prefix that we otherwise + // don't have. + force_replace = true; + j = *k; + } + else + { + j = std::max_element(b.begin(), b.end() + , boost::bind(&node_entry::rtt, _1) + < boost::bind(&node_entry::rtt, _2)); + } + } + + if (j != b.end() && (force_replace || j->rtt > e.rtt)) + { + erase_one(m_ips, j->addr().to_v4().to_bytes()); + *j = e; + m_ips.insert(e.addr().to_v4().to_bytes()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << "replacing node with higher RTT: " << e.id + << " " << e.addr(); +#endif + return node_added; + } + // in order to keep lookup times small, prefer nodes with low RTTs + + } + + // if we can't split, try to insert into the replacement bucket + + if (!can_split) + { + // if we don't have any identified stale nodes in + // the bucket, and the bucket is full, we have to + // cache this node and wait until some node fails + // and then replace it. + + j = std::find_if(rb.begin(), rb.end() + , boost::bind(&node_entry::id, _1) == e.id); + + // if the node is already in the replacement bucket + // just return. + if (j != rb.end()) + { + // if the IP address matches, it's the same node + // make sure it's marked as pinged + if (j->ep() == e.ep()) j->set_pinged(); + return node_added; + } + + if ((int)rb.size() >= m_bucket_size) + { + // if the replacement bucket is full, remove the oldest entry + // but prefer nodes that haven't been pinged, since they are + // less reliable than this one, that has been pinged + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1) == false); + if (j == rb.end()) j = rb.begin(); + erase_one(m_ips, j->addr().to_v4().to_bytes()); + rb.erase(j); + } + + if (rb.empty()) rb.reserve(m_bucket_size); + rb.push_back(e); + m_ips.insert(e.addr().to_v4().to_bytes()); +// TORRENT_LOG(table) << "inserting node in replacement cache: " << e.id << " " << e.addr(); + return node_added; + } + + return need_bucket_split; +} + +void routing_table::split_bucket() +{ + INVARIANT_CHECK; + + int bucket_index = m_buckets.size()-1; + int bucket_size_limit = bucket_limit(bucket_index); + TORRENT_ASSERT(int(m_buckets.back().live_nodes.size()) >= bucket_size_limit); + + // this is the last bucket, and it's full already. Split + // it by adding another bucket + m_buckets.push_back(routing_table_node()); + bucket_t& new_bucket = m_buckets.back().live_nodes; + bucket_t& new_replacement_bucket = m_buckets.back().replacements; + + bucket_t& b = m_buckets[bucket_index].live_nodes; + bucket_t& rb = m_buckets[bucket_index].replacements; + + // move any node whose (160 - distane_exp(m_id, id)) >= (i - m_buckets.begin()) + // to the new bucket + int new_bucket_size = bucket_limit(bucket_index + 1); + for (bucket_t::iterator j = b.begin(); j != b.end();) + { + if (distance_exp(m_id, j->id) >= 159 - bucket_index) + { + ++j; + continue; + } + // this entry belongs in the new bucket + new_bucket.push_back(*j); + j = b.erase(j); + } + + if (b.size() > bucket_size_limit) + { + // TODO: 3 move the lowest priority nodes to the replacement bucket + for (bucket_t::iterator i = b.begin() + bucket_size_limit + , end(b.end()); i != end; ++i) + { + rb.push_back(*i); + } + + b.resize(bucket_size_limit); + } + + // split the replacement bucket as well. If the live bucket + // is not full anymore, also move the replacement entries + // into the main bucket + for (bucket_t::iterator j = rb.begin(); j != rb.end();) + { + if (distance_exp(m_id, j->id) >= 159 - bucket_index) + { + if (int(b.size()) >= bucket_size_limit) + { + ++j; + continue; + } + b.push_back(*j); + } + else + { + // this entry belongs in the new bucket + if (int(new_bucket.size()) < new_bucket_size) + new_bucket.push_back(*j); + else + new_replacement_bucket.push_back(*j); + } + j = rb.erase(j); + } +} + +void routing_table::for_each_node( + void (*fun1)(void*, node_entry const&) + , void (*fun2)(void*, node_entry const&) + , void* userdata) const +{ + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + if (fun1) + { + for (bucket_t::const_iterator j = i->live_nodes.begin() + , end(i->live_nodes.end()); j != end; ++j) + fun1(userdata, *j); + } + if (fun2) + { + for (bucket_t::const_iterator j = i->replacements.begin() + , end(i->replacements.end()); j != end; ++j) + fun2(userdata, *j); + } + } +} + +void routing_table::node_failed(node_id const& nid, udp::endpoint const& ep) +{ + INVARIANT_CHECK; + + // if messages to ourself fails, ignore it + if (nid == m_id) return; + + table_t::iterator i = find_bucket(nid); + bucket_t& b = i->live_nodes; + bucket_t& rb = i->replacements; + + bucket_t::iterator j = std::find_if(b.begin(), b.end() + , boost::bind(&node_entry::id, _1) == nid); + + if (j == b.end()) + { + j = std::find_if(rb.begin(), rb.end() + , boost::bind(&node_entry::id, _1) == nid); + + if (j == rb.end() + || j->ep() != ep) return; + + j->timed_out(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << " NODE FAILED" + " id: " << nid << + " ip: " << j->ep() << + " fails: " << j->fail_count() << + " pinged: " << j->pinged() << + " up-time: " << total_seconds(time_now() - j->first_seen); +#endif + return; + } + + // if the endpoint doesn't match, it's a different node + // claiming the same ID. The node we have in our routing + // table is not necessarily stale + if (j->ep() != ep) return; + + if (rb.empty()) + { + j->timed_out(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(table) << " NODE FAILED" + " id: " << nid << + " ip: " << j->ep() << + " fails: " << j->fail_count() << + " pinged: " << j->pinged() << + " up-time: " << total_seconds(time_now() - j->first_seen); +#endif + + // if this node has failed too many times, or if this node + // has never responded at all, remove it + if (j->fail_count() >= m_settings.max_fail_count || !j->pinged()) + { + erase_one(m_ips, j->addr().to_v4().to_bytes()); + b.erase(j); + } + return; + } + + erase_one(m_ips, j->a); + b.erase(j); + + // sort by RTT first, to find the node with the lowest + // RTT that is pinged + std::sort(rb.begin(), rb.end() + , boost::bind(&node_entry::rtt, _1) < boost::bind(&node_entry::rtt, _2)); + + j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1)); + if (j == rb.end()) j = rb.begin(); + b.push_back(*j); + rb.erase(j); +} + +void routing_table::add_router_node(udp::endpoint router) +{ + m_router_nodes.insert(router); +} + +// we heard from this node, but we don't know if it was spoofed or not (i.e. +// pinged == false) +void routing_table::heard_about(node_id const& id, udp::endpoint const& ep) +{ + add_node(node_entry(id, ep)); +} + +// this function is called every time the node sees a sign of a node being +// alive. This node will either be inserted in the k-buckets or be moved to the +// top of its bucket. the return value indicates if the table needs a refresh. +// if true, the node should refresh the table (i.e. do a find_node on its own +// id) +bool routing_table::node_seen(node_id const& id, udp::endpoint ep, int rtt) +{ + return add_node(node_entry(id, ep, rtt, true)); +} + +// fills the vector with the k nodes from our buckets that +// are nearest to the given id. +void routing_table::find_node(node_id const& target + , std::vector& l, int options, int count) +{ + l.clear(); + if (count == 0) count = m_bucket_size; + + table_t::iterator i = find_bucket(target); + int bucket_index = std::distance(m_buckets.begin(), i); + int bucket_size_limit = bucket_limit(bucket_index); + + l.reserve(bucket_size_limit); + + table_t::iterator j = i; + + int unsorted_start_idx = 0; + for (; j != m_buckets.end() && int(l.size()) < count; ++j) + { + bucket_t& b = j->live_nodes; + if (options & include_failed) + { + copy(b.begin(), b.end() + , std::back_inserter(l)); + } + else + { + std::remove_copy_if(b.begin(), b.end() + , std::back_inserter(l) + , !boost::bind(&node_entry::confirmed, _1)); + } + + if (int(l.size()) == count) return; + + if (int(l.size()) > count) + { + // sort the nodes by how close they are to the target + std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref + , boost::bind(&node_entry::id, _1) + , boost::bind(&node_entry::id, _2), target)); + + l.resize(count); + return; + } + unsorted_start_idx = int(l.size()); + } + + // if we still don't have enough nodes, copy nodes + // further away from us + + if (i == m_buckets.begin()) + return; + + j = i; + + unsorted_start_idx = int(l.size()); + do + { + --j; + bucket_t& b = j->live_nodes; + + if (options & include_failed) + { + std::copy(b.begin(), b.end(), std::back_inserter(l)); + } + else + { + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , !boost::bind(&node_entry::confirmed, _1)); + } + + if (int(l.size()) == count) return; + + if (int(l.size()) > count) + { + // sort the nodes by how close they are to the target + std::sort(l.begin() + unsorted_start_idx, l.end(), boost::bind(&compare_ref + , boost::bind(&node_entry::id, _1) + , boost::bind(&node_entry::id, _2), target)); + + l.resize(count); + return; + } + unsorted_start_idx = int(l.size()); + } + while (j != m_buckets.begin() && int(l.size()) < count); + + TORRENT_ASSERT(int(l.size()) <= count); +} + +#if TORRENT_USE_INVARIANT_CHECKS +void routing_table::check_invariant() const +{ + std::multiset all_ips; + + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + for (bucket_t::const_iterator j = i->replacements.begin(); + j != i->replacements.end(); ++j) + { + all_ips.insert(j->addr().to_v4().to_bytes()); + } + for (bucket_t::const_iterator j = i->live_nodes.begin(); + j != i->live_nodes.end(); ++j) + { + all_ips.insert(j->addr().to_v4().to_bytes()); + } + } + + TORRENT_ASSERT(all_ips == m_ips); +} +#endif + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp new file mode 100644 index 0000000000..4ab8144b78 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/rpc_manager.cpp @@ -0,0 +1,522 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" + +#include + +#include +#include +#include +#include // for generate_random_id +#include +#include +#include +#include +#include +#include +#include +#include +#include // for dht_settings +#include +#include // time() + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +#include +#endif + +namespace libtorrent { namespace dht +{ + +namespace io = libtorrent::detail; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(rpc) +#endif + +void intrusive_ptr_add_ref(observer const* o) +{ + TORRENT_ASSERT(o != 0); + TORRENT_ASSERT(o->m_refs >= 0); + ++o->m_refs; +} + +void intrusive_ptr_release(observer const* o) +{ + TORRENT_ASSERT(o != 0); + TORRENT_ASSERT(o->m_refs > 0); + if (--o->m_refs == 0) + { + boost::intrusive_ptr ta = o->m_algorithm; + (const_cast(o))->~observer(); + ta->free_observer(const_cast(o)); + } +} + +void observer::set_target(udp::endpoint const& ep) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + // use high resolution timers for logging + m_sent = time_now_hires(); +#else + m_sent = time_now(); +#endif + + m_port = ep.port(); +#if TORRENT_USE_IPV6 + if (ep.address().is_v6()) + { + flags |= flag_ipv6_address; + m_addr.v6 = ep.address().to_v6().to_bytes(); + } + else +#endif + { + flags &= ~flag_ipv6_address; + m_addr.v4 = ep.address().to_v4().to_bytes(); + } +} + +address observer::target_addr() const +{ +#if TORRENT_USE_IPV6 + if (flags & flag_ipv6_address) + return address_v6(m_addr.v6); + else +#endif + return address_v4(m_addr.v4); +} + +udp::endpoint observer::target_ep() const +{ + return udp::endpoint(target_addr(), m_port); +} + +void observer::abort() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->failed(observer_ptr(this), traversal_algorithm::prevent_request); +} + +void observer::done() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->finished(observer_ptr(this)); +} + +void observer::short_timeout() +{ + if (flags & flag_short_timeout) return; + m_algorithm->failed(observer_ptr(this), traversal_algorithm::short_timeout); +} + +// this is called when no reply has been received within +// some timeout +void observer::timeout() +{ + if (flags & flag_done) return; + flags |= flag_done; + m_algorithm->failed(observer_ptr(this)); +} + +void observer::set_id(node_id const& id) +{ + if (m_id == id) return; + m_id = id; + if (m_algorithm) m_algorithm->resort_results(); +} + +enum { observer_size = max3< + sizeof(find_data_observer) + , sizeof(announce_observer) + , sizeof(null_observer) + >::value +}; + +rpc_manager::rpc_manager(node_id const& our_id + , routing_table& table, udp_socket_interface* sock) + : m_pool_allocator(observer_size, 10) + , m_sock(sock) + , m_table(table) + , m_timer(time_now()) + , m_our_id(our_id) + , m_allocated_observers(0) + , m_destructing(false) +{ + std::srand((unsigned int)time(0)); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Constructing"; + +#define PRINT_OFFSETOF(x, y) TORRENT_LOG(rpc) << " +" << offsetof(x, y) << ": " #y + + TORRENT_LOG(rpc) << " observer: " << sizeof(observer); + PRINT_OFFSETOF(dht::observer, m_sent); + PRINT_OFFSETOF(dht::observer, m_refs); + PRINT_OFFSETOF(dht::observer, m_algorithm); + PRINT_OFFSETOF(dht::observer, m_id); + PRINT_OFFSETOF(dht::observer, m_addr); + PRINT_OFFSETOF(dht::observer, m_port); + PRINT_OFFSETOF(dht::observer, m_transaction_id); + PRINT_OFFSETOF(dht::observer, flags); + + TORRENT_LOG(rpc) << " announce_observer: " << sizeof(announce_observer); + TORRENT_LOG(rpc) << " null_observer: " << sizeof(null_observer); + TORRENT_LOG(rpc) << " find_data_observer: " << sizeof(find_data_observer); + +#undef PRINT_OFFSETOF +#endif + +} + +rpc_manager::~rpc_manager() +{ + TORRENT_ASSERT(!m_destructing); + m_destructing = true; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Destructing"; +#endif + + for (transactions_t::iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end; ++i) + { + (*i)->abort(); + } +} + +void* rpc_manager::allocate_observer() +{ + m_pool_allocator.set_next_size(10); + void* ret = m_pool_allocator.malloc(); + if (ret) ++m_allocated_observers; + return ret; +} + +void rpc_manager::free_observer(void* ptr) +{ + if (!ptr) return; + --m_allocated_observers; + TORRENT_ASSERT(reinterpret_cast(ptr)->m_in_use == false); + m_pool_allocator.free(ptr); +} + +#if TORRENT_USE_ASSERTS +size_t rpc_manager::allocation_size() const +{ + return observer_size; +} +#endif +#if TORRENT_USE_INVARIANT_CHECKS +void rpc_manager::check_invariant() const +{ + for (transactions_t::const_iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end; ++i) + { + TORRENT_ASSERT(*i); + } +} +#endif + +void rpc_manager::unreachable(udp::endpoint const& ep) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << time_now_string() << " PORT_UNREACHABLE [ ip: " << ep << " ]"; +#endif + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end();) + { + TORRENT_ASSERT(*i); + observer_ptr const& o = *i; + if (o->target_ep() != ep) { ++i; continue; } + observer_ptr ptr = *i; + i = m_transactions.erase(i); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << " found transaction [ tid: " << ptr->transaction_id() << " ]"; +#endif + ptr->timeout(); + break; + } +} + +// defined in node.cpp +void incoming_error(entry& e, char const* msg, int error_code = 203); + +bool rpc_manager::incoming(msg const& m, node_id* id, libtorrent::dht_settings const& settings) +{ + INVARIANT_CHECK; + + if (m_destructing) return false; + + // we only deal with replies and errors, not queries + TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" + || m.message.dict_find_string_value("y") == "e"); + + // if we don't have the transaction id in our + // request list, ignore the packet + + std::string transaction_id = m.message.dict_find_string_value("t"); + if (transaction_id.empty()) return false; + + std::string::const_iterator i = transaction_id.begin(); + int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); + + observer_ptr o; + + for (transactions_t::iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end;) + { + TORRENT_ASSERT(*i); + if ((*i)->transaction_id() != tid + || m.addr.address() != (*i)->target_addr()) + { + ++i; + continue; + } + o = *i; + i = m_transactions.erase(i); + break; + } + + if (!o) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with unknown transaction id size: " + << transaction_id.size() << " from " << m.addr; +#endif + // this isn't necessarily because the other end is doing + // something wrong. This can also happen when we restart + // the node, and we prematurely abort all outstanding + // requests. Also, this opens up a potential magnification + // attack. +// entry e; +// incoming_error(e, "invalid transaction id"); +// m_sock->send_packet(e, m.addr, 0); + return false; + } + + ptime now = time_now_hires(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::ofstream reply_stats("round_trip_ms.log", std::ios::app); + reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent()) + << std::endl; +#endif + + lazy_entry const* ret_ent = m.message.dict_find_dict("r"); + if (ret_ent == 0) + { + // it may be an error + ret_ent = m.message.dict_find("e"); + o->timeout(); + if (ret_ent == NULL) + { + entry e; + incoming_error(e, "missing 'r' key"); + m_sock->send_packet(e, m.addr, 0); + } + return false; + } + + lazy_entry const* node_id_ent = ret_ent->dict_find_string("id"); + if (node_id_ent == 0 || node_id_ent->string_length() != 20) + { + o->timeout(); + entry e; + incoming_error(e, "missing 'id' key"); + m_sock->send_packet(e, m.addr, 0); + return false; + } + + node_id nid = node_id(node_id_ent->string_ptr()); + if (settings.enforce_node_id && !verify_id(nid, m.addr.address())) + { + o->timeout(); + entry e; + incoming_error(e, "invalid node ID"); + m_sock->send_packet(e, m.addr, 0); + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: " + << tid << " from " << m.addr; +#endif + o->reply(m); + *id = nid; + + int rtt = int(total_milliseconds(now - o->sent())); + + // we found an observer for this reply, hence the node is not spoofing + // add it to the routing table + return m_table.node_seen(*id, m.addr, rtt); +} + +time_duration rpc_manager::tick() +{ + INVARIANT_CHECK; + + const static int short_timeout = 1; + const static int timeout = 15; + + // look for observers that have timed out + + if (m_transactions.empty()) return seconds(short_timeout); + + std::list timeouts; + + time_duration ret = seconds(short_timeout); + ptime now = time_now(); + +#if TORRENT_USE_ASSERTS + ptime last = min_time(); + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end(); ++i) + { + TORRENT_ASSERT((*i)->sent() >= last); + last = (*i)->sent(); + } +#endif + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end();) + { + observer_ptr o = *i; + + // if we reach an observer that hasn't timed out + // break, because every observer after this one will + // also not have timed out yet + time_duration diff = now - o->sent(); + if (diff < seconds(timeout)) + { + ret = seconds(timeout) - diff; + break; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Timing out transaction id: " + << (*i)->transaction_id() << " from " << o->target_ep(); +#endif + i = m_transactions.erase(i); + timeouts.push_back(o); + } + + std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::timeout, _1)); + timeouts.clear(); + + for (transactions_t::iterator i = m_transactions.begin(); + i != m_transactions.end(); ++i) + { + observer_ptr o = *i; + + // if we reach an observer that hasn't timed out + // break, because every observer after this one will + // also not have timed out yet + time_duration diff = now - o->sent(); + if (diff < seconds(short_timeout)) + { + ret = seconds(short_timeout) - diff; + break; + } + + // don't call short_timeout() again if we've + // already called it once + if (o->has_short_timeout()) continue; + + timeouts.push_back(o); + } + + std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::short_timeout, _1)); + + return ret; +} + +void rpc_manager::add_our_id(entry& e) +{ + e["id"] = m_our_id.to_string(); +} + +bool rpc_manager::invoke(entry& e, udp::endpoint target_addr + , observer_ptr o) +{ + INVARIANT_CHECK; + + if (m_destructing) return false; + + e["y"] = "q"; + entry& a = e["a"]; + add_our_id(a); + + std::string transaction_id; + transaction_id.resize(2); + char* out = &transaction_id[0]; + int tid = (random() ^ (random() << 5)) & 0xffff; + io::write_uint16(tid, out); + e["t"] = transaction_id; + + o->set_target(target_addr); + o->set_transaction_id(tid); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] invoking " + << e["q"].string() << " -> " << target_addr; +#endif + + if (m_sock->send_packet(e, target_addr, 1)) + { + m_transactions.push_back(o); +#if TORRENT_USE_ASSERTS + o->m_was_sent = true; +#endif + return true; + } + return false; +} + +observer::~observer() +{ + // if the message was sent, it must have been + // reported back to the traversal_algorithm as + // well. If it wasn't sent, it cannot have been + // reported back + TORRENT_ASSERT(m_was_sent == bool(flags & flag_done) || m_was_abandoned); + TORRENT_ASSERT(!m_in_constructor); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_in_use); + m_in_use = false; +#endif +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp b/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp new file mode 100644 index 0000000000..10e65a12c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/kademlia/traversal_algorithm.cpp @@ -0,0 +1,593 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/time.hpp" // for total_seconds + +#include +#include +#include +#include +#include +#include "libtorrent/broadcast_socket.hpp" // for cidr_distance +#include // for read_*_endpoint + +#include + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(traversal) +#endif + +using detail::read_v4_endpoint; +#if TORRENT_USE_IPV6 +using detail::read_v6_endpoint; +#endif + +#if TORRENT_USE_ASSERTS +template +bool is_sorted(It b, It e, Cmp cmp) +{ + if (b == e) return true; + + typename std::iterator_traits::value_type v = *b; + ++b; + while (b != e) + { + if (cmp(*b, v)) return false; + v = *b; + ++b; + } + return true; +} +#endif + +observer_ptr traversal_algorithm::new_observer(void* ptr + , udp::endpoint const& ep, node_id const& id) +{ + observer_ptr o(new (ptr) null_observer(boost::intrusive_ptr(this), ep, id)); +#if TORRENT_USE_ASSERTS + o->m_in_constructor = false; +#endif + return o; +} + +traversal_algorithm::traversal_algorithm( + node_impl& node + , node_id target) + : m_ref_count(0) + , m_node(node) + , m_target(target) + , m_invoke_count(0) + , m_branch_factor(3) + , m_responses(0) + , m_timeouts(0) + , m_num_target_nodes(m_node.m_table.bucket_size()) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] NEW" + " target: " << target + << " k: " << m_node.m_table.bucket_size() + ; +#endif +} + +// returns true of lhs and rhs are too close to each other to appear +// in the same DHT search under different node IDs +bool compare_ip_cidr(observer_ptr const& lhs, observer_ptr const& rhs) +{ + if (lhs->target_addr().is_v4() != rhs->target_addr().is_v4()) + return false; + // the number of bits in the IPs that may match. If + // more bits that this matches, something suspicious is + // going on and we shouldn't add the second one to our + // routing table + int cutoff = rhs->target_addr().is_v4() ? 4 : 64; + int dist = cidr_distance(lhs->target_addr(), rhs->target_addr()); + return dist <= cutoff; +} + +void traversal_algorithm::resort_results() +{ + std::sort( + m_results.begin() + , m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target + ) + ); +} + +void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) +{ + TORRENT_ASSERT(m_node.m_rpc.allocation_size() >= sizeof(find_data_observer)); + void* ptr = m_node.m_rpc.allocate_observer(); + if (ptr == 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] failed to allocate memory for observer. aborting!"; +#endif + done(); + return; + } + observer_ptr o = new_observer(ptr, addr, id); + if (id.is_all_zeros()) + { + o->set_id(generate_random_id()); + o->flags |= observer::flag_no_id; + } + + o->flags |= flags; + + TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target) + )); + + std::vector::iterator i = std::lower_bound( + m_results.begin() + , m_results.end() + , o + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target + ) + ); + + if (i == m_results.end() || (*i)->id() != id) + { + if (m_node.settings().restrict_search_ips + && !(flags & observer::flag_initial)) + { + // don't allow multiple entries from IPs very close to each other + std::vector::iterator j = std::find_if( + m_results.begin(), m_results.end(), boost::bind(&compare_ip_cidr, _1, o)); + + if (j != m_results.end()) + { + // we already have a node in this search with an IP very + // close to this one. We know that it's not the same, because + // it claims a different node-ID. Ignore this to avoid attacks +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] IGNORING result " + << "id: " << o->id() + << " address: " << o->target_addr() + << " existing node: " + << (*j)->id() << " " << (*j)->target_addr() + << " distance: " << distance_exp(m_target, o->id()) + << " type: " << name() + ; +#endif + return; + } + } + + TORRENT_ASSERT((o->flags & observer::flag_no_id) || std::find_if(m_results.begin(), m_results.end() + , boost::bind(&observer::id, _1) == id) == m_results.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] ADD id: " << id + << " address: " << addr + << " distance: " << distance_exp(m_target, id) + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + i = m_results.insert(i, o); + + TORRENT_ASSERT(libtorrent::dht::is_sorted(m_results.begin(), m_results.end() + , boost::bind( + compare_ref + , boost::bind(&observer::id, _1) + , boost::bind(&observer::id, _2) + , m_target) + )); + } + + if (m_results.size() > 100) + { +#if TORRENT_USE_ASSERTS + for (int i = 100; i < int(m_results.size()); ++i) + m_results[i]->m_was_abandoned = true; +#endif + m_results.resize(100); + } +} + +void traversal_algorithm::start() +{ + // in case the routing table is empty, use the + // router nodes in the table + if (m_results.size() < 3) add_router_entries(); + init(); + bool is_done = add_requests(); + if (is_done) done(); +} + +void* traversal_algorithm::allocate_observer() +{ + return m_node.m_rpc.allocate_observer(); +} + +void traversal_algorithm::free_observer(void* ptr) +{ + m_node.m_rpc.free_observer(ptr); +} + +char const* traversal_algorithm::name() const +{ + return "traversal_algorithm"; +} + +void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (id.is_all_zeros()) + { + TORRENT_LOG(traversal) << time_now_string() << "[" << this << "] WARNING node returned a list which included a node with id 0"; + } +#endif + + // let the routing table know this node may exist + m_node.m_table.heard_about(id, addr); + + add_entry(id, addr, 0); +} + +void traversal_algorithm::finished(observer_ptr o) +{ +#ifdef TORRENT_DEBUG + std::vector::iterator i = std::find( + m_results.begin(), m_results.end(), o); + + TORRENT_ASSERT(i != m_results.end() || m_results.size() == 100); +#endif + + // if this flag is set, it means we increased the + // branch factor for it, and we should restore it + if (o->flags & observer::flag_short_timeout) + { + TORRENT_ASSERT(m_branch_factor > 0); + --m_branch_factor; + } + + TORRENT_ASSERT(o->flags & observer::flag_queried); + o->flags |= observer::flag_alive; + + ++m_responses; + --m_invoke_count; + TORRENT_ASSERT(m_invoke_count >= 0); + bool is_done = add_requests(); + if (is_done) done(); +} + +// prevent request means that the total number of requests has +// overflown. This query failed because it was the oldest one. +// So, if this is true, don't make another request +void traversal_algorithm::failed(observer_ptr o, int flags) +{ + TORRENT_ASSERT(m_invoke_count >= 0); + + // don't tell the routing table about + // node ids that we just generated ourself + if ((o->flags & observer::flag_no_id) == 0) + m_node.m_table.node_failed(o->id(), o->target_ep()); + + if (m_results.empty()) return; + + TORRENT_ASSERT(o->flags & observer::flag_queried); + if (flags & short_timeout) + { + // short timeout means that it has been more than + // two seconds since we sent the request, and that + // we'll most likely not get a response. But, in case + // we do get a late response, keep the handler + // around for some more, but open up the slot + // by increasing the branch factor + if ((o->flags & observer::flag_short_timeout) == 0) + ++m_branch_factor; + o->flags |= observer::flag_short_timeout; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] 1ST_TIMEOUT " + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " addr: " << o->target_ep() + << " branch-factor: " << m_branch_factor + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + } + else + { + o->flags |= observer::flag_failed; + // if this flag is set, it means we increased the + // branch factor for it, and we should restore it + if (o->flags & observer::flag_short_timeout) + --m_branch_factor; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] TIMEOUT " + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " addr: " << o->target_ep() + << " branch-factor: " << m_branch_factor + << " invoke-count: " << m_invoke_count + << " type: " << name() + ; +#endif + + ++m_timeouts; + --m_invoke_count; + TORRENT_ASSERT(m_invoke_count >= 0); + } + + if (flags & prevent_request) + { + --m_branch_factor; + if (m_branch_factor <= 0) m_branch_factor = 1; + } + bool is_done = add_requests(); + if (is_done) done(); +} + +void traversal_algorithm::done() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int results_target = m_num_target_nodes; + int closest_target = 160; + + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end && results_target > 0; ++i) + { + boost::intrusive_ptr o = *i; + if (o->flags & observer::flag_alive) + { + TORRENT_ASSERT(o->flags & observer::flag_queried); + TORRENT_LOG(traversal) << "[" << this << "] " + << results_target + << " id: " << o->id() + << " distance: " << distance_exp(m_target, o->id()) + << " address: " << o->target_ep() + ; + --results_target; + int dist = distance_exp(m_target, o->id()); + if (dist < closest_target) closest_target = dist; + } + } + + TORRENT_LOG(traversal) << "[" << this << "] COMPLETED " + << "distance: " << closest_target + << " type: " << name() + ; + +#endif + // delete all our references to the observer objects so + // they will in turn release the traversal algorithm + m_results.clear(); +} + +bool traversal_algorithm::add_requests() +{ + int results_target = m_num_target_nodes; + + // this only counts outstanding requests at the top of the + // target list. This is <= m_invoke count. m_invoke_count + // is the total number of outstanding requests, including + // old ones that may be waiting on nodes much farther behind + // the current point we've reached in the search. + int outstanding = 0; + + // if we're doing aggressive lookups, we keep branch-factor + // outstanding requests _at the tops_ of the result list. Otherwise + // we just keep any branch-factor outstanding requests + bool agg = m_node.settings().aggressive_lookups; + + // Find the first node that hasn't already been queried. + // and make sure that the 'm_branch_factor' top nodes + // stay queried at all times (obviously ignoring failed nodes) + // and without surpassing the 'result_target' nodes (i.e. k=8) + // this is a slight variation of the original paper which instead + // limits the number of outstanding requests, this limits the + // number of good outstanding requests. It will use more traffic, + // but is intended to speed up lookups + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end + && results_target > 0 + && (agg ? outstanding < m_branch_factor + : m_invoke_count < m_branch_factor); + ++i) + { + observer* o = i->get(); + if (o->flags & observer::flag_alive) + { + TORRENT_ASSERT(o->flags & observer::flag_queried); + --results_target; + continue; + } + if (o->flags & observer::flag_queried) + { + // if it's queried, not alive and not failed, it + // must be currently in flight + if ((o->flags & observer::flag_failed) == 0) + ++outstanding; + + continue; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] INVOKE " + << " nodes-left: " << (m_results.end() - i) + << " top-invoke-count: " << outstanding + << " invoke-count: " << m_invoke_count + << " branch-factor: " << m_branch_factor + << " distance: " << distance_exp(m_target, (*i)->id()) + << " type: " << name() + ; +#endif + + o->flags |= observer::flag_queried; + if (invoke(*i)) + { + TORRENT_ASSERT(m_invoke_count >= 0); + ++m_invoke_count; + ++outstanding; + } + else + { + o->flags |= observer::flag_failed; + } + } + + // this is the completion condition. If we found m_num_target_nodes + // (i.e. k=8) completed results, without finding any still + // outstanding requests, we're done. + // also, if invoke count is 0, it means we didn't even find 'k' + // working nodes, we still have to terminate though. + return (results_target == 0 && outstanding == 0) || m_invoke_count == 0; +} + +void traversal_algorithm::add_router_entries() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << this << "] using router nodes to initiate traversal algorithm. " + << std::distance(m_node.m_table.router_begin(), m_node.m_table.router_end()) << " routers"; +#endif + for (routing_table::router_iterator i = m_node.m_table.router_begin() + , end(m_node.m_table.router_end()); i != end; ++i) + { + add_entry(node_id(0), *i, observer::flag_initial); + } +} + +void traversal_algorithm::init() +{ + m_branch_factor = m_node.branch_factor(); + m_node.add_traversal_algorithm(this); +} + +traversal_algorithm::~traversal_algorithm() +{ + m_node.remove_traversal_algorithm(this); +} + +void traversal_algorithm::status(dht_lookup& l) +{ + l.timeouts = m_timeouts; + l.responses = m_responses; + l.outstanding_requests = m_invoke_count; + l.branch_factor = m_branch_factor; + l.type = name(); + l.nodes_left = 0; + l.first_timeout = 0; + + int last_sent = INT_MAX; + ptime now = time_now(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer& o = **i; + if (o.flags & observer::flag_queried) + { + last_sent = (std::min)(last_sent, int(total_seconds(now - o.sent()))); + if (o.has_short_timeout()) ++l.first_timeout; + continue; + } + ++l.nodes_left; + } + l.last_sent = last_sent; +} + +void traversal_observer::reply(msg const& m) +{ + lazy_entry const* r = m.message.dict_find_dict("r"); + if (!r) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() + << "] missing response dict"; +#endif + return; + } + + // look for nodes + lazy_entry const* n = r->dict_find_string("nodes"); + if (n) + { + char const* nodes = n->string_ptr(); + char const* end = nodes + n->string_length(); + + while (end - nodes >= 26) + { + node_id id; + std::copy(nodes, nodes + 20, id.begin()); + nodes += 20; + m_algorithm->traverse(id, read_v4_endpoint(nodes)); + } + } + + lazy_entry const* id = r->dict_find_string("id"); + if (!id || id->string_length() != 20) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "[" << m_algorithm.get() << "] invalid id in response"; +#endif + return; + } + + // in case we didn't know the id of this peer when we sent the message to + // it. For instance if it's a bootstrap node. + set_id(node_id(id->string_ptr())); +} + +void traversal_algorithm::abort() +{ + m_num_target_nodes = 0; + for (std::vector::iterator i = m_results.begin() + , end(m_results.end()); i != end; ++i) + { + observer& o = **i; + if (o.flags & observer::flag_queried) + o.flags |= observer::flag_done; + } + done(); +} + +} } // namespace libtorrent::dht + diff --git a/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp b/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp new file mode 100644 index 0000000000..3a468fc3ca --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lazy_bdecode.cpp @@ -0,0 +1,706 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/lazy_entry.hpp" +#include +#include // for numeric_limits + +namespace +{ + const int lazy_entry_grow_factor = 150; // percent + const int lazy_entry_dict_init = 5; + const int lazy_entry_list_init = 5; +} + +namespace libtorrent +{ + + namespace + { + int fail(int* error_pos + , std::vector& stack + , char const* start + , char const* orig_start) + { + while (!stack.empty()) { + lazy_entry* top = stack.back(); + if (top->type() == lazy_entry::dict_t || top->type() == lazy_entry::list_t) + { + top->pop(); + break; + } + stack.pop_back(); + } + if (error_pos) *error_pos = start - orig_start; + return -1; + } + } + +#define TORRENT_FAIL_BDECODE(code) do { ec = make_error_code(code); return fail(error_pos, stack, start, orig_start); } while (false) + + namespace { bool numeric(char c) { return c >= '0' && c <= '9'; } } + + // fills in 'val' with what the string between start and the + // first occurance of the delimiter is interpreted as an int. + // return the pointer to the delimiter, or 0 if there is a + // parse error. val should be initialized to zero + char const* parse_int(char const* start, char const* end, char delimiter + , boost::int64_t& val, bdecode_errors::error_code_enum& ec) + { + while (start < end && *start != delimiter) + { + if (!numeric(*start)) + { + ec = bdecode_errors::expected_string; + return start; + } + if (val > (std::numeric_limits::max)() / 10) + { + ec = bdecode_errors::overflow; + return start; + } + val *= 10; + int digit = *start - '0'; + if (val > (std::numeric_limits::max)() - digit) + { + ec = bdecode_errors::overflow; + return start; + } + val += digit; + ++start; + } + if (*start != delimiter) + ec = bdecode_errors::expected_colon; + return start; + } + + char const* find_char(char const* start, char const* end, char delimiter) + { + while (start < end && *start != delimiter) ++start; + return start; + } + +#ifndef TORRENT_NO_DEPRECATE + int lazy_bdecode(char const* start, char const* end + , lazy_entry& ret, int depth_limit, int item_limit) + { + error_code ec; + int pos; + return lazy_bdecode(start, end, ret, ec, &pos, depth_limit, item_limit); + } +#endif + + // return 0 = success + int lazy_bdecode(char const* start, char const* end, lazy_entry& ret + , error_code& ec, int* error_pos, int depth_limit, int item_limit) + { + char const* const orig_start = start; + ret.clear(); + if (start == end) return 0; + + std::vector stack; + + stack.push_back(&ret); + while (start <= end) + { + if (stack.empty()) break; // done! + + lazy_entry* top = stack.back(); + + if (int(stack.size()) > depth_limit) TORRENT_FAIL_BDECODE(bdecode_errors::depth_exceeded); + if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + char t = *start; + ++start; + if (start >= end && t != 'e') TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + + switch (top->type()) + { + case lazy_entry::dict_t: + { + if (t == 'e') + { + top->set_end(start); + stack.pop_back(); + continue; + } + if (!numeric(t)) TORRENT_FAIL_BDECODE(bdecode_errors::expected_string); + boost::int64_t len = t - '0'; + bdecode_errors::error_code_enum e = bdecode_errors::no_error; + start = parse_int(start, end, ':', len, e); + if (e) + TORRENT_FAIL_BDECODE(e); + + if (start + len + 1 > end) + TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + + if (len < 0) + TORRENT_FAIL_BDECODE(bdecode_errors::overflow); + + ++start; + if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + lazy_entry* ent = top->dict_append(start); + if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); + start += len; + if (start >= end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + stack.push_back(ent); + t = *start; + ++start; + break; + } + case lazy_entry::list_t: + { + if (t == 'e') + { + top->set_end(start); + stack.pop_back(); + continue; + } + lazy_entry* ent = top->list_append(); + if (ent == 0) TORRENT_FAIL_BDECODE(boost::system::errc::not_enough_memory); + stack.push_back(ent); + break; + } + default: break; + } + + --item_limit; + if (item_limit <= 0) TORRENT_FAIL_BDECODE(bdecode_errors::limit_exceeded); + + top = stack.back(); + switch (t) + { + case 'd': + top->construct_dict(start - 1); + continue; + case 'l': + top->construct_list(start - 1); + continue; + case 'i': + { + char const* int_start = start; + start = find_char(start, end, 'e'); + top->construct_int(int_start, start - int_start); + if (start == end) TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + TORRENT_ASSERT(*start == 'e'); + ++start; + stack.pop_back(); + continue; + } + default: + { + if (!numeric(t)) + TORRENT_FAIL_BDECODE(bdecode_errors::expected_value); + + boost::int64_t len = t - '0'; + bdecode_errors::error_code_enum e = bdecode_errors::no_error; + start = parse_int(start, end, ':', len, e); + if (e) + TORRENT_FAIL_BDECODE(e); + if (start + len + 1 > end) + TORRENT_FAIL_BDECODE(bdecode_errors::unexpected_eof); + if (len < 0) + TORRENT_FAIL_BDECODE(bdecode_errors::overflow); + + ++start; + top->construct_string(start, int(len)); + stack.pop_back(); + start += len; + continue; + } + } + return 0; + } + return 0; + } + + boost::int64_t lazy_entry::int_value() const + { + TORRENT_ASSERT(m_type == int_t); + boost::int64_t val = 0; + bool negative = false; + if (*m_data.start == '-') negative = true; + bdecode_errors::error_code_enum ec = bdecode_errors::no_error; + parse_int(m_data.start + negative + , m_data.start + m_size, 'e', val, ec); + if (ec) return 0; + if (negative) val = -val; + return val; + } + + lazy_entry* lazy_entry::dict_append(char const* name) + { + TORRENT_ASSERT(m_type == dict_t); + TORRENT_ASSERT(m_size <= m_capacity); + if (m_capacity == 0) + { + int capacity = lazy_entry_dict_init; + m_data.dict = new (std::nothrow) lazy_dict_entry[capacity]; + if (m_data.dict == 0) return 0; + m_capacity = capacity; + } + else if (m_size == m_capacity) + { + int capacity = m_capacity * lazy_entry_grow_factor / 100; + lazy_dict_entry* tmp = new (std::nothrow) lazy_dict_entry[capacity]; + if (tmp == 0) return 0; + std::memcpy(tmp, m_data.dict, sizeof(lazy_dict_entry) * m_size); + for (int i = 0; i < int(m_size); ++i) m_data.dict[i].val.release(); + delete[] m_data.dict; + m_data.dict = tmp; + m_capacity = capacity; + } + + TORRENT_ASSERT(m_size < m_capacity); + lazy_dict_entry& ret = m_data.dict[m_size++]; + ret.name = name; + return &ret.val; + } + + void lazy_entry::pop() + { + if (m_size > 0) --m_size; + } + + namespace + { + // the number of decimal digits needed + // to represent the given value + int num_digits(int val) + { + int ret = 1; + while (val >= 10) + { + ++ret; + val /= 10; + } + return ret; + } + } + + void lazy_entry::construct_string(char const* start, int length) + { + TORRENT_ASSERT(m_type == none_t); + m_type = string_t; + m_data.start = start; + m_size = length; + m_begin = start - 1 - num_digits(length); + m_len = start - m_begin + length; + } + + namespace + { + // str1 is null-terminated + // str2 is not, str2 is len2 chars + bool string_equal(char const* str1, char const* str2, int len2) + { + while (len2 > 0) + { + if (*str1 != *str2) return false; + if (*str1 == 0) return false; + ++str1; + ++str2; + --len2; + } + return *str1 == 0; + } + } + + std::pair lazy_entry::dict_at(int i) const + { + TORRENT_ASSERT(m_type == dict_t); + TORRENT_ASSERT(i < int(m_size)); + lazy_dict_entry const& e = m_data.dict[i]; + return std::make_pair(std::string(e.name, e.val.m_begin - e.name), &e.val); + } + + std::string lazy_entry::dict_find_string_value(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); + return e->string_value(); + } + + pascal_string lazy_entry::dict_find_pstr(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); + return e->string_pstr(); + } + + lazy_entry const* lazy_entry::dict_find_string(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_int(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::int_t) return 0; + return e; + } + + boost::int64_t lazy_entry::dict_find_int_value(char const* name, boost::int64_t default_val) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::int_t) return default_val; + return e->int_value(); + } + + lazy_entry const* lazy_entry::dict_find_dict(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::dict_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_dict(std::string const& name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::dict_t) return 0; + return e; + } + + lazy_entry const* lazy_entry::dict_find_list(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::list_t) return 0; + return e; + } + + lazy_entry* lazy_entry::dict_find(char const* name) + { + TORRENT_ASSERT(m_type == dict_t); + for (int i = 0; i < int(m_size); ++i) + { + lazy_dict_entry& e = m_data.dict[i]; + if (string_equal(name, e.name, e.val.m_begin - e.name)) + return &e.val; + } + return 0; + } + + lazy_entry* lazy_entry::dict_find(std::string const& name) + { + TORRENT_ASSERT(m_type == dict_t); + for (int i = 0; i < int(m_size); ++i) + { + lazy_dict_entry& e = m_data.dict[i]; + if (name.size() != e.val.m_begin - e.name) continue; + if (std::equal(name.begin(), name.end(), e.name)) + return &e.val; + } + return 0; + } + + lazy_entry* lazy_entry::list_append() + { + TORRENT_ASSERT(m_type == list_t); + TORRENT_ASSERT(m_size <= m_capacity); + if (m_capacity == 0) + { + int capacity = lazy_entry_list_init; + m_data.list = new (std::nothrow) lazy_entry[capacity]; + if (m_data.list == 0) return 0; + m_capacity = capacity; + } + else if (m_size == m_capacity) + { + int capacity = m_capacity * lazy_entry_grow_factor / 100; + lazy_entry* tmp = new (std::nothrow) lazy_entry[capacity]; + if (tmp == 0) return 0; + std::memcpy(tmp, m_data.list, sizeof(lazy_entry) * m_size); + for (int i = 0; i < int(m_size); ++i) m_data.list[i].release(); + delete[] m_data.list; + m_data.list = tmp; + m_capacity = capacity; + } + + TORRENT_ASSERT(m_size < m_capacity); + return m_data.list + (m_size++); + } + + std::string lazy_entry::list_string_value_at(int i) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::string_t) return std::string(); + return e->string_value(); + } + + pascal_string lazy_entry::list_pstr_at(int i) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::string_t) return pascal_string(0, 0); + return e->string_pstr(); + } + + boost::int64_t lazy_entry::list_int_value_at(int i, boost::int64_t default_val) const + { + lazy_entry const* e = list_at(i); + if (e == 0 || e->type() != lazy_entry::int_t) return default_val; + return e->int_value(); + } + + void lazy_entry::clear() + { + switch (m_type) + { + case list_t: delete[] m_data.list; break; + case dict_t: delete[] m_data.dict; break; + default: break; + } + m_data.start = 0; + m_size = 0; + m_capacity = 0; + m_type = none_t; + } + + std::pair lazy_entry::data_section() const + { + typedef std::pair return_t; + return return_t(m_begin, m_len); + } + + int line_longer_than(lazy_entry const& e, int limit) + { + int line_len = 0; + switch (e.type()) + { + case lazy_entry::list_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.list_size(); ++i) + { + int ret = line_longer_than(*e.list_at(i), limit - line_len); + if (ret == -1) return -1; + line_len += ret + 2; + } + break; + case lazy_entry::dict_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.dict_size(); ++i) + { + line_len += 4 + e.dict_at(i).first.size(); + if (line_len > limit) return -1; + int ret = line_longer_than(*e.dict_at(i).second, limit - line_len); + if (ret == -1) return -1; + line_len += ret + 1; + } + break; + case lazy_entry::string_t: + line_len += 3 + e.string_length(); + break; + case lazy_entry::int_t: + { + boost::int64_t val = e.int_value(); + while (val > 0) + { + ++line_len; + val /= 10; + } + line_len += 2; + } + break; + case lazy_entry::none_t: + line_len += 4; + break; + } + + if (line_len > limit) return -1; + return line_len; + } + + void escape_string(std::string& ret, char const* str, int len) + { + for (int i = 0; i < len; ++i) + { + if (str[i] >= 32 && str[i] < 127) + { + ret += str[i]; + } + else + { + char tmp[5]; + snprintf(tmp, sizeof(tmp), "\\x%02x", (unsigned char)str[i]); + ret += tmp; + } + } + } + + void print_string(std::string& ret, char const* str, int len, bool single_line) + { + bool printable = true; + for (int i = 0; i < len; ++i) + { + char c = str[i]; + if (c >= 32 && c < 127) continue; + printable = false; + break; + } + ret += "'"; + if (printable) + { + if (single_line && len > 30) + { + ret.append(str, 14); + ret += "..."; + ret.append(str + len-14, 14); + } + else + ret.append(str, len); + ret += "'"; + return; + } + if (single_line && len > 20) + { + escape_string(ret, str, 9); + ret += "..."; + escape_string(ret, str + len - 9, 9); + } + else + { + escape_string(ret, str, len); + } + ret += "'"; + } + + std::string print_entry(lazy_entry const& e, bool single_line, int indent) + { + char indent_str[200]; + memset(indent_str, ' ', 200); + indent_str[0] = ','; + indent_str[1] = '\n'; + indent_str[199] = 0; + if (indent < 197 && indent >= 0) indent_str[indent+2] = 0; + std::string ret; + switch (e.type()) + { + case lazy_entry::none_t: return "none"; + case lazy_entry::int_t: + { + char str[100]; + snprintf(str, sizeof(str), "%" PRId64, e.int_value()); + return str; + } + case lazy_entry::string_t: + { + print_string(ret, e.string_ptr(), e.string_length(), single_line); + return ret; + } + case lazy_entry::list_t: + { + ret += '['; + bool one_liner = line_longer_than(e, 200) != -1 || single_line; + + if (!one_liner) ret += indent_str + 1; + for (int i = 0; i < e.list_size(); ++i) + { + if (i == 0 && one_liner) ret += " "; + ret += print_entry(*e.list_at(i), single_line, indent + 2); + if (i < e.list_size() - 1) ret += (one_liner?", ":indent_str); + else ret += (one_liner?" ":indent_str+1); + } + ret += "]"; + return ret; + } + case lazy_entry::dict_t: + { + ret += "{"; + bool one_liner = line_longer_than(e, 200) != -1 || single_line; + + if (!one_liner) ret += indent_str+1; + for (int i = 0; i < e.dict_size(); ++i) + { + if (i == 0 && one_liner) ret += " "; + std::pair ent = e.dict_at(i); + print_string(ret, ent.first.c_str(), ent.first.size(), true); + ret += ": "; + ret += print_entry(*ent.second, single_line, indent + 2); + if (i < e.dict_size() - 1) ret += (one_liner?", ":indent_str); + else ret += (one_liner?" ":indent_str+1); + } + ret += "}"; + return ret; + } + } + return ret; + } + + struct bdecode_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT; + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT; + virtual boost::system::error_condition default_error_condition(int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + const char* bdecode_error_category::name() const BOOST_SYSTEM_NOEXCEPT + { + return "bdecode error"; + } + + std::string bdecode_error_category::message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* msgs[] = + { + "no error", + "expected string in bencoded string", + "expected colon in bencoded string", + "unexpected end of file in bencoded string", + "expected value (list, dict, int or string) in bencoded string", + "bencoded nesting depth exceeded", + "bencoded item count limit exceeded", + "integer overflow", + }; + if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0]))) + return "Unknown error"; + return msgs[ev]; + } + + boost::system::error_category& get_bdecode_category() + { + static bdecode_error_category bdecode_category; + return bdecode_category; + } + + namespace bdecode_errors + { + boost::system::error_code make_error_code(error_code_enum e) + { + return boost::system::error_code(e, get_bdecode_category()); + } + } +}; + diff --git a/apps/Launcher/ext/libtorrent/src/logger.cpp b/apps/Launcher/ext/libtorrent/src/logger.cpp new file mode 100644 index 0000000000..6c42ce8560 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/logger.cpp @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include + +#include "libtorrent/extensions/logger.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer_request.hpp" + +#if TORRENT_USE_IOSTREAM && !defined TORRENT_DISABLE_EXTENSIONS + +#include +#include "libtorrent/file.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_connection.hpp" + +namespace libtorrent { + + class peer_connection; + +namespace +{ + + struct logger_peer_plugin : peer_plugin + { + logger_peer_plugin(std::string const& filename) + { + error_code ec; + std::string dir = complete("libtorrent_ext_logs"); + if (!exists(dir)) create_directories(dir, ec); + m_file.open(combine_path(dir, filename).c_str(), std::ios_base::out); + m_file << "\n\n\n"; + log_timestamp(); + m_file << "*** starting log ***\n"; + } + + void log_timestamp() + { + m_file << time_now_string() << ": "; + } + + // can add entries to the extension handshake + virtual void add_handshake(entry&) {} + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + log_timestamp(); + m_file << "<== EXTENSION_HANDSHAKE\n" << print_entry(h); + return true; + } + + // returning true from any of the message handlers + // indicates that the plugin has handeled the message. + // it will break the plugin chain traversing and not let + // anyone else handle the message, including the default + // handler. + + virtual bool on_choke() + { + log_timestamp(); + m_file << "<== CHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_unchoke() + { + log_timestamp(); + m_file << "<== UNCHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_interested() + { + log_timestamp(); + m_file << "<== INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_not_interested() + { + log_timestamp(); + m_file << "<== NOT_INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_have(int index) + { + log_timestamp(); + m_file << "<== HAVE [" << index << "]\n"; + m_file.flush(); + return false; + } + + virtual bool on_bitfield(bitfield const& bitfield_) + { + log_timestamp(); + m_file << "<== BITFIELD\n"; + m_file.flush(); + return false; + } + + virtual bool on_request(peer_request const& r) + { + log_timestamp(); + m_file << "<== REQUEST [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_piece(peer_request const& r, disk_buffer_holder& data) + { + log_timestamp(); + m_file << "<== PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_cancel(peer_request const& r) + { + log_timestamp(); + m_file << "<== CANCEL [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + // called when an extended message is received. If returning true, + // the message is not processed by any other plugin and if false + // is returned the next plugin in the chain will receive it to + // be able to handle it + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { return false; } + + virtual bool on_unknown_message(int length, int msg + , buffer::const_interval body) + { + if (body.left() < length) return false; + log_timestamp(); + m_file << "<== UNKNOWN [ msg: " << msg + << " | l: " << length << " ]\n"; + m_file.flush(); + return false; + } + + virtual void on_piece_pass(int index) + { + log_timestamp(); + m_file << "*** HASH PASSED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + virtual void on_piece_failed(int index) + { + log_timestamp(); + m_file << "*** HASH FAILED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + private: + std::ofstream m_file; + }; + + struct logger_plugin : torrent_plugin + { + virtual boost::shared_ptr new_connection( + peer_connection* pc) + { + error_code ec; + return boost::shared_ptr(new logger_peer_plugin( + pc->remote().address().to_string(ec) + "_" + + to_string(pc->remote().port()).elems + ".log")); + } + }; + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_logger_plugin(torrent*) + { + return boost::shared_ptr(new logger_plugin()); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/lsd.cpp b/apps/Launcher/ext/libtorrent/src/lsd.cpp new file mode 100644 index 0000000000..86e6bc4c70 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lsd.cpp @@ -0,0 +1,325 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/lsd.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/socket_io.hpp" // for print_address + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif +#include +#include + +using namespace libtorrent; + +namespace libtorrent +{ + // defined in broadcast_socket.cpp + address guess_local_address(io_service&); +} + +static error_code ec; + +lsd::lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb) + : m_callback(cb) + , m_socket(udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771) + , boost::bind(&lsd::on_announce, self(), _1, _2, _3)) +#if TORRENT_USE_IPV6 + , m_socket6(udp::endpoint(address_v6::from_string("ff15::efc0:988f", ec), 6771) + , boost::bind(&lsd::on_announce, self(), _1, _2, _3)) +#endif + , m_broadcast_timer(ios) + , m_cookie(random()) + , m_disabled(false) +#if TORRENT_USE_IPV6 + , m_disabled6(false) +#endif +{ +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log = fopen("lsd.log", "w+"); + if (m_log == NULL) + { + fprintf(stderr, "failed to open 'lsd.log': (%d) %s" + , errno, strerror(errno)); + } +#endif + + error_code ec; + m_socket.open(ios, ec); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (ec) + { + if (m_log) fprintf(m_log, "FAILED TO OPEN SOCKET: (%d) %s\n" + , ec.value(), ec.message().c_str()); + } +#endif + +#if TORRENT_USE_IPV6 + m_socket6.open(ios, ec); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (ec) + { + if (m_log) fprintf(m_log, "FAILED TO OPEN SOCKET6: (%d) %s\n" + , ec.value(), ec.message().c_str()); + } +#endif +#endif +} + +lsd::~lsd() +{ +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fclose(m_log); +#endif +} + +int render_lsd_packet(char* dst, int len, int listen_port + , char const* info_hash_hex, int m_cookie, char const* host) +{ + return snprintf(dst, len, + "BT-SEARCH * HTTP/1.1\r\n" + "Host: %s:6771\r\n" + "Port: %d\r\n" + "Infohash: %s\r\n" + "cookie: %x\r\n" + "\r\n\r\n", host, listen_port, info_hash_hex, m_cookie); +} + +void lsd::announce(sha1_hash const& ih, int listen_port, bool broadcast) +{ + announce_impl(ih, listen_port, broadcast, 0); +} + +void lsd::announce_impl(sha1_hash const& ih, int listen_port, bool broadcast + , int retry_count) +{ +#if TORRENT_USE_IPV6 + if (m_disabled && m_disabled6) return; +#else + if (m_disabled) return; +#endif + + char ih_hex[41]; + to_hex((char const*)&ih[0], 20, ih_hex); + char msg[200]; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s ==> announce: ih: %s port: %u\n" + , time_now_string(), ih_hex, listen_port); +#endif + + error_code ec; + if (!m_disabled) + { + int msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex + , m_cookie, "239.192.152.143"); + m_socket.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); + if (ec) + { + m_disabled = true; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s failed to send message: (%d) %s" + , time_now_string(), ec.value(), ec.message().c_str()); +#endif + } + } + +#if TORRENT_USE_IPV6 + if (!m_disabled6) + { + int msg_len = render_lsd_packet(msg, sizeof(msg), listen_port, ih_hex + , m_cookie, "[ff15::efc0:988f]"); + m_socket6.send(msg, msg_len, ec, broadcast ? broadcast_socket::broadcast : 0); + if (ec) + { + m_disabled6 = true; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s failed to send message6: (%d) %s" + , time_now_string(), ec.value(), ec.message().c_str()); +#endif + } + } +#endif + + ++retry_count; + if (retry_count >= 3) return; + +#if TORRENT_USE_IPV6 + if (m_disabled && m_disabled6) return; +#else + if (m_disabled) return; +#endif + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("lsd::resend_announce"); +#endif + m_broadcast_timer.expires_from_now(seconds(2 * retry_count), ec); + m_broadcast_timer.async_wait(boost::bind(&lsd::resend_announce, self(), _1 + , ih, listen_port, retry_count)); +} + +void lsd::resend_announce(error_code const& e, sha1_hash const& info_hash + , int listen_port, int retry_count) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("lsd::resend_announce"); +#endif + if (e) return; + + announce_impl(info_hash, listen_port, false, retry_count); +} + +void lsd::on_announce(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + + http_parser p; + + bool error = false; + p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred) + , error); + + if (!p.header_finished() || error) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: incomplete HTTP message\n", time_now_string()); +#endif + return; + } + + if (p.method() != "bt-search") + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid HTTP method: %s\n" + , time_now_string(), p.method().c_str()); +#endif + return; + } + + std::string const& port_str = p.header("port"); + if (port_str.empty()) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid BT-SEARCH, missing port\n" + , time_now_string()); +#endif + return; + } + + int port = std::atoi(port_str.c_str()); + + typedef std::multimap headers_t; + headers_t const& headers = p.headers(); + + headers_t::const_iterator cookie_iter = headers.find("cookie"); + if (cookie_iter != headers.end()) + { + // we expect it to be hexadecimal + // if it isn't, it's not our cookie anyway + boost::int32_t cookie = strtol(cookie_iter->second.c_str(), NULL, 16); + if (cookie == m_cookie) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: ignoring packet (cookie matched our own): %x == %x\n" + , time_now_string(), cookie, m_cookie); +#endif + return; + } + } + + std::pair ihs + = headers.equal_range("infohash"); + + for (headers_t::const_iterator i = ihs.first; i != ihs.second; ++i) + { + std::string const& ih_str = i->second; + if (ih_str.size() != 40) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s <== announce: invalid BT-SEARCH, invalid infohash: %s\n" + , time_now_string(), ih_str.c_str()); +#endif + continue; + } + + sha1_hash ih(0); + from_hex(ih_str.c_str(), 40, (char*)&ih[0]); + + if (!ih.is_all_zeros() && port != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (m_log) fprintf(m_log, "%s *** incoming local announce %s:%d ih: %s\n" + , time_now_string(), print_address(from.address()).c_str() + , port, ih_str.c_str()); +#endif + // we got an announce, pass it on through the callback + TORRENT_TRY { + m_callback(tcp::endpoint(from.address(), port), ih); + } TORRENT_CATCH(std::exception&) {} + } + } +} + +void lsd::close() +{ + m_socket.close(); +#if TORRENT_USE_IPV6 + m_socket6.close(); +#endif + error_code ec; + m_broadcast_timer.cancel(ec); + m_disabled = true; +#if TORRENT_USE_IPV6 + m_disabled6 = true; +#endif + m_callback.clear(); +} + diff --git a/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp b/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp new file mode 100644 index 0000000000..1f3d727fb9 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/lt_trackers.cpp @@ -0,0 +1,414 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/lt_trackers.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/parse_url.hpp" +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent { namespace +{ + + bool send_tracker(announce_entry const& e) + { + // max_fails == 0 means that it's one + // of the trackers from the trackers + // from the torrent file + return e.fail_limit == 0 || e.verified; + } + + struct lt_tracker_plugin : torrent_plugin + { + lt_tracker_plugin(torrent& t) + : m_torrent(t) + , m_updates(0) + , m_2_minutes(110) + , m_num_trackers(0) + { + m_old_trackers = t.trackers(); + update_list_hash(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + virtual void tick() + { + if (m_2_minutes++ < 120) return; + m_2_minutes = 0; + + // build tracker diff + entry tex; + entry::list_type& added = tex["added"].list(); + std::vector const& trackers = m_torrent.trackers(); + for (std::vector::const_iterator i = trackers.begin() + , end(trackers.end()); i != end; ++i) + { + std::vector::const_iterator k = std::find_if( + m_old_trackers.begin(), m_old_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url); + if (k != m_old_trackers.end()) continue; + if (!send_tracker(*i)) continue; + m_old_trackers.push_back(*i); + ++m_updates; + added.push_back(i->url); + } + m_lt_trackers_msg.clear(); + bencode(std::back_inserter(m_lt_trackers_msg), tex); + if (m_updates > 0) update_list_hash(); + } + + void update_list_hash() + { + std::vector canonical_list; + std::transform(m_old_trackers.begin(), m_old_trackers.end(), back_inserter(canonical_list) + , boost::bind(&announce_entry::url, _1)); + std::sort(canonical_list.begin(), canonical_list.end()); + + hasher h; + std::for_each(canonical_list.begin(), canonical_list.end() + , boost::bind(&hasher::update, &h, _1)); + m_list_hash = h.final(); + } + + int num_updates() const { return m_updates; } + + std::vector const& get_lt_tex_msg() const { return m_lt_trackers_msg; } + + sha1_hash const& list_hash() const { return m_list_hash; } + + std::vector const& trackers() const { return m_old_trackers; } + + void increment_tracker_counter() { m_num_trackers++; } + int num_tex_trackers() const { return m_num_trackers; } + + private: + torrent& m_torrent; + std::vector m_old_trackers; + int m_updates; + int m_2_minutes; + std::vector m_lt_trackers_msg; + sha1_hash m_list_hash; + int m_num_trackers; + }; + + + struct lt_tracker_peer_plugin : peer_plugin + { + lt_tracker_peer_plugin(torrent& t, bt_peer_connection& pc, lt_tracker_plugin& tp) + : m_message_index(0) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + , m_2_minutes(115) + , m_full_list(true) + {} + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["lt_tex"] = 19; + h["tr"] = m_tp.list_hash().to_string(); + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value("lt_tex", -1)); + if (index == -1) return false; + m_message_index = index; + + // if we have the same tracker list, don't bother sending the + // full list. Just send deltas + std::string tracker_list_hash = h.dict_find_string_value("tr"); + if (tracker_list_hash.size() == 20 + && sha1_hash(tracker_list_hash) == m_tp.list_hash()) + { + m_full_list = false; + } + return true; + } + + virtual bool on_extended(int length + , int extended_msg, buffer::const_interval body) + { + if (extended_msg != 19) return false; + if (m_message_index == 0) return false; + if (!m_pc.packet_finished()) return true; + + lazy_entry msg; + error_code ec; + int ret = lazy_bdecode(body.begin, body.end, msg, ec); + if (ret != 0 || msg.type() != lazy_entry::dict_t) + { + m_pc.disconnect(errors::invalid_lt_tracker_message, 2); + return true; + } + + lazy_entry const* added = msg.dict_find_list("added"); + + // invalid tex message + if (added == 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_pc.m_logger) << time_now_string() << " <== LT_TEX [ NOT A DICTIONARY ]\n"; +#endif + return true; + } + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream log_line; +#endif + if (m_tp.num_tex_trackers() >= 50) + { +#ifdef TORRENT_VERBOSE_LOGGING + log_line << time_now_string() << " <== LT_TEX [ " + "we already have " << m_tp.num_tex_trackers() << " trackers " + "from tex, don't add any more"; + (*m_pc.m_logger) << log_line.str(); +#endif + return true; + } + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << time_now_string() << " <== LT_TEX [ " + "added: "; +#endif + + for (int i = 0; i < added->list_size(); ++i) + { + announce_entry e(added->list_string_value_at(i)); + if (e.url.empty()) continue; + + // ignore urls with binary data in them + if (need_encoding(e.url.c_str(), e.url.size())) continue; + + // ignore invalid URLs + error_code ec; + std::string protocol; + std::string auth; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(e.url, ec); + if (ec) continue; + + // ignore unknown protocols + if (protocol != "udp" && protocol != "http" && protocol != "https") + continue; + + // ignore invalid ports + if (port == 0) + continue; + + if (m_tp.num_tex_trackers() >= 50) + { +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "**reached-limit** "; +#endif + break; + } + + e.fail_limit = 1; + e.send_stats = false; + e.source = announce_entry::source_tex; + if (m_torrent.add_tracker(e)) + m_tp.increment_tracker_counter(); + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << e.url << " "; +#endif + } +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "]\n"; + (*m_pc.m_logger) << log_line.str(); +#endif + return true; + } + + virtual void tick() + { + if (!m_message_index) return; // no handshake yet + if (++m_2_minutes <= 120) return; + m_2_minutes = 0; + + if (m_full_list) + { + if (send_full_tex_list()) + m_full_list = false; + } + else + { + send_lt_tex_diff(); + } + } + + private: + + void send_lt_tex_diff() + { + // if there's no change in out tracker set, don't send anything + if (m_tp.num_updates() == 0) return; + + if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) + return; + + std::vector const& tex_msg = m_tp.get_lt_tex_msg(); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + tex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&tex_msg[0], tex_msg.size()); + m_pc.setup_send(); + } + + bool send_full_tex_list() const + { + if (m_tp.trackers().empty()) return false; + + if (!m_torrent.valid_metadata() || m_torrent.torrent_file().priv()) + return false; + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream log_line; + log_line << time_now_string() << " ==> LT_TEX [ " + "added: "; +#endif + entry tex; + entry::list_type& added = tex["added"].list(); + for (std::vector::const_iterator i = m_tp.trackers().begin() + , end(m_tp.trackers().end()); i != end; ++i) + { + if (!send_tracker(*i)) continue; + added.push_back(i->url); +#ifdef TORRENT_VERBOSE_LOGGING + log_line << i->url << " "; +#endif + } + std::vector tex_msg; + bencode(std::back_inserter(tex_msg), tex); + +#ifdef TORRENT_VERBOSE_LOGGING + log_line << "]\n"; + (*m_pc.m_logger) << log_line.str(); +#endif + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + tex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&tex_msg[0], tex_msg.size()); + m_pc.setup_send(); + + return true; + } + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + torrent& m_torrent; + bt_peer_connection& m_pc; + lt_tracker_plugin& m_tp; + + int m_2_minutes; + bool m_full_list; + }; + + boost::shared_ptr lt_tracker_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + if (m_torrent.valid_metadata() && m_torrent.torrent_file().priv()) + return boost::shared_ptr(); + + bt_peer_connection* c = static_cast(pc); + return boost::shared_ptr(new lt_tracker_peer_plugin(m_torrent, *c, *this)); + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent* t, void*) + { + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new lt_tracker_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp b/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp new file mode 100644 index 0000000000..6b90aff2fe --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/magnet_uri.cpp @@ -0,0 +1,254 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/magnet_uri.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" + +#include + +namespace libtorrent +{ + std::string make_magnet_uri(torrent_handle const& handle) + { + if (!handle.is_valid()) return ""; + + std::string ret; + sha1_hash const& ih = handle.info_hash(); + ret += "magnet:?xt=urn:btih:"; + ret += to_hex(ih.to_string()); + + torrent_status st = handle.status(torrent_handle::query_name); + if (!st.name.empty()) + { + ret += "&dn="; + ret += escape_string(st.name.c_str(), st.name.length()); + } + + std::vector const& tr = handle.trackers(); + for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) + { + ret += "&tr="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + std::set seeds = handle.url_seeds(); + for (std::set::iterator i = seeds.begin() + , end(seeds.end()); i != end; ++i) + { + ret += "&ws="; + ret += escape_string(i->c_str(), i->length()); + } + + return ret; + } + + std::string make_magnet_uri(torrent_info const& info) + { + std::string ret; + sha1_hash const& ih = info.info_hash(); + ret += "magnet:?xt=urn:btih:"; + ret += to_hex(ih.to_string()); + + std::string const& name = info.name(); + + if (!name.empty()) + { + ret += "&dn="; + ret += escape_string(name.c_str(), name.length()); + } + + std::vector const& tr = info.trackers(); + + for (std::vector::const_iterator i = tr.begin(), end(tr.end()); i != end; ++i) + { + ret += "&tr="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + std::vector const& seeds = info.web_seeds(); + for (std::vector::const_iterator i = seeds.begin() + , end(seeds.end()); i != end; ++i) + { + if (i->type != web_seed_entry::url_seed) continue; + + ret += "&ws="; + ret += escape_string(i->url.c_str(), i->url.length()); + } + + return ret; + } + +#ifndef TORRENT_NO_DEPRECATE + + torrent_handle add_magnet_uri_deprecated(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) + { + parse_magnet_uri(uri, p, ec); + if (ec) return torrent_handle(); + return ses.add_torrent(p, ec); + } + + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p, error_code& ec) + { + return add_magnet_uri_deprecated(ses, uri, p, ec); + } + +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , std::string const& save_path + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params params(sc); + params.storage_mode = storage_mode; + params.userdata = userdata; + params.save_path = save_path; + + if (paused) params.flags |= add_torrent_params::flag_paused; + else params.flags &= ~add_torrent_params::flag_paused; + + error_code ec; + std::string display_name = url_has_argument(uri, "dn"); + if (!display_name.empty()) params.name = unescape_string(display_name.c_str(), ec); + std::string tracker_string = url_has_argument(uri, "tr"); + if (!tracker_string.empty()) params.trackers.push_back(unescape_string(tracker_string.c_str(), ec)); + + std::string btih = url_has_argument(uri, "xt"); + if (btih.empty()) return torrent_handle(); + + if (btih.compare(0, 9, "urn:btih:") != 0) return torrent_handle(); + + if (btih.size() == 40 + 9) from_hex(&btih[9], 40, (char*)¶ms.info_hash[0]); + else params.info_hash.assign(base32decode(btih.substr(9))); + + return ses.add_torrent(params); + } + + torrent_handle add_magnet_uri(session& ses, std::string const& uri + , add_torrent_params p) + { + error_code ec; + torrent_handle ret = add_magnet_uri_deprecated(ses, uri, p, ec); + if (ec) throw libtorrent_exception(ec); + return ret; + } +#endif // BOOST_NO_EXCEPTIONS +#endif // TORRENT_NO_DEPRECATE + + void parse_magnet_uri(std::string const& uri, add_torrent_params& p, error_code& ec) + { + ec.clear(); + std::string name; + + error_code e; + std::string display_name = url_has_argument(uri, "dn"); + if (!display_name.empty()) name = unescape_string(display_name.c_str(), e); + + // parse trackers out of the magnet link + std::string::size_type pos = std::string::npos; + std::string url = url_has_argument(uri, "tr", &pos); + while (pos != std::string::npos) + { + error_code e; + url = unescape_string(url, e); + if (e) continue; + p.trackers.push_back(url); + pos = uri.find("&tr=", pos); + if (pos == std::string::npos) break; + pos += 4; + url = uri.substr(pos, uri.find('&', pos) - pos); + } + + // parse web seeds out of the magnet link + pos = std::string::npos; + url = url_has_argument(uri, "ws", &pos); + while (pos != std::string::npos) + { + error_code e; + url = unescape_string(url, e); + if (e) continue; + p.url_seeds.push_back(url); + pos = uri.find("&ws=", pos); + if (pos == std::string::npos) break; + pos += 4; + url = uri.substr(pos, uri.find('&', pos) - pos); + } + + std::string btih = url_has_argument(uri, "xt"); + if (btih.empty()) + { + ec = errors::missing_info_hash_in_uri; + return; + } + + if (btih.compare(0, 9, "urn:btih:") != 0) + { + ec = errors::missing_info_hash_in_uri; + return; + } + +#ifndef TORRENT_DISABLE_DHT + std::string::size_type node_pos = std::string::npos; + std::string node = url_has_argument(uri, "dht", &node_pos); + while (!node.empty()) + { + std::string::size_type divider = node.find_last_of(':'); + if (divider != std::string::npos) + { + int port = atoi(node.c_str()+divider+1); + if (port != 0) + p.dht_nodes.push_back(std::make_pair(node.substr(0, divider), port)); + } + + node_pos = uri.find("&dht=", node_pos); + if (node_pos == std::string::npos) break; + node_pos += 5; + node = uri.substr(node_pos, uri.find('&', node_pos) - node_pos); + } +#endif + + sha1_hash info_hash; + if (btih.size() == 40 + 9) from_hex(&btih[9], 40, (char*)&info_hash[0]); + else info_hash.assign(base32decode(btih.substr(9))); + + p.info_hash = info_hash; + if (!name.empty()) p.name = name; + } +} + + diff --git a/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp b/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp new file mode 100644 index 0000000000..aa19e3f456 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/metadata_transfer.cpp @@ -0,0 +1,582 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include // count + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/metadata_transfer.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/buffer.hpp" + +namespace libtorrent { namespace +{ + int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + + std::pair req_to_offset(std::pair req, int total_size) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.second <= 256); + TORRENT_ASSERT(req.first + req.second <= 256); + + int start = div_round_up(req.first * total_size, 256); + int size = div_round_up((req.first + req.second) * total_size, 256) - start; + return std::make_pair(start, size); + } + + std::pair offset_to_req(std::pair offset, int total_size) + { + int start = offset.first * 256 / total_size; + int size = (offset.first + offset.second) * 256 / total_size - start; + + std::pair ret(start, size); + + TORRENT_ASSERT(start >= 0); + TORRENT_ASSERT(size > 0); + TORRENT_ASSERT(start <= 256); + TORRENT_ASSERT(start + size <= 256); + + // assert the identity of this function +#ifndef NDEBUG + std::pair identity = req_to_offset(ret, total_size); + TORRENT_ASSERT(offset == identity); +#endif + return ret; + } + + struct metadata_plugin : torrent_plugin + { + metadata_plugin(torrent& t) + : m_torrent(t) + , m_metadata_progress(0) + , m_metadata_size(0) + { + m_requested_metadata.resize(256, 0); + } + + virtual void on_files_checked() + { + // if the torrent is a seed, make a reference to + // the metadata from the torrent before it is deallocated + if (m_torrent.is_seed()) metadata(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + buffer::const_interval metadata() const + { + if (!m_metadata) + { + m_metadata = m_torrent.torrent_file().metadata(); + m_metadata_size = m_torrent.torrent_file().metadata_size(); + TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() + == m_torrent.torrent_file().info_hash()); + } + return buffer::const_interval(m_metadata.get(), m_metadata.get() + + m_metadata_size); + } + + bool received_metadata(char const* buf, int size, int offset, int total_size) + { + if (m_torrent.valid_metadata()) return false; + + if (!m_metadata || m_metadata_size < total_size) + { + m_metadata.reset(new char[total_size]); + m_metadata_size = total_size; + } + std::copy(buf, buf + size, &m_metadata[offset]); + + if (m_have_metadata.empty()) + m_have_metadata.resize(256, false); + + std::pair req = offset_to_req(std::make_pair(offset, size) + , total_size); + + TORRENT_ASSERT(req.first + req.second <= (int)m_have_metadata.size()); + + std::fill( + m_have_metadata.begin() + req.first + , m_have_metadata.begin() + req.first + req.second + , true); + + bool have_all = std::count( + m_have_metadata.begin() + , m_have_metadata.end() + , true) == 256; + + if (!have_all) return false; + + if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) + { + std::fill( + m_have_metadata.begin() + , m_have_metadata.begin() + req.first + req.second + , false); + m_metadata_progress = 0; + m_metadata_size = 0; + return false; + } + + // clear the storage for the bitfield + std::vector().swap(m_have_metadata); + std::vector().swap(m_requested_metadata); + + return true; + } + + // returns a range of the metadata that + // we should request. + std::pair metadata_request(); + + void cancel_metadata_request(std::pair req) + { + for (int i = req.first; i < req.first + req.second; ++i) + { + TORRENT_ASSERT(m_requested_metadata[i] > 0); + if (m_requested_metadata[i] > 0) + --m_requested_metadata[i]; + } + } + + // this is called from the peer_connection for + // each piece of metadata it receives + void metadata_progress(int total_size, int received) + { + m_metadata_progress += received; + m_metadata_size = total_size; + m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); + } + + void on_piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + int metadata_size() const { return m_metadata_size; } + + private: + torrent& m_torrent; + + // this buffer is filled with the info-section of + // the metadata file while downloading it from + // peers, and while sending it. + // it is mutable because it's generated lazily + mutable boost::shared_array m_metadata; + + int m_metadata_progress; + mutable int m_metadata_size; + + // this is a bitfield of size 256, each bit represents + // a piece of the metadata. It is set to one if we + // have that piece. This vector may be empty + // (size 0) if we haven't received any metadata + // or if we already have all metadata + std::vector m_have_metadata; + // this vector keeps track of how many times each meatdata + // block has been requested + std::vector m_requested_metadata; + }; + + + struct metadata_peer_plugin : peer_plugin + { + metadata_peer_plugin(torrent& t, peer_connection& pc + , metadata_plugin& tp) + : m_waiting_metadata_request(false) + , m_message_index(0) + , m_metadata_progress(0) + , m_no_metadata(min_time()) + , m_metadata_request(min_time()) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + {} + + virtual char const* type() const { return "LT_metadata"; } + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["LT_metadata"] = 14; + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value("LT_metadata", -1)); + if (index == -1) return false; + m_message_index = index; + return true; + } + + void write_metadata_request(std::pair req) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.first + req.second <= 256); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + TORRENT_ASSERT(!m_pc.associated_torrent().lock()->valid_metadata()); + + int start = req.first; + int size = req.second; + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> METADATA_REQUEST [ start: %d | size: %d ]\n" + , start, size); +#endif + + char msg[9]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + 3, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'request data' + detail::write_uint8(0, ptr); + detail::write_uint8(start, ptr); + detail::write_uint8(size - 1, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.setup_send(); + } + + void write_metadata(std::pair req) + { + TORRENT_ASSERT(req.first >= 0); + TORRENT_ASSERT(req.second > 0); + TORRENT_ASSERT(req.second <= 256); + TORRENT_ASSERT(req.first + req.second <= 256); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + + if (m_torrent.valid_metadata()) + { + std::pair offset + = req_to_offset(req, (int)m_tp.metadata().left()); + + char msg[15]; + char* ptr = msg; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> METADATA [ start: %d | total_size: %d | offset: %d | data_size: %d ]" + , req.first, req.second, offset.first, offset.second); +#endif + // yes, we have metadata, send it + detail::write_uint32(11 + offset.second, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'data packet' + detail::write_uint8(1, ptr); + detail::write_uint32((int)m_tp.metadata().left(), ptr); + detail::write_uint32(offset.first, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + char const* metadata = m_tp.metadata().begin; + m_pc.append_const_send_buffer(metadata + offset.first, offset.second); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> DONT HAVE METADATA\n"); +#endif + char msg[4+3]; + char* ptr = msg; + + // we don't have the metadata, reply with + // don't have-message + detail::write_uint32(1 + 2, ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + // means 'have no data' + detail::write_uint8(2, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + } + m_pc.setup_send(); + } + + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { + if (msg != 14) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + { + m_pc.disconnect(errors::metadata_too_large, 2); + return true; + } + + if (body.left() < 1) return true; + int type = detail::read_uint8(body.begin); + + switch (type) + { + case 0: // request + { + if (body.left() < 2) return true; + int start = detail::read_uint8(body.begin); + int size = detail::read_uint8(body.begin) + 1; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== METADATA_REQUEST [ start: %d | size: %d ]\n" + , start, size); +#endif + + if (length != 3) + { + // invalid metadata request + m_pc.disconnect(errors::invalid_metadata_request, 2); + return true; + } + + write_metadata(std::make_pair(start, size)); + } + break; + case 1: // data + { + if (body.left() < 8) return true; + + int total_size = detail::read_int32(body.begin); + int offset = detail::read_int32(body.begin); + int data_size = length - 9; + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== METADATA [ total_size: %d | offset: %d | data_size: %d ]" + ,total_size, offset, data_size); +#endif + + if (total_size > m_torrent.session().settings().max_metadata_size) + { + m_pc.disconnect(errors::metadata_too_large, 2); + return true; + } + if (total_size <= 0) + { + m_pc.disconnect(errors::invalid_metadata_size, 2); + return true; + } + if (offset > total_size || offset < 0) + { + m_pc.disconnect(errors::invalid_metadata_offset, 2); + return true; + } + if (offset + data_size > total_size) + { + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + m_tp.metadata_progress(total_size + , body.left() - m_metadata_progress); + m_metadata_progress = body.left(); + + if (body.left() < data_size) return true; + + m_waiting_metadata_request = false; + m_tp.received_metadata(body.begin, data_size + , offset, total_size); + m_metadata_progress = 0; + } + break; + case 2: // have no data + m_no_metadata = time_now(); + if (m_waiting_metadata_request) + m_tp.cancel_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = false; +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== DONT HAVE METADATA\n"); +#endif + break; + default: + { + m_pc.disconnect(errors::invalid_metadata_message, 2); + } + } + return true; + } + + virtual void tick() + { + if (m_pc.is_disconnecting()) return; + + // if we don't have any metadata, and this peer + // supports the request metadata extension + // and we aren't currently waiting for a request + // reply. Then, send a request for some metadata. + if (!m_torrent.valid_metadata() + && m_message_index != 0 + && !m_waiting_metadata_request + && has_metadata()) + { + m_last_metadata_request = m_tp.metadata_request(); + write_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = true; + m_metadata_request = time_now(); + } + } + + bool has_metadata() const + { + return time_now() - m_no_metadata > minutes(5); + } + + private: + + // this is set to true when we send a metadata + // request to this peer, and reset to false when + // we receive a reply to our request. + bool m_waiting_metadata_request; + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + // the number of bytes of metadata we have received + // so far from this per, only counting the current + // request. Any previously finished requests + // that have been forwarded to the torrent object + // do not count. + int m_metadata_progress; + + // this is set to the current time each time we get a + // "I don't have metadata" message. + ptime m_no_metadata; + + // this is set to the time when we last sent + // a request for metadata to this peer + ptime m_metadata_request; + + // if we're waiting for a metadata request + // this was the request we sent + std::pair m_last_metadata_request; + + torrent& m_torrent; + peer_connection& m_pc; + metadata_plugin& m_tp; + }; + + boost::shared_ptr metadata_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + return boost::shared_ptr(new metadata_peer_plugin(m_torrent, *pc, *this)); + } + + std::pair metadata_plugin::metadata_request() + { + // the number of blocks to request + int num_blocks = 256 / 4; + TORRENT_ASSERT(num_blocks <= 128); + + int min_element = (std::numeric_limits::max)(); + int best_index = 0; + for (int i = 0; i < 256 - num_blocks + 1; ++i) + { + int min = *std::min_element(m_requested_metadata.begin() + i + , m_requested_metadata.begin() + i + num_blocks); + min += std::accumulate(m_requested_metadata.begin() + i + , m_requested_metadata.begin() + i + num_blocks, (int)0); + + if (min_element > min) + { + best_index = i; + min_element = min; + } + } + + std::pair ret(best_index, num_blocks); + for (int i = ret.first; i < ret.first + ret.second; ++i) + m_requested_metadata[i]++; + + TORRENT_ASSERT(ret.first >= 0); + TORRENT_ASSERT(ret.second > 0); + TORRENT_ASSERT(ret.second <= 256); + TORRENT_ASSERT(ret.first + ret.second <= 256); + + return ret; + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_metadata_plugin(torrent* t, void*) + { + // don't add this extension if the torrent is private + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new metadata_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/mpi.c b/apps/Launcher/ext/libtorrent/src/mpi.c new file mode 100644 index 0000000000..e1c59ee715 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/mpi.c @@ -0,0 +1,9514 @@ +/* Start: bn_error.c */ +#include "libtorrent/tommath.h" +#ifdef BN_ERROR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static const struct { + int code; + char *msg; +} msgs[] = { + { MP_OKAY, "Successful" }, + { MP_MEM, "Out of heap" }, + { MP_VAL, "Value out of range" } +}; + +/* return a char * string for a given code */ +char *mp_error_to_string(int code) +{ + int x; + + /* scan the lookup table for the given message */ + for (x = 0; x < (int)(sizeof(msgs) / sizeof(msgs[0])); x++) { + if (msgs[x].code == code) { + return msgs[x].msg; + } + } + + /* generic reply for invalid code */ + return "Invalid error code"; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_error.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_error.c */ + +/* Start: bn_fast_mp_invmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on slow invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven (b) == 1) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy (b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* we need y = |a| */ + if ((res = mp_mod (a, b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd (&B) == 1) { + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd (&D) == 1) { + /* D = (D-x)/2 */ + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) { + goto top; + } + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add (&D, b, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + mp_exch (&D, c); + c->sign = neg; + res = MP_OKAY; + +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_invmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_mp_invmod.c */ + +/* Start: bn_fast_mp_montgomery_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_mp_montgomery_reduce.c */ + +/* Start: bn_fast_s_mp_mul_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_mul_digs.c */ + +/* Start: bn_fast_s_mp_mul_high_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* this is a modified version of fast_s_mul_digs that only produces + * output digits *above* digs. See the comments for fast_s_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed. This essentially halves the work. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +int fast_s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + pa = a->used + b->used; + if (c->alloc < pa) { + if ((res = mp_grow (c, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = a->used + b->used; + _W = 0; + for (ix = digs; ix < pa; ix++) { + int tx, ty, iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially its + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + + tmpc = c->dp + digs; + for (ix = digs; ix <= pa; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_mul_high_digs.c */ + +/* Start: bn_fast_s_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_FAST_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_fast_s_mp_sqr.c */ + +/* Start: bn_mp_2expt.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_2EXPT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_2expt.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_2expt.c */ + +/* Start: bn_mp_abs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ABS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_abs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_abs.c */ + +/* Start: bn_mp_add.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level addition (handles signs) */ +int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_add.c */ + +/* Start: bn_mp_add_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_add_d.c */ + +/* Start: bn_mp_addmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ADDMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a + b (mod c) */ +int +mp_addmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_add (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_addmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_addmod.c */ + +/* Start: bn_mp_and.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_AND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* AND two ints together */ +int +mp_and (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] &= x->dp[ix]; + } + + /* zero digits above the last from the smallest mp_int */ + for (; ix < t.used; ix++) { + t.dp[ix] = 0; + } + + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_and.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_and.c */ + +/* Start: bn_mp_clamp.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLAMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clamp.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clamp.c */ + +/* Start: bn_mp_clear.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLEAR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* clear one (frees) */ +void +mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clear.c */ + +/* Start: bn_mp_clear_multi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CLEAR_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#include + +void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear_multi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_clear_multi.c */ + +/* Start: bn_mp_cmp.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp.c */ + +/* Start: bn_mp_cmp_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare a digit */ +int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp_d.c */ + +/* Start: bn_mp_cmp_mag.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CMP_MAG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_mag.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cmp_mag.c */ + +/* Start: bn_mp_cnt_lsb.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_CNT_LSB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) +{ + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == 1) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; x < a->used && a->dp[x] == 0; x++); + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cnt_lsb.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_cnt_lsb.c */ + +/* Start: bn_mp_copy.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_copy.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_copy.c */ + +/* Start: bn_mp_count_bits.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_COUNT_BITS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_count_bits.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_count_bits.c */ + +/* Start: bn_mp_div.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div.c */ + +/* Start: bn_mp_div_2.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = a/2 */ +int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_2.c */ + +/* Start: bn_mp_div_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_2d.c */ + +/* Start: bn_mp_div_3.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_3_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) +{ + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t+t+t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_3.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_3.c */ + +/* Start: bn_mp_div_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DIV_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +static int s_is_power_of_two(mp_digit b, int *p) +{ + int x; + + for (x = 1; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1)<dp[0] & ((((mp_digit)1)<used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_div_d.c */ + +/* Start: bn_mp_dr_is_modulus.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_IS_MODULUS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) +{ + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_is_modulus.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_is_modulus.c */ + +/* Start: bn_mp_dr_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Joong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) +{ + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < m + m) { + if ((err = mp_grow (x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp (x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag (x, n) != MP_LT) { + s_mp_sub(x, n, x); + goto top; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_reduce.c */ + +/* Start: bn_mp_dr_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_DR_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) +{ + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_dr_setup.c */ + +/* Start: bn_mp_exch.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXCH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exch.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exch.c */ + +/* Start: bn_mp_expt_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPT_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* calculate c = a**b using a square-multiply algorithm */ +int mp_expt_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, x; + mp_int g; + + if ((res = mp_init_copy (&g, a)) != MP_OKAY) { + return res; + } + + /* set initial result */ + mp_set (c, 1); + + for (x = 0; x < (int) DIGIT_BIT; x++) { + /* square */ + if ((res = mp_sqr (c, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul (c, &g, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + } + + /* shift to next bit */ + b <<= 1; + } + + mp_clear (&g); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_expt_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_expt_d.c */ + +/* Start: bn_mp_exptmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else + /* no invmod */ + return MP_VAL; +#endif + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exptmod.c */ + +/* Start: bn_mp_exptmod_fast.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXPTMOD_FAST_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod_fast.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exptmod_fast.c */ + +/* Start: bn_mp_exteuclid.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_EXTEUCLID_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) +{ + mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } + } + + /* make sure U3 >= 0 */ + if (u3.sign == MP_NEG) { + mp_neg(&u1, &u1); + mp_neg(&u2, &u2); + mp_neg(&u3, &u3); + } + + /* copy result out */ + if (U1 != NULL) { mp_exch(U1, &u1); } + if (U2 != NULL) { mp_exch(U2, &u2); } + if (U3 != NULL) { mp_exch(U3, &u3); } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exteuclid.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_exteuclid.c */ + +/* Start: bn_mp_fread.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_FREAD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) +{ + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for (;;) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fread.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_fread.c */ + +/* Start: bn_mp_fwrite.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_FWRITE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +int mp_fwrite(mp_int *a, int radix, FILE *stream) +{ + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC (len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE (buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE (buf); + return MP_VAL; + } + } + + XFREE (buf); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fwrite.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_fwrite.c */ + +/* Start: bn_mp_gcd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GCD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Greatest Common Divisor using the binary method */ +int mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero (a) == MP_YES) { + return mp_abs (b, c); + } + if (mp_iszero (b) == MP_YES) { + return mp_abs (a, c); + } + + /* get copies of a and b we can modify */ + if ((res = mp_init_copy (&u, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init_copy (&v, b)) != MP_OKAY) { + goto LBL_U; + } + + /* must be positive for the remainder of the algorithm */ + u.sign = v.sign = MP_ZPOS; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + while (mp_iszero(&v) == 0) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { + goto LBL_V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +LBL_V:mp_clear (&u); +LBL_U:mp_clear (&v); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_gcd.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_gcd.c */ + +/* Start: bn_mp_get_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int * a) +{ + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used,(int)((sizeof(unsigned long)*CHAR_BIT+DIGIT_BIT-1)/DIGIT_BIT))-1; + + /* get most significant digit of result */ + res = DIGIT(a,i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a,i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_get_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_get_int.c */ + +/* Start: bn_mp_grow.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_GROW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* grow as required */ +int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_grow.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_grow.c */ + +/* Start: bn_mp_init.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* init a new mp_int */ +int mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init.c */ + +/* Start: bn_mp_init_copy.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* creates "a" then copies b into it */ +int mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_copy.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_copy.c */ + +/* Start: bn_mp_init_multi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#include + +int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_multi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_multi.c */ + +/* Start: bn_mp_init_set.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_set.c */ + +/* Start: bn_mp_init_set_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* initialize and set a digit */ +int mp_init_set_int (mp_int * a, unsigned long b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_set_int.c */ + +/* Start: bn_mp_init_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INIT_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* init an mp_init for a given size */ +int mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_init_size.c */ + +/* Start: bn_mp_invmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* hac 14.61, pp608 */ +int mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + + return MP_VAL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_invmod.c */ + +/* Start: bn_mp_invmod_slow.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_INVMOD_SLOW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* hac 14.61, pp608 */ +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod_slow.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_invmod_slow.c */ + +/* Start: bn_mp_is_square.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_IS_SQUARE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg,int *ret) +{ + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg,0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + + if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if ( (1L<<(r%11)) & 0x5C4L ) goto ERR; + if ( (1L<<(r%13)) & 0x9E4L ) goto ERR; + if ( (1L<<(r%17)) & 0x5CE8L ) goto ERR; + if ( (1L<<(r%19)) & 0x4F50CL ) goto ERR; + if ( (1L<<(r%23)) & 0x7ACCA0L ) goto ERR; + if ( (1L<<(r%29)) & 0xC2EDD0CL ) goto ERR; + if ( (1L<<(r%31)) & 0x6DE2B848L ) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t,&t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; +ERR:mp_clear(&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_is_square.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_is_square.c */ + +/* Start: bn_mp_jacobi.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_JACOBI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int mp_jacobi (mp_int * a, mp_int * p, int *c) +{ + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if p <= 0 return MP_VAL */ + if (mp_cmp_d(p, 0) != MP_GT) { + return MP_VAL; + } + + /* step 1. if a == 0, return 0 */ + if (mp_iszero (a) == 1) { + *c = 0; + return MP_OKAY; + } + + /* step 2. if a == 1, return 1 */ + if (mp_cmp_d (a, 1) == MP_EQ) { + *c = 1; + return MP_OKAY; + } + + /* default */ + s = 0; + + /* step 3. write a = a1 * 2**k */ + if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&p1)) != MP_OKAY) { + goto LBL_A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto LBL_P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = p->dp[0] & 7; + + if (residue == 1 || residue == 7) { + s = 1; + } else if (residue == 3 || residue == 5) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if ( ((p->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { + s = -s; + } + + /* if a1 == 1 we're done */ + if (mp_cmp_d (&a1, 1) == MP_EQ) { + *c = s; + } else { + /* n1 = n mod a1 */ + if ((res = mp_mod (p, &a1, &p1)) != MP_OKAY) { + goto LBL_P1; + } + if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { + goto LBL_P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +LBL_P1:mp_clear (&p1); +LBL_A1:mp_clear (&a1); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_jacobi.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_jacobi.c */ + +/* Start: bn_mp_karatsuba_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_KARATSUBA_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = |a| * |b| using Karatsuba Multiplication using + * three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and + * let n represent half of the number of digits in + * the min(a,b) + * + * a = a1 * B**n + a0 + * b = b1 * B**n + b0 + * + * Then, a * b => + a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be + * computed once. So in total three half size (half # of + * digit) multiplications are performed, a0b0, a1b1 and + * (a1+b1)(a0+b0) + * + * Note that a multiplication of half the digits requires + * 1/4th the number of single precision multiplications so in + * total after one call 25% of the single precision multiplications + * are saved. Note also that the call to mp_mul can end up back + * in this function if the a0, a1, b0, or b1 are above the threshold. + * This is known as divide-and-conquer and leads to the famous + * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than + * the standard O(N**2) that the baseline/comba methods use. + * Generally though the overhead of this method doesn't pay off + * until a certain size (N ~ 80) is reached. + */ +int mp_karatsuba_mul (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x0, x1, y0, y1, t1, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN (a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + if (mp_init_size (&y0, B) != MP_OKAY) + goto X1; + if (mp_init_size (&y1, b->used - B) != MP_OKAY) + goto Y0; + + /* init temps */ + if (mp_init_size (&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size (&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x1y1, B * 2) != MP_OKAY) + goto X0Y0; + + /* now shift the digits */ + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + { + register int x; + register mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* only need to clamp the lower words since by definition the + * upper words x1/y1 must have a known number of digits + */ + mp_clamp (&x0); + mp_clamp (&y0); + + /* now calc the products x0y0 and x1y1 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + if (mp_mul (&x0, &y0, &x0y0) != MP_OKAY) + goto X1Y1; /* x0y0 = x0*y0 */ + if (mp_mul (&x1, &y1, &x1y1) != MP_OKAY) + goto X1Y1; /* x1y1 = x1*y1 */ + + /* now calc x1+x0 and y1+y0 */ + if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x1 - x0 */ + if (s_mp_add (&y1, &y0, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ + + /* add x0y0 */ + if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (s_mp_sub (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + + /* init temps */ + if (mp_init_size (&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size (&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + register int x; + register mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + x0.used = B; + x1.used = a->used - B; + + mp_clamp (&x0); + + /* now calc the products x0*x0 and x1*x1 */ + if (mp_sqr (&x0, &x0x0) != MP_OKAY) + goto X1X1; /* x0x0 = x0*x0 */ + if (mp_sqr (&x1, &x1x1) != MP_OKAY) + goto X1X1; /* x1x1 = x1*x1 */ + + /* now calc (x1+x0)**2 */ + if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) + goto X1X1; /* t1 = x1 - x0 */ + if (mp_sqr (&t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (s_mp_sub (&t1, &t2, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; + +LBL_T: + mp_clear_multi (&t1, &t2, NULL); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lcm.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_lcm.c */ + +/* Start: bn_mp_lshd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_LSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift left a certain amount of digits */ +int mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lshd.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_lshd.c */ + +/* Start: bn_mp_mod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = a mod b, 0 <= c < b */ +int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod.c */ + +/* Start: bn_mp_mod_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* calc a value mod 2**b */ +int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod_2d.c */ + +/* Start: bn_mp_mod_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MOD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ + return mp_div_d(a, b, NULL, c); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mod_d.c */ + +/* Start: bn_mp_montgomery_calc_normalization.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_calc_normalization.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_calc_normalization.c */ + +/* Start: bn_mp_montgomery_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = n->used * 2 + 1; + if ((digs < MP_WARRAY) && + n->used < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_mp_montgomery_reduce (x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow (x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + register int iy; + register mp_digit *tmpn, *tmpx, u; + register mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu) * ((mp_word)*tmpn++) + + ((mp_word) u) + ((mp_word) * tmpx); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd (x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_reduce.c */ + +/* Start: bn_mp_montgomery_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_montgomery_setup.c */ + +/* Start: bn_mp_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level multiplication (handles sign) */ +int mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + +#ifdef BN_FAST_S_MP_MUL_DIGS_C + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul.c */ + +/* Start: bn_mp_mul_2.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_2.c */ + +/* Start: bn_mp_mul_2d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift left by a certain bit count */ +int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_2d.c */ + +/* Start: bn_mp_mul_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MUL_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_d.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mul_d.c */ + +/* Start: bn_mp_mulmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_MULMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a * b (mod c) */ +int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mulmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_mulmod.c */ + +/* Start: bn_mp_n_root.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_N_ROOT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +int mp_n_root (mp_int * a, mp_digit b, mp_int * c) +{ + mp_int t1, t2, t3; + int res, neg; + + /* input must be positive if b is even */ + if ((b & 1) == 0 && a->sign == MP_NEG) { + return MP_VAL; + } + + if ((res = mp_init (&t1)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init (&t3)) != MP_OKAY) { + goto LBL_T2; + } + + /* if a is negative fudge the sign but keep track */ + neg = a->sign; + a->sign = MP_ZPOS; + + /* t2 = 2 */ + mp_set (&t2, 2); + + do { + /* t1 = t2 */ + if ((res = mp_copy (&t2, &t1)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { + goto LBL_T3; + } + + if ((res = mp_sub (&t1, &t3, &t2)) != MP_OKAY) { + goto LBL_T3; + } + } while (mp_cmp (&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + for (;;) { + if ((res = mp_expt_d (&t1, b, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + if (mp_cmp (&t2, a) == MP_GT) { + if ((res = mp_sub_d (&t1, 1, &t1)) != MP_OKAY) { + goto LBL_T3; + } + } else { + break; + } + } + + /* reset the sign of a first */ + a->sign = neg; + + /* set the result */ + mp_exch (&t1, c); + + /* set the sign of the result */ + c->sign = neg; + + res = MP_OKAY; + +LBL_T3:mp_clear (&t3); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_n_root.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_n_root.c */ + +/* Start: bn_mp_neg.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_NEG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* b = -a */ +int mp_neg (mp_int * a, mp_int * b) +{ + int res; + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + if (mp_iszero(b) != MP_YES) { + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } else { + b->sign = MP_ZPOS; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_neg.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_neg.c */ + +/* Start: bn_mp_or.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_OR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* OR two ints together */ +int mp_or (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] |= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_or.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_or.c */ + +/* Start: bn_mp_prime_fermat.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_FERMAT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat (mp_int * a, mp_int * b, int *result) +{ + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init (&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { + goto LBL_T; + } + + /* is it equal to b? */ + if (mp_cmp (&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +LBL_T:mp_clear (&t); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_fermat.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_fermat.c */ + +/* Start: bn_mp_prime_is_divisible.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_IS_DIVISIBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible (mp_int * a, int *result) +{ + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod LBL_prime_tab[ix] */ + if ((err = mp_mod_d (a, ltm_prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_divisible.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_is_divisible.c */ + +/* Start: bn_mp_prime_is_prime.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_IS_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime (mp_int * a, int t, int *result) +{ + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init (&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set (&b, ltm_prime_tab[ix]); + + if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + + if (res == MP_NO) { + goto LBL_B; + } + } + + /* passed the test */ + *result = MP_YES; +LBL_B:mp_clear (&b); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_prime.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_is_prime.c */ + +/* Start: bn_mp_prime_miller_rabin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_MILLER_RABIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) +{ + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { + goto LBL_R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init (&y)) != MP_OKAY) { + goto LBL_R; + } + if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y != 1 and y != n1 do */ + if (mp_cmp_d (&y, 1) != MP_EQ && mp_cmp (&y, &n1) != MP_EQ) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && mp_cmp (&y, &n1) != MP_EQ) { + if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d (&y, 1) == MP_EQ) { + goto LBL_Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp (&y, &n1) != MP_EQ) { + goto LBL_Y; + } + } + + /* probably prime now */ + *result = MP_YES; +LBL_Y:mp_clear (&y); +LBL_R:mp_clear (&r); +LBL_N1:mp_clear (&n1); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_miller_rabin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_miller_rabin.c */ + +/* Start: bn_mp_prime_next_prime.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_NEXT_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) +{ + int err, res, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE-1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((ltm_prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((ltm_prime_tab[y] & 3) == 3) { + mp_set(a, ltm_prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, ltm_prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; + } + } else { + if (mp_iseven(a) == 1) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (;;) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= ltm_prime_tab[x]) { + res_tab[x] -= ltm_prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while (y == 1 && step < ((((mp_digit)1)<= ((((mp_digit)1)< size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return sizes[x-1].t + 1; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_rabin_miller_trials.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_rabin_miller_trials.c */ + +/* Start: bn_mp_prime_random_ex.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_PRIME_RANDOM_EX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) +{ + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if (size <= 1 || t <= 0) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if (flags & LTM_PRIME_SAFE) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size>>3) + ((size&7)?1:0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = ((size&7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; + if (flags & LTM_PRIME_2MSB_ON) { + maskOR_msb |= 0x80 >> ((9 - size) & 7); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 1; + if (flags & LTM_PRIME_BBS) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize-1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + if (res == MP_NO) { + continue; + } + + if (flags & LTM_PRIME_SAFE) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } + if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + } + } while (res == MP_NO); + + if (flags & LTM_PRIME_SAFE) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_random_ex.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_prime_random_ex.c */ + +/* Start: bn_mp_radix_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RADIX_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* returns size of ASCII reprensentation */ +int mp_radix_size (mp_int * a, int radix, int *size) +{ + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; + return MP_OKAY; + } + + /* make sure the radix is in range */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + if (mp_iszero(a) == MP_YES) { + *size = 2; + return MP_OKAY; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (a->sign == MP_NEG) { + ++digs; + } + + /* init a copy of the input */ + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* force temp to positive */ + t.sign = MP_ZPOS; + + /* fetch out all of the digits */ + while (mp_iszero (&t) == MP_NO) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + ++digs; + } + mp_clear (&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_size.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_radix_size.c */ + +/* Start: bn_mp_radix_smap.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RADIX_SMAP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_smap.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_radix_smap.c */ + +/* Start: bn_mp_rand.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* makes a pseudo-random int of a given size */ +int +mp_rand (mp_int * a, int digits) +{ + int res; + mp_digit d; + + mp_zero (a); + if (digits <= 0) { + return MP_OKAY; + } + + /* first place a random non-zero digit */ + do { + d = ((mp_digit) abs (rand ())) & MP_MASK; + } while (d == 0); + + if ((res = mp_add_d (a, d, a)) != MP_OKAY) { + return res; + } + + while (--digits > 0) { + if ((res = mp_lshd (a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rand.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_rand.c */ + +/* Start: bn_mp_read_radix.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_RADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read a string [ASCII] in a given radix */ +int mp_read_radix (mp_int * a, const char *str, int radix) +{ + int y, res, neg; + char ch; + + /* zero the digit bignum */ + mp_zero(a); + + /* make sure the radix is ok */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero (a); + + /* process each digit of the string */ + while (*str) { + /* if the radix < 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (char) ((radix < 36) ? toupper (*str) : *str); + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + if (y < radix) { + if ((res = mp_mul_d (a, (mp_digit) radix, a)) != MP_OKAY) { + return res; + } + if ((res = mp_add_d (a, (mp_digit) y, a)) != MP_OKAY) { + return res; + } + } else { + break; + } + ++str; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != 1) { + a->sign = neg; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_radix.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_radix.c */ + +/* Start: bn_mp_read_signed_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int mp_read_signed_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = MP_NEG; + } + + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_signed_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_signed_bin.c */ + +/* Start: bn_mp_read_unsigned_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_READ_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_unsigned_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_read_unsigned_bin.c */ + +/* Start: bn_mp_reduce.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if ((res = mp_add (x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce.c */ + +/* Start: bn_mp_reduce_2k.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces a modulo n where n is of the form 2**p - d */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k.c */ + +/* Start: bn_mp_reduce_2k_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_l.c */ + +/* Start: bn_mp_reduce_2k_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d) +{ + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_setup.c */ + +/* Start: bn_mp_reduce_2k_setup_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines the setup value */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_2k_setup_l.c */ + +/* Start: bn_mp_reduce_is_2k.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) +{ + int ix, iy, iw; + mp_digit iz; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return MP_NO; + } + iz <<= 1; + if (iz > (mp_digit)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return MP_YES; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_is_2k.c */ + +/* Start: bn_mp_reduce_is_2k_l.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* determines if reduce_2k_l can be used */ +int mp_reduce_is_2k_l(mp_int *a) +{ + int ix, iy; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + /* if more than half of the digits are -1 we're sold */ + for (iy = ix = 0; ix < a->used; ix++) { + if (a->dp[ix] == MP_MASK) { + ++iy; + } + } + return (iy >= (a->used/2)) ? MP_YES : MP_NO; + + } + return MP_NO; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k_l.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_is_2k_l.c */ + +/* Start: bn_mp_reduce_setup.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_REDUCE_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_setup.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_reduce_setup.c */ + +/* Start: bn_mp_rshd.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_RSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shift right a certain amount of digits */ +void mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rshd.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_rshd.c */ + +/* Start: bn_mp_set.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set to a digit */ +void mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_set.c */ + +/* Start: bn_mp_set_int.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set a 32-bit const */ +int mp_set_int (mp_int * a, unsigned long b) +{ + int x, res; + + mp_zero (a); + + /* set four bits at a time */ + for (x = 0; x < 8; x++) { + /* shift the number up four bits */ + if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { + return res; + } + + /* OR in the top four bits of the source */ + a->dp[0] |= (b >> 28) & 15; + + /* shift the source up to the next four bits */ + b <<= 4; + + /* ensure that digits are not clamped off */ + a->used += 1; + } + mp_clamp (a); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set_int.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_set_int.c */ + +/* Start: bn_mp_shrink.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SHRINK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* shrink a bignum */ +int mp_shrink (mp_int * a) +{ + mp_digit *tmp; + if (a->alloc != a->used && a->used > 0) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + a->alloc = a->used; + } + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_shrink.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_shrink.c */ + +/* Start: bn_mp_signed_bin_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the size for an signed equivalent */ +int mp_signed_bin_size (mp_int * a) +{ + return 1 + mp_unsigned_bin_size (a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_signed_bin_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_signed_bin_size.c */ + +/* Start: bn_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqr.c */ + +/* Start: bn_mp_sqrmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQRMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* c = a * a (mod b) */ +int +mp_sqrmod (mp_int * a, mp_int * b, mp_int * c) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sqr (a, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, b, c); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrmod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqrmod.c */ + +/* Start: bn_mp_sqrt.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SQRT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) +{ + int res; + mp_int t1,t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd (&t1,t1.used/2); + + /* t1 > 0 */ + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1,&t2) == MP_GT); + + mp_exch(&t1,ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrt.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sqrt.c */ + +/* Start: bn_mp_sub.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* high level subtraction (handles signs) */ +int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sub.c */ + +/* Start: bn_mp_sub_d.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUB_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub_d.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_sub_d.c */ + +/* Start: bn_mp_submod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_SUBMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* d = a - b (mod c) */ +int +mp_submod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sub (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_submod.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_submod.c */ + +/* Start: bn_mp_to_signed_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin (mp_int * a, unsigned char *b) +{ + int res; + + if ((res = mp_to_unsigned_bin (a, b + 1)) != MP_OKAY) { + return res; + } + b[0] = (unsigned char) ((a->sign == MP_ZPOS) ? 0 : 1); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_signed_bin.c */ + +/* Start: bn_mp_to_signed_bin_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ + if (*outlen < (unsigned long)mp_signed_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_signed_bin_size(a); + return mp_to_signed_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin_n.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_signed_bin_n.c */ + +/* Start: bn_mp_to_unsigned_bin.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_unsigned_bin.c */ + +/* Start: bn_mp_to_unsigned_bin_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ + if (*outlen < (unsigned long)mp_unsigned_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_unsigned_bin_size(a); + return mp_to_unsigned_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin_n.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_to_unsigned_bin_n.c */ + +/* Start: bn_mp_toom_mul.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TOOM_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplication using the Toom-Cook 3-way algorithm + * + * Much more complicated than Karatsuba but has a lower + * asymptotic running time of O(N**1.464). This algorithm is + * only particularly useful on VERY large inputs + * (we're talking 1000s of digits here...). +*/ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B*2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_mul.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toom_mul.c */ + +/* Start: bn_mp_toom_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TOOM_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) +{ + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toom_sqr.c */ + +/* Start: bn_mp_toradix.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TORADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int mp_toradix (mp_int * a, char *str, int radix) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the radix */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + ++_s; + *str++ = '-'; + t.sign = MP_ZPOS; + } + + digs = 0; + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toradix.c */ + +/* Start: bn_mp_toradix_n.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_TORADIX_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) + * + * Stores upto maxlen-1 chars and always a NULL byte + */ +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the maxlen, radix */ + if (maxlen < 2 || radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == MP_YES) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + /* we have to reverse our digits later... but not the - sign!! */ + ++_s; + + /* store the flag and mark the number as positive */ + *str++ = '-'; + t.sign = MP_ZPOS; + + /* subtract a char */ + --maxlen; + } + + digs = 0; + while (mp_iszero (&t) == 0) { + if (--maxlen < 1) { + /* no more room */ + break; + } + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix_n.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_toradix_n.c */ + +/* Start: bn_mp_unsigned_bin_size.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_UNSIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* get the size for an unsigned equivalent */ +int mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_unsigned_bin_size.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_unsigned_bin_size.c */ + +/* Start: bn_mp_xor.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_XOR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* XOR two ints together */ +int +mp_xor (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] ^= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_xor.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_xor.c */ + +/* Start: bn_mp_zero.c */ +#include "libtorrent/tommath.h" +#ifdef BN_MP_ZERO_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* set to zero */ +void mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_zero.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_mp_zero.c */ + +/* Start: bn_prime_tab.c */ +#include "libtorrent/tommath.h" +#ifdef BN_PRIME_TAB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +const mp_digit ltm_prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, +#ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 +#endif +}; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_prime_tab.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_prime_tab.c */ + +/* Start: bn_reverse.c */ +#include "libtorrent/tommath.h" +#ifdef BN_REVERSE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* reverse an array, used for radix code */ +void +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_reverse.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_reverse.c */ + +/* Start: bn_s_mp_add.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_add.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_add.c */ + +/* Start: bn_s_mp_exptmod.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_exptmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_exptmod.c */ + +/* Start: bn_s_mp_mul_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_mul_digs.c */ + +/* Start: bn_s_mp_mul_high_digs.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_mul_high_digs.c */ + +/* Start: bn_s_mp_sqr.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sqr.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_sqr.c */ + +/* Start: bn_s_mp_sub.c */ +#include "libtorrent/tommath.h" +#ifdef BN_S_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sub.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bn_s_mp_sub.c */ + +/* Start: bncore.c */ +#include "libtorrent/tommath.h" +#ifdef BNCORE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF +------------------------------------------------------------- + Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) + AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 + +*/ + +int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; +#endif + +/* $Source: /cvs/libtom/libtommath/bncore.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ + +/* End: bncore.c */ + + +/* EOF */ diff --git a/apps/Launcher/ext/libtorrent/src/natpmp.cpp b/apps/Launcher/ext/libtorrent/src/natpmp.cpp new file mode 100644 index 0000000000..5f12f49f10 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/natpmp.cpp @@ -0,0 +1,702 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#include "libtorrent/config.hpp" +#if defined TORRENT_OS2 +#include +#endif + +#include +#include + +#include "libtorrent/natpmp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/io_service.hpp" +//#include "libtorrent/random.hpp" + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +//#define NATPMP_LOG + +#ifdef NATPMP_LOG +#include +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +using namespace libtorrent; + +natpmp::natpmp(io_service& ios, address const& listen_interface + , portmap_callback_t const& cb, log_callback_t const& lcb) + : m_callback(cb) + , m_log_callback(lcb) + , m_currently_mapping(-1) + , m_retry_count(0) + , m_socket(ios) + , m_send_timer(ios) + , m_refresh_timer(ios) + , m_next_refresh(-1) + , m_disabled(false) + , m_abort(false) +{ + // unfortunately async operations rely on the storage + // for this array not to be reallocated, by passing + // around pointers to its elements. so reserve size for now + m_mappings.reserve(10); + rebind(listen_interface); +} + +void natpmp::rebind(address const& listen_interface) +{ + mutex::scoped_lock l(m_mutex); + + error_code ec; + address gateway = get_default_gateway(m_socket.get_io_service(), ec); + if (ec) + { + char msg[200]; + snprintf(msg, sizeof(msg), "failed to find default route: %s" + , convert_from_native(ec.message()).c_str()); + log(msg, l); + disable(ec, l); + return; + } + + m_disabled = false; + + udp::endpoint nat_endpoint(gateway, 5351); + if (nat_endpoint == m_nat_endpoint) return; + m_nat_endpoint = nat_endpoint; + + char msg[200]; + snprintf(msg, sizeof(msg), "found router at: %s" + , print_address(m_nat_endpoint.address()).c_str()); + log(msg, l); + + m_socket.open(udp::v4(), ec); + if (ec) + { + disable(ec, l); + return; + } + m_socket.bind(udp::endpoint(address_v4::any(), 0), ec); + if (ec) + { + disable(ec, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::on_reply"); +#endif + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); + send_get_ip_address_request(l); + + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol != none + || i->action != mapping_t::action_none) + continue; + i->action = mapping_t::action_add; + update_mapping(i - m_mappings.begin(), l); + } +} + +void natpmp::send_get_ip_address_request(mutex::scoped_lock& l) +{ + using namespace libtorrent::detail; + + char buf[2]; + char* out = buf; + write_uint8(0, out); // NAT-PMP version + write_uint8(0, out); // public IP address request opcode + log("==> get public IP address", l); + + error_code ec; + m_socket.send_to(asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); +} + +bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + +void natpmp::log(char const* msg, mutex::scoped_lock& l) +{ + l.unlock(); + m_log_callback(msg); + l.lock(); +} + +void natpmp::disable(error_code const& ec, mutex::scoped_lock& l) +{ + m_disabled = true; + + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none) continue; + i->protocol = none; + int index = i - m_mappings.begin(); + l.unlock(); + m_callback(index, address(), 0, ec); + l.lock(); + } + close_impl(l); +} + +void natpmp::delete_mapping(int index) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return; + mapping_t& m = m_mappings[index]; + + if (m.protocol == none) return; + if (!m.map_sent) + { + m.action = mapping_t::action_none; + m.protocol = none; + return; + } + + m.action = mapping_t::action_delete; + update_mapping(index, l); +} + +int natpmp::add_mapping(protocol_type p, int external_port, int local_port) +{ + mutex::scoped_lock l(m_mutex); + + if (m_disabled) return -1; + + std::vector::iterator i = std::find_if(m_mappings.begin() + , m_mappings.end(), boost::bind(&mapping_t::protocol, _1) == int(none)); + if (i == m_mappings.end()) + { + m_mappings.push_back(mapping_t()); + i = m_mappings.end() - 1; + } + i->protocol = p; + i->external_port = external_port; + i->local_port = local_port; + i->action = mapping_t::action_add; + + int mapping_index = i - m_mappings.begin(); + +#ifdef NATPMP_LOG + ptime now = time_now(); + for (std::vector::iterator m = m_mappings.begin() + , end(m_mappings.end()); m != end; ++m) + { + std::cout << " ADD MAPPING: " << mapping_index << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; + } +#endif + + update_mapping(mapping_index, l); + return mapping_index; +} + +void natpmp::try_next_mapping(int i, mutex::scoped_lock& l) +{ +#ifdef NATPMP_LOG + ptime now = time_now(); + for (std::vector::iterator m = m_mappings.begin() + , end(m_mappings.end()); m != end; ++m) + { + std::cout << " " << (m - m_mappings.begin()) << " [ " + "proto: " << (m->protocol == none ? "none" : m->protocol == tcp ? "tcp" : "udp") + << " port: " << m->external_port + << " local-port: " << m->local_port + << " action: " << (m->action == mapping_t::action_none ? "none" : m->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(m->expires - now) + << " ]" << std::endl; + } +#endif + if (i < int(m_mappings.size()) - 1) + { + update_mapping(i + 1, l); + return; + } + + std::vector::iterator m = std::find_if( + m_mappings.begin(), m_mappings.end() + , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); + + if (m == m_mappings.end()) + { + if (m_abort) + { + error_code ec; + m_send_timer.cancel(ec); + m_socket.close(ec); + } +#ifdef NATPMP_LOG + std::cout << " done" << (m_abort?" shutting down":"") << std::endl; +#endif + return; + } + +#ifdef NATPMP_LOG + std::cout << " updating " << (m - m_mappings.begin()) << std::endl; +#endif + + update_mapping(m - m_mappings.begin(), l); +} + +void natpmp::update_mapping(int i, mutex::scoped_lock& l) +{ + if (i == int(m_mappings.size())) + { + if (m_abort) + { + error_code ec; + m_send_timer.cancel(ec); + m_socket.close(ec); + } +#ifdef NATPMP_LOG + std::cout << " done" << (m_abort?" shutting down":"") << std::endl; +#endif + return; + } + + natpmp::mapping_t& m = m_mappings[i]; + if (m.action == mapping_t::action_none + || m.protocol == none) + { + try_next_mapping(i, l); + return; + } + + if (m_currently_mapping == -1) + { + // the socket is not currently in use + // send out a mapping request + m_retry_count = 0; + send_map_request(i, l); + } +} + +void natpmp::send_map_request(int i, mutex::scoped_lock& l) +{ + using namespace libtorrent::detail; + + TORRENT_ASSERT(m_currently_mapping == -1 + || m_currently_mapping == i); + m_currently_mapping = i; + mapping_t& m = m_mappings[i]; + TORRENT_ASSERT(m.action != mapping_t::action_none); + char buf[12]; + char* out = buf; + write_uint8(0, out); // NAT-PMP version + write_uint8(m.protocol, out); // map "protocol" + write_uint16(0, out); // reserved + write_uint16(m.local_port, out); // private port + write_uint16(m.external_port, out); // requested public port + int ttl = m.action == mapping_t::action_add ? 3600 : 0; + write_uint32(ttl, out); // port mapping lifetime + + char msg[200]; + snprintf(msg, sizeof(msg), "==> port map [ mapping: %d action: %s" + " proto: %s local: %u external: %u ttl: %u ]" + , i, m.action == mapping_t::action_add ? "add" : "delete" + , m.protocol == udp ? "udp" : "tcp" + , m.local_port, m.external_port, ttl); + log(msg, l); + + error_code ec; + m_socket.send_to(asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec); + m.map_sent = true; + m.outstanding_request = true; + if (m_abort) + { + // when we're shutting down, ignore the + // responses and just remove all mappings + // immediately + m_currently_mapping = -1; + m.action = mapping_t::action_none; + try_next_mapping(i, l); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::resend_request"); +#endif + // linear back-off instead of exponential + ++m_retry_count; + m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); + m_send_timer.async_wait(boost::bind(&natpmp::resend_request, self(), i, _1)); + } +} + +void natpmp::resend_request(int i, error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::resend_request"); +#endif + if (e) return; + mutex::scoped_lock l(m_mutex); + if (m_currently_mapping != i) return; + + // if we're shutting down, don't retry, just move on + // to the next mapping + if (m_retry_count >= 9 || m_abort) + { + m_currently_mapping = -1; + m_mappings[i].action = mapping_t::action_none; + // try again in two hours + m_mappings[i].expires = time_now() + hours(2); + try_next_mapping(i, l); + return; + } + send_map_request(i, l); +} + +void natpmp::on_reply(error_code const& e + , std::size_t bytes_transferred) +{ + mutex::scoped_lock l(m_mutex); + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::on_reply"); +#endif + + using namespace libtorrent::detail; + if (e) + { + char msg[200]; + snprintf(msg, sizeof(msg), "error on receiving reply: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::on_reply"); +#endif + // make a copy of the response packet buffer + // to avoid overwriting it in the next receive call + char msg_buf[16]; + memcpy(msg_buf, m_response_buffer, bytes_transferred); + + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, boost::bind(&natpmp::on_reply, self(), _1, _2)); + + // simulate packet loss +/* + if ((random() % 2) == 0) + { + log(" simulating drop", l); + return; + } +*/ + if (m_remote != m_nat_endpoint) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet from wrong IP: %s" + , print_endpoint(m_remote).c_str()); + log(msg, l); + return; + } + + error_code ec; + m_send_timer.cancel(ec); + + if (bytes_transferred < 12) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); + log(msg, l); + return; + } + + char* in = msg_buf; + int version = read_uint8(in); + int cmd = read_uint8(in); + int result = read_uint16(in); + int time = read_uint32(in); + + if (cmd == 128) + { + // public IP request response + m_external_ip = read_v4_address(in); + + char msg[200]; + snprintf(msg, sizeof(msg), "<== public IP address [ %s ]", print_address(m_external_ip).c_str()); + log(msg, l); + return; + + } + + if (bytes_transferred < 16) + { + char msg[200]; + snprintf(msg, sizeof(msg), "received packet of invalid size: %d", int(bytes_transferred)); + log(msg, l); + return; + } + + int private_port = read_uint16(in); + int public_port = read_uint16(in); + int lifetime = read_uint32(in); + + (void)time; // to remove warning + + int protocol = (cmd - 128 == 1)?udp:tcp; + + char msg[200]; + int num_chars = snprintf(msg, sizeof(msg), "<== port map [" + " protocol: %s local: %u external: %u ttl: %u ]" + , (cmd - 128 == 1 ? "udp" : "tcp") + , private_port, public_port, lifetime); + + if (version != 0) + { + snprintf(msg + num_chars, sizeof(msg) - num_chars, "unexpected version: %u" + , version); + log(msg, l); + } + + mapping_t* m = 0; + int index = -1; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (private_port != i->local_port) continue; + if (protocol != i->protocol) continue; + if (!i->map_sent) continue; + if (!i->outstanding_request) continue; + m = &*i; + index = i - m_mappings.begin(); + break; + } + + if (m == 0) + { + snprintf(msg + num_chars, sizeof(msg) - num_chars, " not found in map table"); + log(msg, l); + return; + } + m->outstanding_request = false; + + log(msg, l); + + if (public_port == 0 || lifetime == 0) + { + // this means the mapping was + // successfully closed + m->protocol = none; + } + else + { + m->expires = time_now() + seconds(int(lifetime * 0.7f)); + m->external_port = public_port; + } + + if (result != 0) + { + int errors[] = + { + errors::unsupported_protocol_version, + errors::natpmp_not_authorized, + errors::network_failure, + errors::no_resources, + errors::unsupported_opcode, + }; + int ev = errors::no_error; + if (result >= 1 && result <= 5) ev = errors[result - 1]; + + m->expires = time_now() + hours(2); + l.unlock(); + m_callback(index, address(), 0, error_code(ev, get_libtorrent_category())); + l.lock(); + } + else if (m->action == mapping_t::action_add) + { + l.unlock(); + m_callback(index, m_external_ip, m->external_port, + error_code(errors::no_error, get_libtorrent_category())); + l.lock(); + } + + if (m_abort) return; + + m_currently_mapping = -1; + m->action = mapping_t::action_none; + m_send_timer.cancel(ec); + update_expiration_timer(l); + try_next_mapping(index, l); +} + +void natpmp::update_expiration_timer(mutex::scoped_lock& l) +{ + if (m_abort) return; + + ptime now = time_now() + milliseconds(100); +#ifdef NATPMP_LOG + std::cout << time_now_string() << " update_expiration_timer " << std::endl; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + std::cout << " " << (i - m_mappings.begin()) << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; + } +#endif + ptime min_expire = now + seconds(3600); + int min_index = -1; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none + || i->action != mapping_t::action_none) continue; + int index = i - m_mappings.begin(); + if (i->expires < now) + { + char msg[200]; + snprintf(msg, sizeof(msg), "mapping %u expired", index); + log(msg, l); + i->action = mapping_t::action_add; + if (m_next_refresh == index) m_next_refresh = -1; + update_mapping(index, l); + } + else if (i->expires < min_expire) + { + min_expire = i->expires; + min_index = index; + } + } + + // this is already the mapping we're waiting for + if (m_next_refresh == min_index) return; + + if (min_index >= 0) + { +#ifdef NATPMP_LOG + std::cout << time_now_string() << " next expiration [" + " i: " << min_index + << " ttl: " << total_seconds(min_expire - time_now()) + << " ]" << std::endl; +#endif + error_code ec; + if (m_next_refresh >= 0) m_refresh_timer.cancel(ec); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("natpmp::mapping_expired"); +#endif + m_refresh_timer.expires_from_now(min_expire - now, ec); + m_refresh_timer.async_wait(boost::bind(&natpmp::mapping_expired, self(), _1, min_index)); + m_next_refresh = min_index; + } +} + +void natpmp::mapping_expired(error_code const& e, int i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("natpmp::mapping_expired"); +#endif + if (e) return; + mutex::scoped_lock l(m_mutex); + char msg[200]; + snprintf(msg, sizeof(msg), "mapping %u expired", i); + log(msg, l); + m_mappings[i].action = mapping_t::action_add; + if (m_next_refresh == i) m_next_refresh = -1; + update_mapping(i, l); +} + +void natpmp::close() +{ + mutex::scoped_lock l(m_mutex); + close_impl(l); +} + +void natpmp::close_impl(mutex::scoped_lock& l) +{ + m_abort = true; + log("closing", l); +#ifdef NATPMP_LOG + std::cout << time_now_string() << " close" << std::endl; + ptime now = time_now(); +#endif + if (m_disabled) return; + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { +#ifdef NATPMP_LOG + std::cout << " " << (i - m_mappings.begin()) << " [ " + "proto: " << (i->protocol == none ? "none" : i->protocol == tcp ? "tcp" : "udp") + << " port: " << i->external_port + << " local-port: " << i->local_port + << " action: " << (i->action == mapping_t::action_none ? "none" : i->action == mapping_t::action_add ? "add" : "delete") + << " ttl: " << total_seconds(i->expires - now) + << " ]" << std::endl; +#endif + if (i->protocol == none) continue; + i->action = mapping_t::action_delete; + } + error_code ec; + m_refresh_timer.cancel(ec); + m_currently_mapping = -1; + update_mapping(0, l); +} + diff --git a/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp b/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp new file mode 100644 index 0000000000..b0178a611f --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/packet_buffer.cpp @@ -0,0 +1,215 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include // free and calloc +#include "libtorrent/packet_buffer.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/invariant_check.hpp" + +namespace libtorrent { + + bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs + , boost::uint32_t mask); + + packet_buffer::packet_buffer() + : m_storage(0) + , m_capacity(0) + , m_size(0) + , m_first(0) + , m_last(0) + {} + +#if TORRENT_USE_INVARIANT_CHECKS + void packet_buffer::check_invariant() const + { + int count = 0; + for (int i = 0; i < int(m_capacity); ++i) + { + count += m_storage[i] ? 1 : 0; + } + TORRENT_ASSERT(count == int(m_size)); + } +#endif + + packet_buffer::~packet_buffer() + { + free(m_storage); + } + + void* packet_buffer::insert(index_type idx, void* value) + { + INVARIANT_CHECK; + + TORRENT_ASSERT_VAL(idx <= 0xffff, idx); + // you're not allowed to insert NULLs! + TORRENT_ASSERT(value); + + if (value == 0) return remove(idx); + + if (m_size != 0) + { + if (compare_less_wrap(idx, m_first, 0xffff)) + { + // Index comes before m_first. If we have room, we can simply + // adjust m_first backward. + + std::size_t free_space = 0; + + for (index_type i = (m_first - 1) & (m_capacity - 1); + i != (m_first & (m_capacity - 1)); i = (i - 1) & (m_capacity - 1)) + { + if (m_storage[i & (m_capacity - 1)]) + break; + ++free_space; + } + + if (((m_first - idx) & 0xffff) > free_space) + reserve(((m_first - idx) & 0xffff) + m_capacity - free_space); + + m_first = idx; + } + else if (idx >= m_first + m_capacity) + { + reserve(idx - m_first + 1); + } + else if (idx < m_first) + { + // We have wrapped. + if (idx >= ((m_first + m_capacity) & 0xffff) && m_capacity < 0xffff) + { + reserve(m_capacity + (idx + 1 - ((m_first + m_capacity) & 0xffff))); + } + } + if (compare_less_wrap(m_last, (idx + 1) & 0xffff, 0xffff)) + m_last = (idx + 1) & 0xffff; + } + else + { + m_first = idx; + m_last = (idx + 1) & 0xffff; + } + + if (m_capacity == 0) reserve(16); + + void* old_value = m_storage[idx & (m_capacity - 1)]; + m_storage[idx & (m_capacity - 1)] = value; + + if (m_size == 0) m_first = idx; + // if we're just replacing an old value, the number + // of elements in the buffer doesn't actually increase + if (old_value == 0) ++m_size; + + TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); + return old_value; + } + + void* packet_buffer::at(index_type idx) const + { + INVARIANT_CHECK; + if (idx >= m_first + m_capacity) + return 0; + + if (compare_less_wrap(idx, m_first, 0xffff)) + { + return 0; + } + + const int mask = (m_capacity - 1); + return m_storage[idx & mask]; + } + + void packet_buffer::reserve(std::size_t size) + { + INVARIANT_CHECK; + TORRENT_ASSERT_VAL(size <= 0xffff, size); + std::size_t new_size = m_capacity == 0 ? 16 : m_capacity; + + while (new_size < size) + new_size <<= 1; + + void** new_storage = (void**)malloc(sizeof(void*) * new_size); + + for (index_type i = 0; i < new_size; ++i) + new_storage[i] = 0; + + for (index_type i = m_first; i < (m_first + m_capacity); ++i) + new_storage[i & (new_size - 1)] = m_storage[i & (m_capacity - 1)]; + + free(m_storage); + + m_storage = new_storage; + m_capacity = new_size; + } + + void* packet_buffer::remove(index_type idx) + { + INVARIANT_CHECK; + // TODO: use compare_less_wrap for this comparison as well + if (idx >= m_first + m_capacity) + return 0; + + if (compare_less_wrap(idx, m_first, 0xffff)) + return 0; + + const int mask = (m_capacity - 1); + void* old_value = m_storage[idx & mask]; + m_storage[idx & mask] = 0; + + if (old_value) + { + --m_size; + if (m_size == 0) m_last = m_first; + } + + if (idx == m_first && m_size != 0) + { + ++m_first; + for (boost::uint32_t i = 0; i < m_capacity; ++i, ++m_first) + if (m_storage[m_first & mask]) break; + m_first &= 0xffff; + } + + if (((idx + 1) & 0xffff) == m_last && m_size != 0) + { + --m_last; + for (boost::uint32_t i = 0; i < m_capacity; ++i, --m_last) + if (m_storage[m_last & mask]) break; + ++m_last; + m_last &= 0xffff; + } + + TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); + return old_value; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/parse_url.cpp b/apps/Launcher/ext/libtorrent/src/parse_url.cpp new file mode 100644 index 0000000000..06f2fb42b0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/parse_url.cpp @@ -0,0 +1,135 @@ +/* + +Copyright (c) 2008-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/parse_url.hpp" +#include + +namespace libtorrent +{ + + // returns protocol, auth, hostname, port, path + boost::tuple + parse_url_components(std::string url, error_code& ec) + { + std::string hostname; // hostname only + std::string auth; // user:pass + std::string protocol; // http or https for instance + int port = -1; + + std::string::iterator at; + std::string::iterator colon; + std::string::iterator port_pos; + + // PARSE URL + std::string::iterator start = url.begin(); + // remove white spaces in front of the url + while (start != url.end() && (*start == ' ' || *start == '\t')) + ++start; + std::string::iterator end + = std::find(url.begin(), url.end(), ':'); + protocol.assign(start, end); + + if (end == url.end()) + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + if (end == url.end() || *end != '/') + { + ec = errors::unsupported_url_protocol; + goto exit; + } + ++end; + start = end; + + at = std::find(start, url.end(), '@'); + colon = std::find(start, url.end(), ':'); + end = std::find(start, url.end(), '/'); + + if (at != url.end() + && colon != url.end() + && colon < at + && at < end) + { + auth.assign(start, at); + start = at; + ++start; + } + + // this is for IPv6 addresses + if (start != url.end() && *start == '[') + { + port_pos = std::find(start, url.end(), ']'); + if (port_pos == url.end()) + { + ec = errors::expected_close_bracket_in_address; + goto exit; + } + port_pos = std::find(port_pos, url.end(), ':'); + } + else + { + port_pos = std::find(start, url.end(), ':'); + } + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + for (std::string::iterator i = port_pos; i < end; ++i) + { + if (is_digit(*i)) continue; + ec = errors::invalid_port; + goto exit; + } + port = std::atoi(std::string(port_pos, end).c_str()); + } + else + { + hostname.assign(start, end); + } + + start = end; +exit: + return boost::make_tuple(protocol, auth, hostname, port + , std::string(start, url.end())); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp b/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp new file mode 100644 index 0000000000..d758b5a95d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/pe_crypto.cpp @@ -0,0 +1,373 @@ +/* + +Copyright (c) 2007-2014, Un Shyam & Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#include +#include + +#if defined TORRENT_USE_GCRYPT +#include +#elif defined TORRENT_USE_OPENSSL +#include +#include "libtorrent/random.hpp" +#elif defined TORRENT_USE_TOMMATH +extern "C" { +#include "libtorrent/tommath.h" +} +#include "libtorrent/random.hpp" +#endif + +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + namespace + { + const unsigned char dh_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 + }; + } + + + // Set the prime P and the generator, generate local public key + dh_key_exchange::dh_key_exchange() + { +#ifdef TORRENT_USE_GCRYPT + // create local key + gcry_randomize(m_dh_local_secret, sizeof(m_dh_local_secret), GCRY_STRONG_RANDOM); + + // build gcrypt big ints from the prime and the secret + gcry_mpi_t prime = 0; + gcry_mpi_t secret = 0; + gcry_mpi_t key = 0; + gcry_error_t e; + + e = gcry_mpi_scan(&prime, GCRYMPI_FMT_USG, dh_prime, sizeof(dh_prime), 0); + if (e) goto get_out; + e = gcry_mpi_scan(&secret, GCRYMPI_FMT_USG, m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (e) goto get_out; + + key = gcry_mpi_new(8); + + // generator is 2 + gcry_mpi_set_ui(key, 2); + // key = (2 ^ secret) % prime + gcry_mpi_powm(key, key, secret, prime); + + // key is now our local key + size_t written; + gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char*)m_dh_local_key + , sizeof(m_dh_local_key), &written, key); + if (written < 96) + { + memmove(m_dh_local_key + (sizeof(m_dh_local_key) - written), m_dh_local_key, written); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - written); + } + +get_out: + if (key) gcry_mpi_release(key); + if (prime) gcry_mpi_release(prime); + if (secret) gcry_mpi_release(secret); + +#elif defined TORRENT_USE_OPENSSL + // create local key + for (int i = 0; i < sizeof(m_dh_local_secret); ++i) + m_dh_local_secret[i] = random() & 0xff; + + BIGNUM* prime = 0; + BIGNUM* secret = 0; + BIGNUM* key = 0; + BN_CTX* ctx = 0; + int size; + + prime = BN_bin2bn(dh_prime, sizeof(dh_prime), 0); + if (prime == 0) goto get_out; + secret = BN_bin2bn((unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (secret == 0) goto get_out; + + key = BN_new(); + if (key == 0) goto get_out; + // generator is 2 + BN_set_word(key, 2); + + ctx = BN_CTX_new(); + if (ctx == 0) goto get_out; + BN_mod_exp(key, key, secret, prime, ctx); + BN_CTX_free(ctx); + + // print key to m_dh_local_key + size = BN_num_bytes(key); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size); + BN_bn2bin(key, (unsigned char*)m_dh_local_key + sizeof(m_dh_local_key) - size); + +get_out: + if (key) BN_free(key); + if (secret) BN_free(secret); + if (prime) BN_free(prime); +#elif defined TORRENT_USE_TOMMATH + // create local key + for (int i = 0; i < int(sizeof(m_dh_local_secret)); ++i) + m_dh_local_secret[i] = random() & 0xff; + + mp_int prime; + mp_int secret; + mp_int key; + int e; + int size; + + mp_init(&prime); + mp_init(&secret); + mp_init(&key); + + e = mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)); + if (e) goto get_out; + e = mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)); + if (e) goto get_out; + + // generator is 2 + mp_set_int(&key, 2); + // key = (2 ^ secret) % prime + e = mp_exptmod(&key, &secret, &prime, &key); + if (e) goto get_out; + + // key is now our local key + size = mp_unsigned_bin_size(&key); + memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size); + mp_to_unsigned_bin(&key, (unsigned char*)m_dh_local_key + sizeof(m_dh_local_key) - size); + +get_out: + mp_clear(&key); + mp_clear(&prime); + mp_clear(&secret); +#else +#error you must define which bigint library to use +#endif + } + + char const* dh_key_exchange::get_local_key() const + { + return m_dh_local_key; + } + + + // compute shared secret given remote public key + int dh_key_exchange::compute_secret(char const* remote_pubkey) + { + TORRENT_ASSERT(remote_pubkey); + int ret = 0; +#ifdef TORRENT_USE_GCRYPT + + gcry_mpi_t prime = 0; + gcry_mpi_t remote_key = 0; + gcry_mpi_t secret = 0; + size_t written; + gcry_error_t e; + + e = gcry_mpi_scan(&prime, GCRYMPI_FMT_USG, dh_prime, sizeof(dh_prime), 0); + if (e != 0) { ret = 1; goto get_out; } + e = gcry_mpi_scan(&remote_key, GCRYMPI_FMT_USG, remote_pubkey, 96, 0); + if (e != 0) { ret = 1; goto get_out; } + e = gcry_mpi_scan(&secret, GCRYMPI_FMT_USG, (unsigned char const*)m_dh_local_secret + , sizeof(m_dh_local_secret), 0); + if (e != 0) { ret = 1; goto get_out; } + + gcry_mpi_powm(remote_key, remote_key, secret, prime); + + // remote_key is now the shared secret + e = gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char*)m_dh_shared_secret + , sizeof(m_dh_shared_secret), &written, remote_key); + if (e != 0) { ret = 1; goto get_out; } + + if (written < 96) + { + memmove(m_dh_shared_secret, m_dh_shared_secret + + (sizeof(m_dh_shared_secret) - written), written); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - written); + } + +get_out: + // TODO: 3 clean this up using destructors instead + if (prime) gcry_mpi_release(prime); + if (remote_key) gcry_mpi_release(remote_key); + if (secret) gcry_mpi_release(secret); + +#elif defined TORRENT_USE_OPENSSL + + BIGNUM* prime = 0; + BIGNUM* secret = 0; + BIGNUM* remote_key = 0; + BN_CTX* ctx = 0; + int size; + + prime = BN_bin2bn(dh_prime, sizeof(dh_prime), 0); + if (prime == 0) { ret = 1; goto get_out; } + secret = BN_bin2bn((unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret), 0); + if (secret == 0) { ret = 1; goto get_out; } + remote_key = BN_bin2bn((unsigned char*)remote_pubkey, 96, 0); + if (remote_key == 0) { ret = 1; goto get_out; } + + ctx = BN_CTX_new(); + if (ctx == 0) { ret = 1; goto get_out; } + BN_mod_exp(remote_key, remote_key, secret, prime, ctx); + BN_CTX_free(ctx); + + // remote_key is now the shared secret + size = BN_num_bytes(remote_key); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size); + BN_bn2bin(remote_key, (unsigned char*)m_dh_shared_secret + sizeof(m_dh_shared_secret) - size); + +get_out: + BN_free(remote_key); + BN_free(secret); + BN_free(prime); +#elif defined TORRENT_USE_TOMMATH + mp_int prime; + mp_int secret; + mp_int remote_key; + int size; + int e; + + mp_init(&prime); + mp_init(&secret); + mp_init(&remote_key); + + e = mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)); + if (e) { ret = 1; goto get_out; } + e = mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)); + if (e) { ret = 1; goto get_out; } + e = mp_read_unsigned_bin(&remote_key, (unsigned char*)remote_pubkey, 96); + if (e) { ret = 1; goto get_out; } + + e = mp_exptmod(&remote_key, &secret, &prime, &remote_key); + if (e) goto get_out; + + // remote_key is now the shared secret + size = mp_unsigned_bin_size(&remote_key); + memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size); + mp_to_unsigned_bin(&remote_key, (unsigned char*)m_dh_shared_secret + sizeof(m_dh_shared_secret) - size); + +get_out: + mp_clear(&remote_key); + mp_clear(&secret); + mp_clear(&prime); +#else +#error you must define which bigint library to use +#endif + + // calculate the xor mask for the obfuscated hash + hasher h; + h.update("req3", 4); + h.update(m_dh_shared_secret, sizeof(m_dh_shared_secret)); + m_xor_mask = h.final(); + return ret; + } + +} // namespace libtorrent + +#if !defined TORRENT_USE_OPENSSL && !defined TORRENT_USE_GCRYPT + +// All this code is based on libTomCrypt (http://www.libtomcrypt.com/) +// this library is public domain and has been specially +// tailored for libtorrent by Arvid Norberg + +void rc4_init(const unsigned char* in, unsigned long len, rc4 *state) +{ + unsigned char key[256], tmp, *s; + int keylen, x, y, j; + + TORRENT_ASSERT(state != 0); + TORRENT_ASSERT(len <= 256); + + state->x = 0; + while (len--) { + state->buf[state->x++] = *in++; + } + + /* extract the key */ + s = state->buf; + memcpy(key, s, 256); + keylen = state->x; + + /* make RC4 perm and shuffle */ + for (x = 0; x < 256; x++) { + s[x] = x; + } + + for (j = x = y = 0; x < 256; x++) { + y = (y + state->buf[x] + key[j++]) & 255; + if (j == keylen) { + j = 0; + } + tmp = s[x]; s[x] = s[y]; s[y] = tmp; + } + state->x = 0; + state->y = 0; +} + +unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state) +{ + unsigned char x, y, *s, tmp; + unsigned long n; + + TORRENT_ASSERT(out != 0); + TORRENT_ASSERT(state != 0); + + n = outlen; + x = state->x; + y = state->y; + s = state->buf; + while (outlen--) { + x = (x + 1) & 255; + y = (y + s[x]) & 255; + tmp = s[x]; s[x] = s[y]; s[y] = tmp; + tmp = (s[x] + s[y]) & 255; + *out++ ^= s[tmp]; + } + state->x = x; + state->y = y; + return n; +} + +#endif + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + diff --git a/apps/Launcher/ext/libtorrent/src/peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/peer_connection.cpp new file mode 100644 index 0000000000..67781ef797 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/peer_connection.cpp @@ -0,0 +1,6108 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING +#include // for va_start, va_end +#include // for vsnprintf +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/kademlia/node_id.hpp" + +#ifdef TORRENT_DEBUG +#include +#endif + +#ifdef TORRENT_USE_OPENSSL +#include +#endif + +//#define TORRENT_CORRUPT_DATA + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + int round_up8(int v) + { + return ((v & 7) == 0) ? v : v + (8 - (v & 7)); + } + +#if defined TORRENT_REQUEST_LOGGING + void write_request_log(FILE* f, sha1_hash const& ih + , peer_connection* p, peer_request const& r) + { + // the event format in the log is: + // uint64_t timestamp (microseconds) + // uint64_t info-hash prefix + // uint32_t peer identifier + // uint32_t piece + // uint32_t start offset + // uint32_t length + char event[32]; + char* ptr = event; + detail::write_uint64(total_microseconds((time_now_hires() - min_time())), ptr); + memcpy(ptr, &ih[0], 8); + ptr += 8; + detail::write_uint32(uintptr_t(p) & 0xffffffff, ptr); + detail::write_uint32(r.piece, ptr); + detail::write_uint32(r.start, ptr); + detail::write_uint32(r.length, ptr); + + int ret = fwrite(event, 1, sizeof(event), f); + if (ret != sizeof(event)) + { + fprintf(stderr, "ERROR writing to request log: (%d) %s\n" + , errno, strerror(errno)); + } + } +#endif + + // outbound connection + peer_connection::peer_connection( + session_impl& ses + , boost::weak_ptr tor + , shared_ptr s + , tcp::endpoint const& endp + , policy::peer* peerinfo + , bool outgoing) + : m_received_listen_port(false) + , m_have_all(false) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_disconnecting(false) + , m_bitfield_received(false) + , m_endgame_mode(false) + , m_sent_suggests(false) + , m_holepunch_mode(false) + , m_ignore_stats(false) + , m_corked(false) + , m_has_metadata(true) + , m_exceeded_limit(false) + , m_ses(ses) + , m_work(ses.m_io_service) + , m_last_piece(time_now()) + , m_last_request(time_now()) + , m_last_incoming_request(min_time()) + , m_last_unchoke(time_now()) + , m_last_unchoked(time_now()) + , m_last_choke(min_time()) + , m_last_receive(time_now()) + , m_last_sent(time_now()) + , m_requested(min_time()) + , m_remote_dl_update(time_now()) + , m_connect(time_now()) + , m_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) + , m_downloaded_at_last_round(0) + , m_uploaded_at_last_round(0) + , m_uploaded_at_last_unchoke(0) + , m_disk_recv_buffer(ses, 0) + , m_socket(s) + , m_torrent(tor) + , m_peer_info(peerinfo) + , m_last_seen_complete(0) + , m_receiving_block(piece_block::invalid) + , m_remote(endp) + , m_timeout_extend(0) + , m_outstanding_bytes(0) + , m_extension_outstanding_bytes(0) + , m_queued_time_critical(0) + , m_num_pieces(0) + , m_timeout(m_ses.settings().peer_timeout) + , m_packet_size(0) + , m_soft_packet_size(0) + , m_recv_pos(0) + , m_disk_recv_buffer_size(0) + , m_reading_bytes(0) + , m_num_invalid_requests(0) + , m_priority(1) + , m_upload_limit(0) + , m_download_limit(0) + , m_speed(slow) + , m_connection_ticket(-1) + , m_remote_pieces_dled(0) + , m_remote_dl_rate(0) + , m_outstanding_writing_bytes(0) + , m_download_rate_peak(0) + , m_upload_rate_peak(0) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_rtt(0) + , m_desired_queue_size(2) + , m_prefer_whole_pieces(0) + , m_fast_reconnect(false) + , m_outgoing(outgoing) + , m_ignore_bandwidth_limits(false) + , m_ignore_unchoke_slots(false) + , m_connecting(outgoing) + , m_queued(outgoing) + , m_request_large_blocks(false) + , m_share_mode(false) + , m_upload_only(false) + , m_snubbed(false) + , m_no_download(false) +#if TORRENT_USE_ASSERTS + , m_in_constructor(true) + , m_disconnect_started(false) + , m_initialized(false) + , m_in_use(1337) + , m_received_in_piece(0) +#endif + { + m_superseed_piece[0] = -1; + m_superseed_piece[1] = -1; + boost::shared_ptr t = m_torrent.lock(); + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) t->inc_num_connecting(); + m_est_reciprocation_rate = m_ses.m_settings.default_est_reciprocation_rate; + +#if TORRENT_USE_I2P + if (peerinfo && peerinfo->is_i2p_addr) + { + // quadruple the timeout for i2p peers + m_timeout *= 4; + } +#endif + + m_channel_state[upload_channel] = peer_info::bw_idle; + m_channel_state[download_channel] = peer_info::bw_idle; + + m_quota[0] = 0; + m_quota[1] = 0; + + TORRENT_ASSERT(peerinfo == 0 || peerinfo->banned == false); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + std::fill(m_country, m_country + 2, 0); +#ifndef TORRENT_DISABLE_GEO_IP + if (m_ses.has_country_db()) + { + char const *country = m_ses.country_for_ip(m_remote.address()); + if (country != 0) + { + m_country[0] = country[0]; + m_country[1] = country[1]; + } + } +#endif +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + error_code ec; + TORRENT_ASSERT(m_socket->remote_endpoint(ec) == m_remote || ec); + tcp::endpoint local_ep = m_socket->local_endpoint(ec); + std::string log_name = "[" + local_ep.address().to_string(ec) + "#" + + to_string(local_ep.port()).elems + "]-" + "[" + m_remote.address().to_string(ec) + "#" + + to_string(m_remote.port()).elems + "]"; + + if (t) log_name = combine_path(to_hex(t->info_hash().to_string()) + , log_name); + + m_logger = m_ses.create_log(log_name, m_ses.listen_port()); + peer_log("%s [ ep: %s type: %s seed: %d p: %p local: %s]" + , m_outgoing ? ">>> OUTGOING_CONNECTION" : "<<< INCOMING CONNECTION" + , print_endpoint(m_remote).c_str() + , m_socket->type_name() + , m_peer_info ? m_peer_info->seed : 0, m_peer_info + , print_endpoint(local_ep).c_str()); +#endif +#ifndef TORRENT_DISABLE_GEO_IP + m_inet_as_name = m_ses.as_name_for_ip(m_remote.address()); +#endif +#ifdef TORRENT_DEBUG + piece_failed = false; +#endif + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + } + +#ifdef TORRENT_DISK_STATS + void peer_connection::log_buffer_usage(char* buffer, int size, char const* label) + { + if (m_ses.m_disk_thread.is_disk_buffer(buffer)) + m_ses.m_disk_thread.rename_buffer(buffer, label); + + m_ses.m_buffer_usage_logger << log_time() << " append_send_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); + } +#endif + + void peer_connection::increase_est_reciprocation_rate() + { + m_est_reciprocation_rate += m_est_reciprocation_rate + * m_ses.m_settings.increase_est_reciprocation_rate / 100; + } + + void peer_connection::decrease_est_reciprocation_rate() + { + m_est_reciprocation_rate -= m_est_reciprocation_rate + * m_ses.m_settings.decrease_est_reciprocation_rate / 100; + } + + bool peer_connection::bittyrant_unchoke_compare( + boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(p); + peer_connection const& rhs = *p; + + size_type d1, d2, u1, u2; + + // first compare how many bytes they've sent us + d1 = downloaded_in_last_round(); + d2 = rhs.downloaded_in_last_round(); + // divided by the number of bytes we've sent them + u1 = uploaded_in_last_round(); + u2 = rhs.uploaded_in_last_round(); + + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = rhs.associated_torrent().lock(); + TORRENT_ASSERT(t2); + + // take torrent priority into account + d1 *= 1 + t1->priority(); + d2 *= 1 + t2->priority(); + + d1 = d1 * 1000 / (std::max)(size_type(1), u1); + d2 = d2 * 1000 / (std::max)(size_type(1), u2); + if (d1 > d2) return true; + if (d1 < d2) return false; + + // if both peers are still in their send quota or not in their send quota + // prioritize the one that has waited the longest to be unchoked + return m_last_unchoke < rhs.m_last_unchoke; + } + + // return true if 'this' peer should be preferred to be unchoke over p + bool peer_connection::unchoke_compare(boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(p); + peer_connection const& rhs = *p; + + // if one peer belongs to a higher priority torrent than the other one + // that one should be unchoked. + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = rhs.associated_torrent().lock(); + TORRENT_ASSERT(t2); + + if (t1->priority() != t2->priority()) + return t1->priority() > t2->priority(); + + // compare how many bytes they've sent us + size_type c1; + size_type c2; + c1 = downloaded_in_last_round(); + c2 = rhs.downloaded_in_last_round(); + + if (c1 != c2) return c1 > c2; + + if (m_ses.settings().seed_choking_algorithm == session_settings::round_robin) + { + // the amount uploaded since unchoked (not just in the last round) + c1 = uploaded_since_unchoked(); + c2 = rhs.uploaded_since_unchoked(); + + // the way the round-robin unchoker works is that it, + // by default, prioritizes any peer that is already unchoked. + // this maintain the status quo across unchoke rounds. However, + // peers that are unchoked, but have sent more than one quota + // since they were unchoked, they get de-prioritized. + + int pieces = m_ses.settings().seeding_piece_quota; + // if a peer is already unchoked, and the number of bytes sent since it was unchoked + // is greater than the send quanta, then it's done with it' upload slot, and we + // can de-prioritize it + bool c1_quota_complete = !is_choked() && c1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024); + bool c2_quota_complete = !rhs.is_choked() && c2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024); + + // if c2 has completed a quanta, it shuold be de-prioritized + // and vice versa + if (c1_quota_complete < c2_quota_complete) return true; + if (c1_quota_complete > c2_quota_complete) return false; + + // if both peers have either completed a quanta, or not. + // keep unchoked peers prioritized over choked ones, to let + // peers keep working on uploading a full quanta + if (is_choked() < rhs.is_choked()) return true; + if (is_choked() > rhs.is_choked()) return false; + + // if the peers are still identical (say, they're both waiting to be unchoked) + // fall through and rely on the logic to prioritize peers who have waited + // the longest to be unchoked + } + else if (m_ses.settings().seed_choking_algorithm == session_settings::fastest_upload) + { + c1 = uploaded_in_last_round(); + c2 = rhs.uploaded_in_last_round(); + + // take torrent priority into account + c1 *= 1 + t1->priority(); + c2 *= 1 + t2->priority(); + + if (c1 > c2) return true; + if (c2 > c1) return false; + } + else if (m_ses.settings().seed_choking_algorithm == session_settings::anti_leech) + { + // the anti-leech seeding algorithm is based on the paper "Improving + // BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based + // on how many pieces they have, prefering to unchoke peers that just + // started and peers that are close to completing. Like this: + // ^ + // | \ / | + // | \ / | + // | \ / | + // s | \ / | + // c | \ / | + // o | \ / | + // r | \ / | + // e | \ / | + // | \ / | + // | \ / | + // | \ / | + // | \ / | + // | V | + // +---------------------------+ + // 0% num have pieces 100% + int t1_total = t1->torrent_file().num_pieces(); + int t2_total = t2->torrent_file().num_pieces(); + int score1 = (num_have_pieces() < t1_total / 2 + ? t1_total - num_have_pieces() : num_have_pieces()) * 1000 / t1_total; + int score2 = (rhs.num_have_pieces() < t2_total / 2 + ? t2_total - rhs.num_have_pieces() : rhs.num_have_pieces()) * 1000 / t2_total; + if (score1 > score2) return true; + if (score2 > score1) return false; + } + + // prioritize the one that has waited the longest to be unchoked + // the round-robin unchoker relies on this logic. Don't change it + // without moving this into that unchoker logic + return m_last_unchoke < rhs.m_last_unchoke; + } + + bool peer_connection::upload_rate_compare(peer_connection const* p) const + { + size_type c1; + size_type c2; + + boost::shared_ptr t1 = m_torrent.lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = p->associated_torrent().lock(); + TORRENT_ASSERT(t2); + + c1 = uploaded_in_last_round(); + c2 = p->uploaded_in_last_round(); + + // take torrent priority into account + c1 *= 1 + t1->priority(); + c2 *= 1 + t2->priority(); + + return c1 > c2; + } + + void peer_connection::reset_choke_counters() + { + m_downloaded_at_last_round= m_statistics.total_payload_download(); + m_uploaded_at_last_round = m_statistics.total_payload_upload(); + } + + void peer_connection::start() + { + TORRENT_ASSERT(m_peer_info == 0 || m_peer_info->connection == this); + boost::shared_ptr t = m_torrent.lock(); + + if (!m_outgoing) + { + tcp::socket::non_blocking_io ioc(true); + error_code ec; + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec); + return; + } + m_remote = m_socket->remote_endpoint(ec); + if (ec) + { + disconnect(ec); + return; + } + if (m_remote.address().is_v4() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> SET_TOS[ tos: %d e: %s ]", m_ses.settings().peer_tos, ec.message().c_str()); +#endif + } +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + else if (m_remote.address().is_v6() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(traffic_class(m_ses.settings().peer_tos), ec); + } +#endif + } + + if (t && t->ready_for_connections()) + { + init(); + } + } + + void peer_connection::update_interest() + { + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + + // if m_have_piece is 0, it means the connections + // have not been initialized yet. The interested + // flag will be updated once they are. + if (m_have_piece.size() == 0) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ connections not initialized ]"); +#endif + return; + } + if (!t->ready_for_connections()) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ not ready for connections ]"); +#endif + return; + } + + bool interested = false; + if (!t->is_upload_only()) + { + piece_picker const& p = t->picker(); + int num_pieces = p.num_pieces(); + for (int j = 0; j != num_pieces; ++j) + { + if (!p.have_piece(j) + && t->piece_priority(j) > 0 + && m_have_piece[j]) + { + interested = true; +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_INTEREST [ interesting, piece: %d ]", j); +#endif + break; + } + } + } + +#if defined TORRENT_VERBOSE_LOGGING + if (!interested) + { + peer_log("*** UPDATE_INTEREST [ not interesting ]"); + } +#endif + + if (!interested) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + + TORRENT_ASSERT(in_handshake() || is_interesting() == interested); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + void peer_connection::peer_log(char const* fmt, ...) const + { + if (!m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[400]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[450]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_logger) << buf; + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + void peer_connection::add_extension(boost::shared_ptr ext) + { + m_extensions.push_back(ext); + } + + peer_plugin const* peer_connection::find_plugin(char const* type) + { + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if (strcmp((*i)->type(), type) == 0) return (*i).get(); + } + return 0; + } +#endif + + void peer_connection::send_allowed_set() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (t->super_seeding()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SKIPPING ALLOWED SET BECAUSE OF SUPER SEEDING"); +#endif + return; + } + + if (upload_only()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** SKIPPING ALLOWED SET BECAUSE PEER IS UPLOAD ONLY"); +#endif + return; + } + + int num_allowed_pieces = m_ses.settings().allowed_fast_set_size; + if (num_allowed_pieces == 0) return; + + int num_pieces = t->torrent_file().num_pieces(); + + if (num_allowed_pieces >= num_pieces) + { + // this is a special case where we have more allowed + // fast pieces than pieces in the torrent. Just send + // an allowed fast message for every single piece + for (int i = 0; i < num_pieces; ++i) + { + // there's no point in offering fast pieces + // that the peer already has + if (has_piece(i)) continue; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> ALLOWED_FAST [ %d ]", i); +#endif + write_allow_fast(i); + TORRENT_ASSERT(std::find(m_accept_fast.begin() + , m_accept_fast.end(), i) + == m_accept_fast.end()); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } + m_accept_fast.push_back(i); + m_accept_fast_piece_cnt.push_back(0); + } + return; + } + + std::string x; + address const& addr = m_remote.address(); + if (addr.is_v4()) + { + address_v4::bytes_type bytes = addr.to_v4().to_bytes(); + x.assign((char*)&bytes[0], bytes.size()); + } +#if TORRENT_USE_IPV6 + else + { + address_v6::bytes_type bytes = addr.to_v6().to_bytes(); + x.assign((char*)&bytes[0], bytes.size()); + } +#endif + x.append((char*)&t->torrent_file().info_hash()[0], 20); + + sha1_hash hash = hasher(x.c_str(), x.size()).final(); + for (;;) + { + char* p = (char*)&hash[0]; + for (int i = 0; i < 5; ++i) + { + int piece = detail::read_uint32(p) % num_pieces; + if (std::find(m_accept_fast.begin(), m_accept_fast.end(), piece) + == m_accept_fast.end()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> ALLOWED_FAST [ %d ]", piece); +#endif + write_allow_fast(piece); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } + m_accept_fast.push_back(piece); + m_accept_fast_piece_cnt.push_back(0); + if (int(m_accept_fast.size()) >= num_allowed_pieces + || int(m_accept_fast.size()) == num_pieces) return; + } + } + hash = hasher((char*)&hash[0], 20).final(); + } + } + + void peer_connection::on_metadata_impl() + { + boost::shared_ptr t = associated_torrent().lock(); + m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); + m_num_pieces = m_have_piece.count(); + + // now that we know how many pieces there are + // remove any invalid allowed_fast and suggest pieces + // now that we know what the number of pieces are + for (std::vector::iterator i = m_allowed_fast.begin(); + i != m_allowed_fast.end();) + { + if (*i < m_num_pieces) + { + ++i; + continue; + } + i = m_allowed_fast.erase(i); + } + + for (std::vector::iterator i = m_suggested_pieces.begin(); + i != m_suggested_pieces.end();) + { + if (*i < m_num_pieces) + { + ++i; + continue; + } + i = m_suggested_pieces.erase(i); + } + on_metadata(); + if (m_disconnecting) return; + } + + void peer_connection::init() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->valid_metadata()); + TORRENT_ASSERT(t->ready_for_connections()); + + m_have_piece.resize(t->torrent_file().num_pieces(), m_have_all); + + if (m_have_all) m_num_pieces = t->torrent_file().num_pieces(); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(!m_initialized); + m_initialized = true; +#endif + // now that we have a piece_picker, + // update it with this peer's pieces + + TORRENT_ASSERT(m_num_pieces == m_have_piece.count()); + + if (m_num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + // if this is a web seed. we don't have a peer_info struct + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + + t->peer_has_all(this); + if (t->is_upload_only()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + return; + } + + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + t->peer_has(m_have_piece, this); + bool interesting = false; + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i]) + { + // if the peer has a piece and we don't, the peer is interesting + if (!t->have_piece(i) + && t->picker().piece_priority(i) != 0) + interesting = true; + } + } + if (interesting) t->get_policy().peer_is_interesting(*this); + else send_not_interested(); + } + else + { + update_interest(); + } + } + + peer_connection::~peer_connection() + { +// INVARIANT_CHECK; + TORRENT_ASSERT(!m_in_constructor); + TORRENT_ASSERT(m_disconnecting); + TORRENT_ASSERT(m_disconnect_started); + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if TORRENT_USE_ASSERTS + m_in_use = 0; +#endif + + // defensive + + boost::shared_ptr t = m_torrent.lock(); + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + TORRENT_ASSERT(t || !m_connecting); + + // we should really have dealt with this already + TORRENT_ASSERT(!m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + + m_disk_recv_buffer_size = 0; + +#ifndef TORRENT_DISABLE_EXTENSIONS + m_extensions.clear(); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** CONNECTION CLOSED"); +#endif +// TORRENT_ASSERT(!m_ses.has_peer(this)); + TORRENT_ASSERT(m_request_queue.empty()); + TORRENT_ASSERT(m_download_queue.empty()); +#if TORRENT_USE_ASSERTS + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer(this)); + if (m_peer_info) + TORRENT_ASSERT(m_peer_info->connection == 0); +#endif + } + + int peer_connection::picker_options() const + { + int ret = 0; + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (!t) return 0; + + if (t->num_time_critical_pieces() > 0) + { + ret |= piece_picker::time_critical_mode; + } + + if (t->is_sequential_download()) + { + ret |= piece_picker::sequential; + } + else if (t->num_have() < t->settings().initial_picker_threshold) + { + // if we have fewer pieces than a certain threshols + // don't pick rare pieces, just pick random ones, + // and prioritize finishing them + ret |= piece_picker::prioritize_partials; + } + else + { + ret |= piece_picker::rarest_first | piece_picker::speed_affinity; + } + + if (m_snubbed) + { + // snubbed peers should request + // the common pieces first, just to make + // it more likely for all snubbed peers to + // request blocks from the same piece + ret |= piece_picker::reverse; + } + + if (t->settings().prioritize_partial_pieces) + ret |= piece_picker::prioritize_partials; + + if (on_parole()) ret |= piece_picker::on_parole + | piece_picker::prioritize_partials; + + // only one of rarest_first, common_first and sequential can be set. + TORRENT_ASSERT((ret & piece_picker::rarest_first) ? 1 : 0 + + (ret & piece_picker::sequential) ? 1 : 0 <= 1); + return ret; + } + + void peer_connection::fast_reconnect(bool r) + { + if (!peer_info_struct() || peer_info_struct()->fast_reconnects > 1) + return; + m_fast_reconnect = r; + peer_info_struct()->last_connected = (boost::uint16_t)m_ses.session_time(); + int rewind = m_ses.settings().min_reconnect_time * m_ses.settings().max_failcount; + if (peer_info_struct()->last_connected < rewind) peer_info_struct()->last_connected = 0; + else peer_info_struct()->last_connected -= rewind; + + if (peer_info_struct()->fast_reconnects < 15) + ++peer_info_struct()->fast_reconnects; + } + + void peer_connection::announce_piece(int index) + { + // dont announce during handshake + if (in_handshake()) return; + + // remove suggested pieces once we have them + std::vector::iterator i = std::find( + m_suggested_pieces.begin(), m_suggested_pieces.end(), index); + if (i != m_suggested_pieces.end()) m_suggested_pieces.erase(i); + + // remove allowed fast pieces + i = std::find(m_allowed_fast.begin(), m_allowed_fast.end(), index); + if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); + + if (has_piece(index)) + { + // if we got a piece that this peer has + // it might have been the last interesting + // piece this peer had. We might not be + // interested anymore + update_interest(); + if (is_disconnecting()) return; + + // optimization, don't send have messages + // to peers that already have the piece + if (!m_ses.settings().send_redundant_have) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ] SUPRESSED", index); +#endif + return; + } + } + + disconnect_if_redundant(); + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ]", index); +#endif + write_have(index); +#if TORRENT_USE_ASSERTS + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); +#endif + } + + bool peer_connection::has_piece(int i) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->valid_metadata()); + TORRENT_ASSERT(i >= 0); + TORRENT_ASSERT(i < t->torrent_file().num_pieces()); + return m_have_piece[i]; + } + + std::vector const& peer_connection::request_queue() const + { + return m_request_queue; + } + + std::vector const& peer_connection::download_queue() const + { + return m_download_queue; + } + + std::vector const& peer_connection::upload_queue() const + { + return m_requests; + } + + time_duration peer_connection::download_queue_time(int extra_bytes) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + int rate = 0; + + // if we haven't received any data recently, the current download rate + // is not representative + if (time_now() - m_last_piece > seconds(30) && m_download_rate_peak > 0) + { + rate = m_download_rate_peak; + } + else if (time_now() - m_last_unchoked < seconds(5) + && m_statistics.total_payload_upload() < 2 * 0x4000) + { + // if we're have only been unchoked for a short period of time, + // we don't know what rate we can get from this peer. Instead of assuming + // the lowest possible rate, assume the average. + + // TODO: this should only be peers we're trying to download from + int peers_with_requests = m_ses.num_connections(); + // avoid division by 0 + if (peers_with_requests == 0) peers_with_requests = 1; + + rate = m_ses.m_stat.transfer_rate(stat::download_payload) / peers_with_requests; + } + else + { + // current download rate in bytes per seconds + rate = m_statistics.transfer_rate(stat::download_payload); + } + + // avoid division by zero + if (rate < 50) rate = 50; + + // average of current rate and peak +// rate = (rate + m_download_rate_peak) / 2; + + return milliseconds((m_outstanding_bytes + + m_queued_time_critical * t->block_size() * 1000) / rate); + } + + void peer_connection::add_stat(size_type downloaded, size_type uploaded) + { + m_statistics.add_stat(downloaded, uploaded); + } + + bitfield const& peer_connection::get_bitfield() const + { + return m_have_piece; + } + + void peer_connection::received_valid_data(int index) + { + // this fails because we haven't had time to disconnect + // seeds yet, and we might have just become one +// INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_pass(index); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + + bool peer_connection::received_invalid_data(int index, bool single_peer) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_failed(index); + } TORRENT_CATCH(std::exception&) {} + } +#endif + return true; + } + + // verifies a piece to see if it is valid (is within a valid range) + // and if it can correspond to a request generated by libtorrent. + bool peer_connection::verify_piece(const peer_request& p) const + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + torrent_info const& ti = t->torrent_file(); + + return p.piece >= 0 + && p.piece < ti.num_pieces() + && p.start >= 0 + && p.start < ti.piece_length() + && t->to_req(piece_block(p.piece, p.start / t->block_size())) == p; + } + + void peer_connection::attach_to_torrent(sha1_hash const& ih, bool allow_encrypted) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_disconnecting); + TORRENT_ASSERT(m_torrent.expired()); + boost::weak_ptr wpt = m_ses.find_torrent(ih); + boost::shared_ptr t = wpt.lock(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + // now that we know which torrent this peer belongs + // to. Move the log file into its directory + + error_code ec; + std::string log_name = combine_path(to_hex(ih.to_string()) + , m_remote.address().to_string(ec) + "_" + + to_string(m_remote.port()).elems); + m_logger->move_log_file(m_ses.get_log_path(), log_name, m_ses.listen_port()); +#endif + + if (t && t->is_aborted()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** the torrent has been aborted"); +#endif + t.reset(); + } + + if (!t) + { + // we couldn't find the torrent! +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** couldn't find a torrent with the given info_hash: %s torrents:", to_hex(ih.to_string()).c_str()); + session_impl::torrent_map const& torrents = m_ses.m_torrents; + for (session_impl::torrent_map::const_iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + peer_log(" %s", to_hex(i->second->torrent_file().info_hash().to_string()).c_str()); + } +#endif + +#ifndef TORRENT_DISABLE_DHT + if (dht::verify_secret_id(ih)) + { + // this means the hash was generated from our generate_secret_id() + // as part of DHT traffic. The fact that we got an incoming + // connection on this info-hash, means the other end, making this + // connection fished it out of the DHT chatter. That's suspicious. + m_ses.m_ip_filter.add_rule(m_remote.address(), m_remote.address(), 0); + } +#endif + disconnect(errors::invalid_info_hash, 1); + return; + } + + if (t->is_paused() && (!t->is_auto_managed() + || !m_ses.m_settings.incoming_starts_queued_torrents)) + { + // paused torrents will not accept + // incoming connections unless they are auto managed + // and inconing_starts_queued_torrents is true + // torrents that have errors should always reject + // incoming peers +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("rejected connection to paused torrent"); +#endif + disconnect(errors::torrent_paused, 2); + return; + } + +#if TORRENT_USE_I2P + i2p_stream* i2ps = m_socket->get(); + if (!i2ps && t->torrent_file().is_i2p() && !m_ses.m_settings.allow_i2p_mixed) + { + // the torrent is an i2p torrent, the peer is a regular peer + // and we don't allow mixed mode. Disconnect the peer. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("rejected regular connection to i2p torrent"); +#endif + disconnect(errors::peer_banned, 2); + return; + } +#endif // TORRENT_USE_I2P + + TORRENT_ASSERT(m_torrent.expired()); + + if (t->is_paused() + && m_ses.m_settings.incoming_starts_queued_torrents + && !m_ses.is_paused() + && !t->is_aborted() + && !m_ses.is_aborted()) + { + t->resume(); + } + + // check to make sure we don't have another connection with the same + // info_hash and peer_id. If we do. close this connection. + t->attach_peer(this); + if (m_disconnecting) return; + m_torrent = wpt; + + if (m_exceeded_limit) + { + // find a peer in some torrent (presumably the one with most peers) + // and disconnect the lowest ranking peer + boost::weak_ptr torr = m_ses.find_disconnect_candidate_torrent(); + boost::shared_ptr other_t = torr.lock(); + + if (other_t) + { + if (other_t->num_peers() <= t->num_peers()) + { + disconnect(errors::too_many_connections); + return; + } + // find the lowest ranking peer and disconnect that + peer_connection* p = other_t->find_lowest_ranking_peer(); + p->disconnect(errors::too_many_connections); + peer_disconnected_other(); + } + else + { + disconnect(errors::too_many_connections); + return; + } + } + + TORRENT_ASSERT(!m_torrent.expired()); + + // if the torrent isn't ready to accept + // connections yet, we'll have to wait with + // our initialization + if (t->ready_for_connections()) init(); + + TORRENT_ASSERT(!m_torrent.expired()); + + // assume the other end has no pieces + // if we don't have valid metadata yet, + // leave the vector unallocated + TORRENT_ASSERT(m_num_pieces == 0); + m_have_piece.clear_all(); + TORRENT_ASSERT(!m_torrent.expired()); + } + + boost::uint32_t peer_connection::peer_rank() const + { + return m_peer_info == NULL ? 0 + : m_peer_info->rank(m_ses.external_address(), m_ses.listen_port()); + } + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void peer_connection::incoming_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== KEEPALIVE"); +#endif + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void peer_connection::incoming_choke() + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_choke()) return; + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== CHOKE"); +#endif + m_peer_choked = true; + set_endgame(false); + + clear_request_queue(); + } + + void peer_connection::clear_request_queue() + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // clear the requests that haven't been sent yet + if (peer_info_struct() == 0 || !peer_info_struct()->on_parole) + { + // if the peer is not in parole mode, clear the queued + // up block requests + if (t->has_picker()) + { + piece_picker& p = t->picker(); + for (std::vector::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + p.abort_download(i->block, peer_info_struct()); + } + } + m_request_queue.clear(); + m_queued_time_critical = 0; + } + } + + bool match_request(peer_request const& r, piece_block const& b, int block_size) + { + if (int(b.piece_index) != r.piece) return false; + if (int(b.block_index) != r.start / block_size) return false; + if (r.start % block_size != 0) return false; + return true; + } + + // ----------------------------- + // -------- REJECT PIECE ------- + // ----------------------------- + + void peer_connection::incoming_reject_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_reject(r)) return; + } +#endif + + if (is_disconnecting()) return; + + std::vector::iterator i = std::find_if( + m_download_queue.begin(), m_download_queue.end() + , boost::bind(match_request, boost::cref(r), boost::bind(&pending_block::block, _1) + , t->block_size())); + + if (i != m_download_queue.end()) + { + pending_block b = *i; + bool remove_from_picker = !i->timed_out && !i->not_wanted; + m_download_queue.erase(i); + TORRENT_ASSERT(m_outstanding_bytes >= r.length); + m_outstanding_bytes -= r.length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + + // if the peer is in parole mode, keep the request + if (peer_info_struct() && peer_info_struct()->on_parole) + { + // we should only add it if the block is marked as + // busy in the piece-picker + if (remove_from_picker) + m_request_queue.insert(m_request_queue.begin(), b); + } + else if (!t->is_seed() && remove_from_picker) + { + piece_picker& p = t->picker(); + p.abort_download(b.block, peer_info_struct()); + } +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + peer_log("*** PIECE NOT IN REQUEST QUEUE"); + } +#endif + if (has_peer_choked()) + { + // if we're choked and we got a rejection of + // a piece in the allowed fast set, remove it + // from the allow fast set. + std::vector::iterator i = std::find( + m_allowed_fast.begin(), m_allowed_fast.end(), r.piece); + if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); + } + else + { + std::vector::iterator i = std::find(m_suggested_pieces.begin() + , m_suggested_pieces.end(), r.piece); + if (i != m_suggested_pieces.end()) + m_suggested_pieces.erase(i); + } + + if (m_request_queue.empty() && m_download_queue.size() < 2) + { +#ifdef TORRENT_STATS + ++m_ses.m_reject_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + } + + // ----------------------------- + // ------- SUGGEST PIECE ------- + // ----------------------------- + + void peer_connection::incoming_suggest(int index) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== SUGGEST_PIECE [ piece: %d ]", index); +#endif + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_suggest(index)) return; + } +#endif + + if (is_disconnecting()) return; + if (index < 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_SUGGEST_PIECE [ %d ]", index); +#endif + return; + } + + if (t->valid_metadata()) + { + if (index >= int(m_have_piece.size())) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_SUGGEST [ %d | s: %d ]" + , index, int(m_have_piece.size())); +#endif + return; + } + + // if we already have the piece, we can + // ignore this message + if (t->have_piece(index)) + return; + } + + if (int(m_suggested_pieces.size()) > m_ses.m_settings.max_suggest_pieces) + m_suggested_pieces.erase(m_suggested_pieces.begin()); + + m_suggested_pieces.push_back(index); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("** SUGGEST_PIECE [ piece: %d added to set: %d ]", index, int(m_suggested_pieces.size())); +#endif + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void peer_connection::incoming_unchoke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_unchoke()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== UNCHOKE"); +#endif + m_peer_choked = false; + m_last_unchoked = time_now(); + if (is_disconnecting()) return; + + if (is_interesting()) + { +#ifdef TORRENT_STATS + ++m_ses.m_unchoke_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void peer_connection::incoming_interested() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_interested()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== INTERESTED"); +#endif + m_peer_interested = true; + if (is_disconnecting()) return; + + // if the peer is ready to download stuff, it must have metadata + m_has_metadata = true; + + disconnect_if_redundant(); + if (is_disconnecting()) return; + + if (is_choked() && !t->graceful_pause()) + { + if (ignore_unchoke_slots()) + { + // if this peer is expempted from the choker + // just unchoke it immediately + send_unchoke(); + } + else if ((m_ses.num_uploads() < m_ses.settings().unchoke_slots_limit + || m_ses.settings().unchoke_slots_limit < 0)) + { + // if the peer is choked and we have upload slots left, + // then unchoke it. Another condition that has to be met + // is that the torrent doesn't keep track of the individual + // up/down ratio for each peer (ratio == 0) or (if it does + // keep track) this particular connection isn't a leecher. + // If the peer was choked because it was leeching, don't + // unchoke it again. + // The exception to this last condition is if we're a seed. + // In that case we don't care if people are leeching, they + // can't pay for their downloads anyway. + m_ses.unchoke_peer(*this); + } +#if defined TORRENT_VERBOSE_LOGGING + else + { + peer_log("DID NOT UNCHOKE [ the number of uploads (%d)" + "is more than or equal to the limit (%d) ]" + , m_ses.num_uploads(), m_ses.settings().unchoke_slots_limit); + } +#endif + } +#if defined TORRENT_VERBOSE_LOGGING + else if (t->graceful_pause()) + { + peer_log("DID NOT UNCHOKE [ graceful pause mode ]"); + } +#endif + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void peer_connection::incoming_not_interested() + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_not_interested()) return; + } +#endif + + m_became_uninterested = time_now(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== NOT_INTERESTED"); +#endif + m_peer_interested = false; + if (is_disconnecting()) return; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (!is_choked()) + { + if (ignore_unchoke_slots()) + { + send_choke(); + } + else + { + if (m_peer_info && m_peer_info->optimistically_unchoked) + { + m_peer_info->optimistically_unchoked = false; + m_ses.m_optimistic_unchoke_time_scaler = 0; + } + m_ses.choke_peer(*this); + m_ses.m_unchoke_time_scaler = 0; + } + } + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void peer_connection::incoming_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have(index)) return; + } +#endif + + if (is_disconnecting()) return; + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE [ piece: %d ]", index); +#endif + + if (is_disconnecting()) return; + + if (!t->valid_metadata() && index >= int(m_have_piece.size())) + { + if (index < 131072) + { + // if we don't have metadata + // and we might not have received a bitfield + // extend the bitmask to fit the new + // have message + m_have_piece.resize(index + 1, false); + } + else + { + // unless the index > 64k, in which case + // we just ignore it + return; + } + } + + // if we got an invalid message, abort + if (index >= int(m_have_piece.size()) || index < 0) + { + disconnect(errors::invalid_have, 2); + return; + } + + if (t->super_seeding() && !m_ses.settings().strict_super_seeding) + { + // if we're superseeding and the peer just told + // us that it completed the piece we're superseeding + // to it, change the superseeding piece for this peer + // if the peer optimizes out redundant have messages + // this will be handled when the peer sends not-interested + // instead. + if (super_seeded_piece(index)) + { + superseed_piece(index, t->get_piece_to_super_seed(m_have_piece)); + } + } + + if (m_have_piece[index]) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(" got redundant HAVE message for index: %d", index); +#endif + return; + } + + m_have_piece.set_bit(index); + ++m_num_pieces; + + // if the peer is downloading stuff, it must have metadata + m_has_metadata = true; + + // only update the piece_picker if + // we have the metadata and if + // we're not a seed (in which case + // we won't have a piece picker) + if (!t->valid_metadata()) return; + + t->peer_has(index, this); + + // this will disregard all have messages we get within + // the first two seconds. Since some clients implements + // lazy bitfields, these will not be reliable to use + // for an estimated peer download rate. + if (!peer_info_struct() + || m_ses.session_time() - peer_info_struct()->last_connected > 2) + { + // update bytes downloaded since last timer + ++m_remote_pieces_dled; + } + + // it's important to not disconnect before we have + // updated the piece picker, otherwise we will incorrectly + // decrement the piece count without first incrementing it + if (is_seed()) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + t->seen_complete(); + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + } + + // it's important to update whether we're intersted in this peer before + // calling disconnect_if_redundant, otherwise we may disconnect even if + // we are interested + if (!t->have_piece(index) + && !t->is_seed() + && !is_interesting() + && t->picker().piece_priority(index) != 0) + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + if (is_disconnecting()) return; + + // if we're super seeding, this might mean that somebody + // forwarded this piece. In which case we need to give + // a new piece to that peer + if (t->super_seeding() + && m_ses.settings().strict_super_seeding + && (!super_seeded_piece(index) || t->num_peers() == 1)) + { + for (torrent::peer_iterator i = t->begin() + , end(t->end()); i != end; ++i) + { + peer_connection* p = *i; + if (!p->super_seeded_piece(index)) continue; + if (!p->has_piece(index)) continue; + p->superseed_piece(index, t->get_piece_to_super_seed(p->get_bitfield())); + } + } + } + + // ----------------------------- + // -------- DONT HAVE ---------- + // ----------------------------- + + void peer_connection::incoming_dont_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_dont_have(index)) return; + } +#endif + + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DONT_HAVE [ piece: %d ]", index); +#endif + + // if we got an invalid message, abort + if (index >= int(m_have_piece.size()) || index < 0) + { + disconnect(errors::invalid_dont_have, 2); + return; + } + + if (!m_have_piece[index]) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(" got redundant DONT_HAVE message for index: %d", index); +#endif + return; + } + + bool was_seed = is_seed(); + m_have_piece.clear_bit(index); + TORRENT_ASSERT(m_num_pieces > 0); + --m_num_pieces; + + // only update the piece_picker if + // we have the metadata and if + // we're not a seed (in which case + // we won't have a piece picker) + if (!t->valid_metadata()) return; + + t->peer_lost(index, this); + + if (was_seed) + t->get_policy().set_seed(m_peer_info, false); + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void peer_connection::incoming_bitfield(bitfield const& bits) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_bitfield(bits)) return; + } +#endif + + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + std::string bitfield_str; + bitfield_str.resize(bits.size()); + for (int i = 0; i < int(bits.size()); ++i) + bitfield_str[i] = bits[i] ? '1' : '0'; + peer_log("<== BITFIELD [ %s ]", bitfield_str.c_str()); +#endif + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && (bits.size() + 7) / 8 != (m_have_piece.size() + 7) / 8) + { + disconnect(errors::invalid_bitfield_size, 2); + return; + } + + if (m_bitfield_received) + { + // if we've already received a bitfield message + // we first need to count down all the pieces + // we believe the peer has first + t->peer_lost(m_have_piece, this); + } + + m_bitfield_received = true; + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + if (!t->ready_for_connections()) + { +#ifdef TORRENT_VERBOSE_LOGGING + if (m_num_pieces == int(bits.size())) + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + m_have_piece = bits; + m_num_pieces = bits.count(); + t->get_policy().set_seed(m_peer_info, m_num_pieces == int(bits.size())); + return; + } + + TORRENT_ASSERT(t->valid_metadata()); + + int num_pieces = bits.count(); + if (num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + // if this is a web seed. we don't have a peer_info struct + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + + m_have_piece.set_all(); + m_num_pieces = num_pieces; + t->peer_has_all(this); + + // this will cause us to send the INTERESTED message + if (!t->is_upload_only()) + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + + return; + } + + // let the torrent know which pieces the + // peer has + // if we're a seed, we don't keep track of piece availability + t->peer_has(bits, this); + + m_have_piece = bits; + m_num_pieces = num_pieces; + + update_interest(); + } + + void peer_connection::disconnect_if_redundant() + { + // we cannot disconnect in a constructor + TORRENT_ASSERT(m_in_constructor == false); + if (!m_ses.settings().close_redundant_connections) return; + + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + + // if we don't have the metadata yet, don't disconnect + // also, if the peer doesn't have metadata we shouldn't + // disconnect it, since it may want to request the + // metadata from us + if (!t->valid_metadata() || !has_metadata()) return; + + // don't close connections in share mode, we don't know if we need them + if (t->share_mode()) return; + + if (m_upload_only && t->is_upload_only()) + { + if (!can_disconnect(error_code(errors::upload_upload_connection, get_libtorrent_category()))) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** the peer is upload-only and our torrent is also upload-only"); +#endif + disconnect(errors::upload_upload_connection); + return; + } + + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked()) + { + if (!can_disconnect(error_code(errors::uninteresting_upload_peer, get_libtorrent_category()))) return; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** the peer is upload-only and we're not interested in it"); +#endif + disconnect(errors::uninteresting_upload_peer); + return; + } + } + + bool peer_connection::can_disconnect(error_code const& ec) const + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::const_iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if (!(*i)->can_disconnect(ec)) return false; + } +#endif + return true; + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void peer_connection::incoming_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_STATS + ++m_ses.m_piece_requests; +#endif + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("<== REQUEST [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + + if (t->super_seeding() + && !super_seeded_piece(r.piece)) + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif + ++m_num_invalid_requests; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ piece not superseeded " + "i: %d t: %d n: %d h: %d ss1: %d ss2: %d ]" + , m_peer_interested + , int(t->torrent_file().piece_size(r.piece)) + , t->torrent_file().num_pieces() + , t->have_piece(r.piece) + , m_superseed_piece[0] + , m_superseed_piece[1]); +#endif + + write_reject_request(r); + + if (t->alerts().should_post()) + { + t->alerts().post_alert(invalid_request_alert( + t->get_handle(), m_remote, m_peer_id, r)); + } + return; + } + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_request(r)) return; + } +#endif + if (is_disconnecting()) return; + + if (!t->valid_metadata()) + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif + // if we don't have valid metadata yet, + // we shouldn't get a request +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ we don't have metadata yet ]"); + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + return; + } + + if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue) + { +#ifdef TORRENT_STATS + ++m_ses.m_max_piece_requests; +#endif + // don't allow clients to abuse our + // memory consumption. + // ignore requests if the client + // is making too many of them. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ incoming request queue full %d ]" + , int(m_requests.size())); + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + return; + } + + int fast_idx = -1; + std::vector::iterator fast_iter = std::find(m_accept_fast.begin() + , m_accept_fast.end(), r.piece); + if (fast_iter != m_accept_fast.end()) fast_idx = fast_iter - m_accept_fast.begin(); + + // make sure this request + // is legal and that the peer + // is not choked + if (r.piece >= 0 + && r.piece < t->torrent_file().num_pieces() + && t->have_piece(r.piece) + && r.start >= 0 + && r.start < t->torrent_file().piece_size(r.piece) + && r.length > 0 + && r.length + r.start <= t->torrent_file().piece_size(r.piece) + && m_peer_interested + && r.length <= t->block_size()) + { + // if we have choked the client + // ignore the request + const int blocks_per_piece = static_cast( + (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); + + // disconnect peers that downloads more than foo times an allowed + // fast piece + if (m_choked && fast_idx != -1 + && m_accept_fast_piece_cnt[fast_idx] >= 3 * blocks_per_piece + && can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category()))) + { + disconnect(errors::too_many_requests_when_choked); + return; + } + + if (m_choked && fast_idx == -1) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** REJECTING REQUEST [ peer choked and piece not in allowed fast set ]"); + peer_log(" ==> REJECT_PIECE [ piece: %d | s: %d | l: %d ]" + , r.piece, r.start, r.length); +#endif +#ifdef TORRENT_STATS + ++m_ses.m_choked_piece_requests; +#endif + write_reject_request(r); + + time_duration since_choked = time_now() - m_last_choke; + + // allow peers to send request up to 2 seconds after getting choked, + // the disconnect them + if (total_milliseconds(since_choked) > 2000 + && can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category()))) + { + disconnect(errors::too_many_requests_when_choked, 2); + return; + } + } + else + { + // increase the allowed fast set counter + if (fast_idx != -1) + ++m_accept_fast_piece_cnt[fast_idx]; + + m_requests.push_back(r); +#ifdef TORRENT_REQUEST_LOGGING + if (m_ses.m_request_log) + write_request_log(m_ses.m_request_log, t->info_hash(), this, r); +#endif + m_last_incoming_request = time_now(); + fill_send_buffer(); + } + } + else + { +#ifdef TORRENT_STATS + ++m_ses.m_invalid_piece_requests; +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_REQUEST [ " + "i: %d t: %d n: %d h: %d block_limit: %d ]" + , m_peer_interested + , int(t->torrent_file().piece_size(r.piece)) + , t->torrent_file().num_pieces() + , t->have_piece(r.piece) + , t->block_size()); + + peer_log("==> REJECT_PIECE [ piece: %d | s: %d | l: %d ] invalid request" + , r.piece , r.start , r.length); +#endif + + write_reject_request(r); + ++m_num_invalid_requests; + + if (t->alerts().should_post()) + { + t->alerts().post_alert(invalid_request_alert( + t->get_handle(), m_remote, m_peer_id, r)); + } + } + } + + void peer_connection::incoming_piece_fragment(int bytes) + { + m_last_piece = time_now(); + TORRENT_ASSERT(m_outstanding_bytes >= bytes); + m_outstanding_bytes -= bytes; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + boost::shared_ptr t = associated_torrent().lock(); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece + bytes <= t->block_size()); + m_received_in_piece += bytes; +#endif + + // progress of this torrent increased + t->state_updated(); + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void peer_connection::start_receive_piece(peer_request const& r) + { +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif +#if TORRENT_USE_ASSERTS + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = recv_buffer.end - recv_buffer.begin; + TORRENT_ASSERT(recv_pos >= 9); +#endif + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + if (!verify_piece(r)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** INVALID_PIECE [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + disconnect(errors::invalid_piece, 2); + return; + } + + piece_block b(r.piece, r.start / t->block_size()); + m_receiving_block = b; + + bool in_req_queue = false; + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->block != b) continue; + in_req_queue = true; + break; + } + + // if this is not in the request queue, we have to + // assume our outstanding bytes includes this piece too + // if we're disconnecting, we shouldn't add pieces + if (!in_req_queue && !m_disconnecting) + { + for (std::vector::iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + if (i->block != b) continue; + in_req_queue = true; + if (i - m_request_queue.begin() < m_queued_time_critical) + --m_queued_time_critical; + m_request_queue.erase(i); + break; + } + + m_download_queue.insert(m_download_queue.begin(), b); + if (!in_req_queue) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(unwanted_block_alert(t->get_handle(), m_remote + , m_peer_id, b.block_index, b.piece_index)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** The block we just got was not in the request queue ***"); +#endif + TORRENT_ASSERT(m_download_queue.front().block == b); + m_download_queue.front().not_wanted = true; + } + m_outstanding_bytes += r.length; + } + } + +#ifdef TORRENT_DEBUG + struct check_postcondition + { + check_postcondition(boost::shared_ptr const& t_ + , bool init_check = true): t(t_) { if (init_check) check(); } + + ~check_postcondition() { check(); } + + void check() + { + if (!t->is_seed()) + { + const int blocks_per_piece = static_cast( + (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); + + std::vector const& dl_queue + = t->picker().get_download_queue(); + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + TORRENT_ASSERT(i->finished <= blocks_per_piece); + } + } + } + + shared_ptr t; + }; +#endif + + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void peer_connection::incoming_piece(peer_request const& p, char const* data) + { + char* buffer = m_ses.allocate_disk_buffer("receive buffer"); + if (buffer == 0) + { + disconnect(errors::no_memory); + return; + } + disk_buffer_holder holder(m_ses, buffer); + std::memcpy(buffer, data, p.length); + incoming_piece(p, holder); + } + + void peer_connection::incoming_piece(peer_request const& p, disk_buffer_holder& data) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(m_disk_recv_buffer_size == 0); + + // we're not receiving any block right now + m_receiving_block = piece_block::invalid; + +#ifdef TORRENT_CORRUPT_DATA + // corrupt all pieces from certain peers + if (m_remote.address().is_v4() + && (m_remote.address().to_v4().to_ulong() & 0xf) == 0) + { + data.get()[0] = ~data.get()[0]; + } +#endif + + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + if (is_disconnecting()) return; + + update_desired_queue_size(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_piece(p, data)) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece == p.length); + m_received_in_piece = 0; +#endif + return; + } + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_DEBUG + check_postcondition post_checker_(t); +#if TORRENT_USE_INVARIANT_CHECKS + t->check_invariant(); +#endif +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + hasher h; + h.update(data.get(), p.length); + peer_log("<== PIECE [ piece: %d | s: %d | l: %d | ds: %d | qs: %d | q: %d | hash: %s ]" + , p.piece, p.start, p.length, statistics().download_rate() + , int(m_desired_queue_size), int(m_download_queue.size()) + , to_hex(h.final().to_string()).c_str()); +#endif + + if (p.length == 0) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(peer_error_alert(t->get_handle(), m_remote + , m_peer_id, errors::peer_sent_empty_piece)); + } + // This is used as a reject-request by bitcomet + incoming_reject_request(p); + return; + } + + // if we're already seeding, don't bother, + // just ignore it + if (t->is_seed()) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_received_in_piece == p.length); + m_received_in_piece = 0; +#endif + if (!m_download_queue.empty()) m_download_queue.erase(m_download_queue.begin()); + t->add_redundant_bytes(p.length, torrent::piece_seed); + return; + } + + ptime now = time_now(); + + piece_picker& picker = t->picker(); + piece_manager& fs = t->filesystem(); + + piece_block block_finished(p.piece, p.start / t->block_size()); + TORRENT_ASSERT(verify_piece(p)); + + std::vector::iterator b + = std::find_if( + m_download_queue.begin() + , m_download_queue.end() + , has_block(block_finished)); + + if (b == m_download_queue.end()) + { + if (t->alerts().should_post()) + { + t->alerts().post_alert(unwanted_block_alert(t->get_handle(), m_remote + , m_peer_id, block_finished.block_index, block_finished.piece_index)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** The block we just got was not in the request queue ***"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); + m_received_in_piece = 0; +#endif + t->add_redundant_bytes(p.length, torrent::piece_unknown); + + // the bytes of the piece we just completed have been deducted from + // m_outstanding_bytes as we received it, in incoming_piece_fragment. + // however, it now turns out the piece we received wasn't in the + // download queue, so we still have the same number of pieces in the + // download queue, which is why we need to add the bytes back. + m_outstanding_bytes += p.length; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + return; + } + +#if TORRENT_USE_ASSERTS + pending_block pending_b = *b; +#endif + + int block_index = b - m_download_queue.begin(); + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + for (int i = 0; i < block_index; ++i) + { + pending_block& qe = m_download_queue[i]; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + TORRENT_ASSERT(i < block_index); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** SKIPPED_PIECE [ piece: %d b: %d dqs: %d ]" + , qe.block.piece_index, qe.block.block_index, int(m_desired_queue_size)); +#endif + + ++qe.skipped; + // if the number of times a block is skipped by out of order + // blocks exceeds the size of the outstanding queue, assume that + // the other end dropped the request. + if (m_ses.m_settings.drop_skipped_requests + && qe.skipped > m_desired_queue_size * 2) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(request_dropped_alert(t->get_handle() + , remote(), pid(), qe.block.block_index, qe.block.piece_index)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** DROPPED_PIECE [ piece: %d b: %d dqs: %d skip: %d ]" + , qe.block.piece_index, qe.block.block_index + , int(m_desired_queue_size), qe.skipped); +#endif + if (!qe.timed_out && !qe.not_wanted) + picker.abort_download(qe.block, peer_info_struct()); + + TORRENT_ASSERT(m_outstanding_bytes >= t->to_req(qe.block).length); + m_outstanding_bytes -= t->to_req(qe.block).length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); + m_download_queue.erase(m_download_queue.begin() + i); + --i; + --block_index; + TORRENT_ASSERT(m_download_queue[block_index] == pending_b); +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + } + TORRENT_ASSERT(int(m_download_queue.size()) > block_index); + b = m_download_queue.begin() + block_index; + TORRENT_ASSERT(*b == pending_b); + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT_VAL(m_received_in_piece == p.length, m_received_in_piece); + m_received_in_piece = 0; +#endif + // if the block we got is already finished, then ignore it + if (picker.is_downloaded(block_finished)) + { + torrent::wasted_reason_t reason; + if (b->timed_out) reason = torrent::piece_timed_out; + else if (b->not_wanted) reason = torrent::piece_cancelled; + else if (b->busy) reason = torrent::piece_end_game; + else reason = torrent::piece_unknown; + + t->add_redundant_bytes(p.length, reason); + + m_download_queue.erase(b); + m_timeout_extend = 0; + + if (!m_download_queue.empty()) + m_requested = now; + +#ifdef TORRENT_STATS + ++m_ses.m_incoming_redundant_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + return; + } + + if (total_seconds(now - m_requested) + < m_ses.settings().request_timeout + && m_snubbed) + { + m_snubbed = false; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(peer_unsnubbed_alert(t->get_handle() + , m_remote, m_peer_id)); + } + } + + if (t->is_deleted()) return; + + int write_queue_size = fs.async_write(p, data, boost::bind(&peer_connection::on_disk_write_complete + , self(), _1, _2, p, t)); + m_outstanding_writing_bytes += p.length; + m_download_queue.erase(b); + + if (write_queue_size / 16 / 1024 > m_ses.m_settings.cache_size / 2 + && m_ses.m_settings.cache_size > 5 + && (now - m_ses.m_last_disk_queue_performance_warning) > seconds(10) + && m_ses.m_alerts.should_post()) + { + m_ses.m_last_disk_queue_performance_warning = now; + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::too_high_disk_queue_limit)); + } + + if (!m_ses.can_write_to_disk() + && m_ses.settings().max_queued_disk_bytes + && t->alerts().should_post() + && (now - m_ses.m_last_disk_performance_warning) > seconds(10)) + { + m_ses.m_last_disk_performance_warning = now; + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::outstanding_disk_buffer_limit_reached)); + } + + if (!m_download_queue.empty()) + { + m_timeout_extend = (std::max)(m_timeout_extend + - m_ses.settings().request_timeout, 0); + m_requested += seconds(m_ses.settings().request_timeout); + if (m_requested > now) m_requested = now; + } + else + { + m_timeout_extend = 0; + } + + bool was_finished = picker.is_piece_finished(p.piece); + // did we request this block from any other peers? + bool multi = picker.num_peers(block_finished) > 1; + picker.mark_as_writing(block_finished, peer_info_struct()); + + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + // if we requested this block from other peers, cancel it now + if (multi) t->cancel_block(block_finished); + + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + +#if TORRENT_USE_INVARIANT_CHECKS \ + && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + t->check_invariant(); +#endif + +#if TORRENT_USE_ASSERTS + piece_picker::downloading_piece pi; + picker.piece_info(p.piece, pi); + int num_blocks = picker.blocks_in_piece(p.piece); + TORRENT_ASSERT(pi.writing + pi.finished + pi.requested <= num_blocks); + TORRENT_ASSERT(picker.is_piece_finished(p.piece) == (pi.writing + pi.finished == num_blocks)); +#endif + + // did we just finish the piece? + // this means all blocks are either written + // to disk or are in the disk write cache + if (picker.is_piece_finished(p.piece) && !was_finished) + { +#ifdef TORRENT_DEBUG + check_postcondition post_checker2_(t, false); +#endif + t->async_verify_piece(p.piece, boost::bind(&torrent::piece_finished, t + , p.piece, _1)); + } + + if (is_disconnecting()) return; + +#ifdef TORRENT_STATS + ++m_ses.m_incoming_piece_picks; +#endif + request_a_block(*t, *this); + send_block_requests(); + } + + void peer_connection::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p, boost::shared_ptr t) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_disk_write_counter]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + // flush send buffer at the end of this scope + // TODO: 1 peers should really be corked/uncorked outside of + // all completed disk operations + cork _c(*this); + + INVARIANT_CHECK; + + m_outstanding_writing_bytes -= p.length; + TORRENT_ASSERT(m_outstanding_writing_bytes >= 0); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) +// (*m_ses.m_logger) << time_now_string() << " *** DISK_WRITE_COMPLETE [ p: " +// << p.piece << " o: " << p.start << " ]\n"; +#endif + + if (!t) + { + disconnect(j.error); + return; + } + + // in case the outstanding bytes just dropped down + // to allow to receive more data + setup_receive(read_async); + + piece_block block_finished(p.piece, p.start / t->block_size()); + + if (ret == -1) + { + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); + return; + } + + if (!t->has_picker()) return; + + piece_picker& picker = t->picker(); + + TORRENT_ASSERT(p.piece == j.piece); + TORRENT_ASSERT(p.start == j.offset); + TORRENT_ASSERT(picker.num_peers(block_finished) == 0); + picker.mark_as_finished(block_finished, peer_info_struct()); + if (t->alerts().should_post()) + { + t->alerts().post_alert(block_finished_alert(t->get_handle(), + remote(), pid(), block_finished.block_index, block_finished.piece_index)); + } + + if (t->is_aborted()) return; + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void peer_connection::incoming_cancel(peer_request const& r) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_cancel(r)) return; + } +#endif + if (is_disconnecting()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== CANCEL [ piece: %d | s: %d | l: %d ]", r.piece, r.start, r.length); +#endif + + std::vector::iterator i + = std::find(m_requests.begin(), m_requests.end(), r); + + if (i != m_requests.end()) + { +#ifdef TORRENT_STATS + ++m_ses.m_cancelled_piece_requests; +#endif + m_requests.erase(i); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** GOT CANCEL NOT IN THE QUEUE"); +#endif + } + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void peer_connection::incoming_dht_port(int listen_port) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== DHT_PORT [ p: %d ]", listen_port); +#endif +#ifndef TORRENT_DISABLE_DHT + m_ses.add_dht_node(udp::endpoint( + m_remote.address(), listen_port)); +#endif + } + + // ----------------------------- + // --------- HAVE ALL ---------- + // ----------------------------- + + void peer_connection::incoming_have_all() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // we cannot disconnect in a constructor, and + // this function may end up doing that + TORRENT_ASSERT(m_in_constructor == false); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE_ALL"); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have_all()) return; + } +#endif + if (is_disconnecting()) return; + + if (m_bitfield_received) + t->peer_lost(m_have_piece, this); + + m_have_all = true; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** THIS IS A SEED [ p: %p ]", m_peer_info); +#endif + + t->get_policy().set_seed(m_peer_info, true); + m_upload_only = true; + m_bitfield_received = true; + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + if (!t->ready_for_connections()) + { + // assume seeds are interesting when we + // don't even have the metadata + t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + // TODO: this might need something more + // so that once we have the metadata + // we can construct a full bitfield + return; + } + + TORRENT_ASSERT(!m_have_piece.empty()); + m_have_piece.set_all(); + m_num_pieces = m_have_piece.size(); + + t->peer_has_all(this); + + // if we're finished, we're not interested + if (t->is_upload_only()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); + } + + // ----------------------------- + // --------- HAVE NONE --------- + // ----------------------------- + + void peer_connection::incoming_have_none() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== HAVE_NONE"); +#endif + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have_none()) return; + } +#endif + if (is_disconnecting()) return; + + if (m_bitfield_received) + t->peer_lost(m_have_piece, this); + + t->get_policy().set_seed(m_peer_info, false); + m_bitfield_received = true; + + m_have_piece.clear_all(); + m_num_pieces = 0; + + // if the peer is ready to download stuff, it must have metadata + m_has_metadata = true; + + // we're never interested in a peer that doesn't have anything + send_not_interested(); + + TORRENT_ASSERT(!m_have_piece.empty() || !t->ready_for_connections()); + disconnect_if_redundant(); + } + + // ----------------------------- + // ------- ALLOWED FAST -------- + // ----------------------------- + + void peer_connection::incoming_allowed_fast(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== ALLOWED_FAST [ %d ]", index); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_allowed_fast(index)) return; + } +#endif + if (is_disconnecting()) return; + if (index < 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_ALLOWED_FAST [ %d ]", index); +#endif + return; + } + + if (t->valid_metadata()) + { + if (index >= int(m_have_piece.size())) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("<== INVALID_ALLOWED_FAST [ %d | s: %d ]" + , index, int(m_have_piece.size())); +#endif + return; + } + + // if we already have the piece, we can + // ignore this message + if (t->have_piece(index)) + return; + } + + // if we don't have the metadata, we'll verify + // this piece index later + m_allowed_fast.push_back(index); + + // if the peer has the piece and we want + // to download it, request it + if (int(m_have_piece.size()) > index + && m_have_piece[index] + && t->valid_metadata() + && t->has_picker() + && t->picker().piece_priority(index) > 0) + { + t->get_policy().peer_is_interesting(*this); + } + } + + std::vector const& peer_connection::allowed_fast() + { + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + // TODO: sort the allowed fast set in priority order + return m_allowed_fast; + } + + bool peer_connection::can_request_time_critical() const + { + if (has_peer_choked() || !is_interesting()) return false; + if ((int)m_download_queue.size() + (int)m_request_queue.size() + > m_desired_queue_size * 2) return false; + if (on_parole()) return false; + if (m_disconnecting) return false; + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (t->upload_mode()) return false; + + // ignore snubbed peers, since they're not likely to return pieces in a + // timely manner anyway + if (m_snubbed) return false; + return true; + } + + bool peer_connection::make_time_critical(piece_block const& block) + { + std::vector::iterator rit = std::find_if(m_request_queue.begin() + , m_request_queue.end(), has_block(block)); + if (rit == m_request_queue.end()) return false; +#if TORRENT_USE_ASSERTS + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + TORRENT_ASSERT(t->has_picker()); + TORRENT_ASSERT(t->picker().is_requested(block)); +#endif + // ignore it if it's already time critical + if (rit - m_request_queue.begin() < m_queued_time_critical) return false; + pending_block b = *rit; + m_request_queue.erase(rit); + m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical, b); + ++m_queued_time_critical; + return true; + } + + bool peer_connection::add_request(piece_block const& block, int flags) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(!m_disconnecting); + TORRENT_ASSERT(t->valid_metadata()); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); + TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); + TORRENT_ASSERT(!t->picker().is_requested(block) || (t->picker().num_peers(block) > 0)); + TORRENT_ASSERT(!t->have_piece(block.piece_index)); + TORRENT_ASSERT(std::find_if(m_download_queue.begin(), m_download_queue.end() + , has_block(block)) == m_download_queue.end()); + TORRENT_ASSERT(std::find(m_request_queue.begin(), m_request_queue.end() + , block) == m_request_queue.end()); + + if (t->upload_mode()) return false; + if (m_disconnecting) return false; + + piece_picker::piece_state_t state; + peer_speed_t speed = peer_speed(); + char const* speedmsg = 0; + if (speed == fast) + { + speedmsg = "fast"; + state = piece_picker::fast; + } + else if (speed == medium) + { + speedmsg = "medium"; + state = piece_picker::medium; + } + else + { + speedmsg = "slow"; + state = piece_picker::slow; + } + + if ((flags & req_busy) && !(flags & req_time_critical)) + { + // this block is busy (i.e. it has been requested + // from another peer already). Only allow one busy + // request in the pipeline at the time + // this rule does not apply to time critical pieces, + // in which case we are allowed to pick more than one + // busy blocks + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->busy) return false; + } + + for (std::vector::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + if (i->busy) return false; + } + } + + if (!t->picker().mark_as_downloading(block, peer_info_struct(), state)) + return false; + + if (t->alerts().should_post()) + { + t->alerts().post_alert(block_downloading_alert(t->get_handle(), + remote(), pid(), speedmsg, block.block_index, block.piece_index)); + } + + pending_block pb(block); + pb.busy = (flags & req_busy) ? true : false; + if (flags & req_time_critical) + { + m_request_queue.insert(m_request_queue.begin() + m_queued_time_critical + , pb); + ++m_queued_time_critical; + } + else + { + m_request_queue.push_back(pb); + } + return true; + } + + void peer_connection::cancel_all_requests() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + // this peer might be disconnecting + if (!t) return; + + TORRENT_ASSERT(t->valid_metadata()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** CANCEL ALL REQUESTS"); +#endif + + while (!m_request_queue.empty()) + { + t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + m_queued_time_critical = 0; + + // make a local temporary copy of the download queue, since it + // may be modified when we call write_cancel (for peers that don't + // support the FAST extensions). + std::vector temp_copy = m_download_queue; + + for (std::vector::iterator i = temp_copy.begin() + , end(temp_copy.end()); i != end; ++i) + { + piece_block b = i->block; + + int block_offset = b.block_index * t->block_size(); + int block_size + = (std::min)(t->torrent_file().piece_size(b.piece_index)-block_offset, + t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + // we can't cancel the piece if we've started receiving it + if (m_receiving_block == b) continue; + + peer_request r; + r.piece = b.piece_index; + r.start = block_offset; + r.length = block_size; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CANCEL [ piece: %d s: %d l: %d b: %d ]" + , b.piece_index, block_offset, block_size, b.block_index); +#endif + write_cancel(r); + } + } + + void peer_connection::cancel_request(piece_block const& block, bool force) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + // this peer might be disconnecting + if (!t) return; + + TORRENT_ASSERT(t->valid_metadata()); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(int(block.piece_index) < t->torrent_file().num_pieces()); + TORRENT_ASSERT(int(block.block_index) < t->torrent_file().piece_size(block.piece_index)); + + // if all the peers that requested this block has been + // cancelled, then just ignore the cancel. + if (!t->picker().is_requested(block)) return; + + std::vector::iterator it + = std::find_if(m_download_queue.begin(), m_download_queue.end(), has_block(block)); + if (it == m_download_queue.end()) + { + std::vector::iterator rit = std::find_if(m_request_queue.begin() + , m_request_queue.end(), has_block(block)); + + // when a multi block is received, it is cancelled + // from all peers, so if this one hasn't requested + // the block, just ignore to cancel it. + if (rit == m_request_queue.end()) return; + + if (rit - m_request_queue.begin() < m_queued_time_critical) + --m_queued_time_critical; + + t->picker().abort_download(block, peer_info_struct()); + m_request_queue.erase(rit); + // since we found it in the request queue, it means it hasn't been + // sent yet, so we don't have to send a cancel. + return; + } + + int block_offset = block.block_index * t->block_size(); + int block_size + = (std::min)(t->torrent_file().piece_size(block.piece_index)-block_offset, + t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + it->not_wanted = true; + + if (force) t->picker().abort_download(block, peer_info_struct()); + + if (m_outstanding_bytes < block_size) return; + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CANCEL [ piece: %d s: %d l: %d b: %d ]" + , block.piece_index, block_offset, block_size, block.block_index); +#endif + write_cancel(r); + } + + bool peer_connection::send_choke() + { + INVARIANT_CHECK; + + if (m_peer_info && m_peer_info->optimistically_unchoked) + m_peer_info->optimistically_unchoked = false; + + if (m_choked) return false; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> CHOKE"); +#endif + write_choke(); + m_choked = true; + + m_last_choke = time_now(); + m_num_invalid_requests = 0; + + // reject the requests we have in the queue + // except the allowed fast pieces + for (std::vector::iterator i = m_requests.begin(); + i != m_requests.end();) + { + if (std::find(m_accept_fast.begin(), m_accept_fast.end(), i->piece) + != m_accept_fast.end()) + { + ++i; + continue; + } + peer_request const& r = *i; +#ifdef TORRENT_STATS + ++m_ses.m_choked_piece_requests; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ] choking" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + i = m_requests.erase(i); + } + return true; + } + + bool peer_connection::send_unchoke() + { + INVARIANT_CHECK; + + if (!m_choked) return false; + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return false; + + if (!m_sent_suggests) + { + std::vector ret; + t->get_suggested_pieces(ret); + for (std::vector::iterator i = ret.begin() + , end(ret.end()); i != end; ++i) + { + TORRENT_ASSERT(*i >= 0); + send_suggest(*i); + } + + m_sent_suggests = true; + } + + m_last_unchoke = time_now(); + write_unchoke(); + m_choked = false; + + m_uploaded_at_last_unchoke = m_statistics.total_payload_upload(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> UNCHOKE"); +#endif + return true; + } + + void peer_connection::send_interested() + { + if (m_interesting) return; + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return; + m_interesting = true; + write_interested(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> INTERESTED"); +#endif + } + + void peer_connection::send_not_interested() + { + // we cannot disconnect in a constructor, and + // this function may end up doing that + TORRENT_ASSERT(m_in_constructor == false); + + if (!m_interesting) + { + disconnect_if_redundant(); + return; + } + + boost::shared_ptr t = m_torrent.lock(); + if (!t->ready_for_connections()) return; + m_interesting = false; + write_not_interested(); + + m_became_uninteresting = time_now(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> NOT_INTERESTED"); +#endif + disconnect_if_redundant(); + } + + void peer_connection::send_suggest(int piece) + { + if (m_connecting) return; + if (in_handshake()) return; + + // don't suggest a piece that the peer already has + // don't suggest anything to a peer that isn't interested + if (has_piece(piece) + || !m_peer_interested) + return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> SUGGEST [ %d ]", piece); +#endif + write_suggest(piece); + } + + void peer_connection::send_block_requests() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (m_disconnecting) return; + + if (t->graceful_pause() && m_outstanding_bytes == 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** GRACEFUL PAUSE [ NO MORE DOWNLOAD ]"); +#endif + disconnect(errors::torrent_paused); + return; + } + + // we can't download pieces in these states + if (t->state() == torrent_status::checking_files + || t->state() == torrent_status::checking_resume_data + || t->state() == torrent_status::downloading_metadata + || t->state() == torrent_status::allocating) + return; + + if ((int)m_download_queue.size() >= m_desired_queue_size + || t->upload_mode()) return; + + bool empty_download_queue = m_download_queue.empty(); + + while (!m_request_queue.empty() + && ((int)m_download_queue.size() < m_desired_queue_size + || m_queued_time_critical > 0)) + { + pending_block block = m_request_queue.front(); + + m_request_queue.erase(m_request_queue.begin()); + if (m_queued_time_critical) --m_queued_time_critical; + + // if we're a seed, we don't have a piece picker + // so we don't have to worry about invariants getting + // out of sync with it + if (!t->has_picker()) continue; + + // this can happen if a block times out, is re-requested and + // then arrives "unexpectedly" + if (t->picker().is_finished(block.block) + || t->picker().is_downloaded(block.block)) + { + t->picker().abort_download(block.block, peer_info_struct()); + continue; + } + + int block_offset = block.block.block_index * t->block_size(); + int block_size = (std::min)(t->torrent_file().piece_size( + block.block.piece_index) - block_offset, t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + peer_request r; + r.piece = block.block.piece_index; + r.start = block_offset; + r.length = block_size; + + TORRENT_ASSERT(verify_piece(t->to_req(block.block))); + m_download_queue.push_back(block); + m_outstanding_bytes += block_size; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + +/* +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** REQUEST-QUEUE** [ " + "piece: " << block.piece_index << " | " + "block: " << block.block_index << " ]\n"; +#endif +*/ + // if we are requesting large blocks, merge the smaller + // blocks that are in the same piece into larger requests + if (m_request_large_blocks) + { + int blocks_per_piece = t->torrent_file().piece_length() / t->block_size(); + + while (!m_request_queue.empty()) + { + // check to see if this block is connected to the previous one + // if it is, merge them, otherwise, break this merge loop + pending_block const& front = m_request_queue.front(); + if (front.block.piece_index * blocks_per_piece + front.block.block_index + != block.block.piece_index * blocks_per_piece + block.block.block_index + 1) + break; + block = m_request_queue.front(); + m_request_queue.erase(m_request_queue.begin()); + TORRENT_ASSERT(verify_piece(t->to_req(block.block))); + m_download_queue.push_back(block); + if (m_queued_time_critical) --m_queued_time_critical; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MERGING REQUEST [ piece: %d block: %d ]" + , block.block.piece_index, block.block.block_index); +#endif + + block_offset = block.block.block_index * t->block_size(); + block_size = (std::min)(t->torrent_file().piece_size( + block.block.piece_index) - block_offset, t->block_size()); + TORRENT_ASSERT(block_size > 0); + TORRENT_ASSERT(block_size <= t->block_size()); + + r.length += block_size; + m_outstanding_bytes += block_size; +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + } + + // the verification will fail for coalesced blocks + TORRENT_ASSERT(verify_piece(r) || m_request_large_blocks); + +#ifndef TORRENT_DISABLE_EXTENSIONS + bool handled = false; + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((handled = (*i)->write_request(r))) break; + } + if (is_disconnecting()) return; + if (!handled) +#endif + { + write_request(r); + m_last_request = time_now(); + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> REQUEST [ piece: %d | s: %d | l: %d | ds: %d B/s | " + "dqs: %d rqs: %d blk: %s ]" + , r.piece, r.start, r.length, statistics().download_rate() + , int(m_desired_queue_size), int(m_download_queue.size()) + , m_request_large_blocks?"large":"single"); +#endif + } + m_last_piece = time_now(); + + if (!m_download_queue.empty() + && empty_download_queue) + { + // This means we just added a request to this connection + m_requested = time_now(); + } + } + + void peer_connection::on_timeout() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + connect_failed(errors::timed_out); + } + + void peer_connection::connect_failed(error_code const& e) + { + TORRENT_ASSERT(e); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("CONNECTION FAILED: %s", print_endpoint(m_remote).c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote) << "\n"; +#endif + +#ifdef TORRENT_STATS + ++m_ses.m_connect_timeouts; +#endif + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(!m_connecting || t); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + // a connection attempt using uTP just failed + // mark this peer as not supporting uTP + // we'll never try it again (unless we're trying holepunch) + if (is_utp(*m_socket) + && m_peer_info + && m_peer_info->supports_utp + && !m_holepunch_mode) + { + m_peer_info->supports_utp = false; + // reconnect immediately using TCP + policy::peer* pi = peer_info_struct(); + boost::shared_ptr t = m_torrent.lock(); + fast_reconnect(true); + disconnect(e, 0); + if (t && pi) t->connect_to_peer(pi, true); + return; + } + + if (m_holepunch_mode) + fast_reconnect(true); + +#ifndef TORRENT_DISABLE_EXTENSIONS + if ((!is_utp(*m_socket) + || !m_ses.m_settings.enable_outgoing_tcp) + && m_peer_info + && m_peer_info->supports_holepunch + && !m_holepunch_mode) + { + boost::shared_ptr t = m_torrent.lock(); + // see if we can try a holepunch + bt_peer_connection* p = t->find_introducer(remote()); + if (p) + p->write_holepunch_msg(bt_peer_connection::hp_rendezvous, remote(), 0); + } +#endif + + disconnect(e, 1); + return; + } + + // the error argument defaults to 0, which means deliberate disconnect + // 1 means unexpected disconnect/error + // 2 protocol error (client sent something invalid) + void peer_connection::disconnect(error_code const& ec, int error) + { +#if TORRENT_USE_ASSERTS + m_disconnect_started = true; +#endif + + if (m_disconnecting) return; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + switch (error) + { + case 0: + peer_log("*** CONNECTION CLOSED %s", ec.message().c_str()); + break; + case 1: + peer_log("*** CONNECTION FAILED %s", ec.message().c_str()); + break; + case 2: + peer_log("*** PEER ERROR %s", ec.message().c_str()); + break; + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->on_disconnect(ec); + } +#endif + // for incoming connections, we get invalid argument errors + // when asking for the remote endpoint and the socket already + // closed, which is an edge case, but possible to happen when + // a peer makes a TCP and uTP connection in parallel. + // for outgoing connections however, why would we get this? + TORRENT_ASSERT(ec != error::invalid_argument || !m_outgoing); + +#ifdef TORRENT_STATS + ++m_ses.m_disconnected_peers; + if (error == 2) ++m_ses.m_error_peers; + if (ec == error::connection_reset) ++m_ses.m_connreset_peers; + else if (ec == error::eof) ++m_ses.m_eof_peers; + else if (ec == error::connection_refused) ++m_ses.m_connrefused_peers; + else if (ec == error::connection_aborted) ++m_ses.m_connaborted_peers; + else if (ec == error::no_permission) ++m_ses.m_perm_peers; + else if (ec == error::no_buffer_space) ++m_ses.m_buffer_peers; + else if (ec == error::host_unreachable) ++m_ses.m_unreachable_peers; + else if (ec == error::broken_pipe) ++m_ses.m_broken_pipe_peers; + else if (ec == error::address_in_use) ++m_ses.m_addrinuse_peers; + else if (ec == error::access_denied) ++m_ses.m_no_access_peers; + else if (ec == error::invalid_argument) ++m_ses.m_invalid_arg_peers; + else if (ec == error::operation_aborted) ++m_ses.m_aborted_peers; + else if (ec == error_code(errors::upload_upload_connection) + || ec == error_code(errors::uninteresting_upload_peer) + || ec == error_code(errors::torrent_aborted) + || ec == error_code(errors::self_connection) + || ec == error_code(errors::torrent_paused)) + ++m_ses.m_uninteresting_peers; + + if (ec == error_code(errors::timed_out) + || ec == error::timed_out) + ++m_ses.m_transport_timeout_peers; + + if (ec == error_code(errors::timed_out_inactivity) + || ec == error_code(errors::timed_out_no_request) + || ec == error_code(errors::timed_out_no_interest)) + ++m_ses.m_timeout_peers; + + if (ec == error_code(errors::no_memory)) + ++m_ses.m_no_memory_peers; + + if (ec == error_code(errors::too_many_connections)) + ++m_ses.m_too_many_peers; + + if (ec == error_code(errors::timed_out_no_handshake)) + ++m_ses.m_connect_timeouts; + + if (is_utp(*m_socket)) ++m_ses.m_error_utp_peers; + else ++m_ses.m_error_tcp_peers; + + if (m_outgoing) ++m_ses.m_error_outgoing_peers; + else ++m_ses.m_error_incoming_peers; + + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (type() == bittorrent_connection) + { + bt_peer_connection* bt = static_cast(this); + if (bt->supports_encryption()) ++m_ses.m_error_encrypted_peers; + if (bt->rc4_encrypted() && bt->supports_encryption()) ++m_ses.m_error_rc4_peers; + } +#endif // TORRENT_DISABLE_ENCRYPTION +#endif + + // we cannot do this in a constructor + TORRENT_ASSERT(m_in_constructor == false); + if (error > 0) m_failed = true; + boost::intrusive_ptr me(this); + + INVARIANT_CHECK; + + if (m_channel_state[upload_channel] & peer_info::bw_disk) + { + m_ses.dec_disk_queue(upload_channel); + m_channel_state[upload_channel] &= ~peer_info::bw_disk; + } + if (m_channel_state[download_channel] & peer_info::bw_disk) + { + m_ses.dec_disk_queue(download_channel); + m_channel_state[download_channel] &= ~peer_info::bw_disk; + } + + boost::shared_ptr t = m_torrent.lock(); + if (m_connecting) + { + t->dec_num_connecting(); + m_connecting = false; + } + if (m_connection_ticket >= 0) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + torrent_handle handle; + if (t) handle = t->get_handle(); + + if (ec == error::address_in_use + && m_ses.m_settings.outgoing_ports.first != 0) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(performance_alert( + handle, performance_alert::too_few_outgoing_ports)); + } + + if (ec) + { + if ((error > 1 || ec.category() == get_socks_category()) + && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + peer_error_alert(handle, remote(), pid(), ec)); + } + else if (error <= 1 && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + peer_disconnected_alert(handle, remote(), pid(), ec)); + } + } + + if (t) + { + // make sure we keep all the stats! + if (!m_ignore_stats) + { + t->add_stats(statistics()); + + // report any partially received payload as redundant + boost::optional pbp = downloading_piece_progress(); + if (pbp + && pbp->bytes_downloaded > 0 + && pbp->bytes_downloaded < pbp->full_block_bytes) + { + t->add_redundant_bytes(pbp->bytes_downloaded, torrent::piece_closing); + } + } + + if (t->has_picker()) + { + piece_picker& picker = t->picker(); + + while (!m_download_queue.empty()) + { + pending_block& qe = m_download_queue.back(); + if (!qe.timed_out && !qe.not_wanted) + picker.abort_download(qe.block, peer_info_struct()); + m_outstanding_bytes -= t->to_req(qe.block).length; + if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; + m_download_queue.pop_back(); + } + while (!m_request_queue.empty()) + { + picker.abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + } + else + { + m_download_queue.clear(); + m_request_queue.clear(); + m_outstanding_bytes = 0; + } + m_queued_time_critical = 0; + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + t->remove_peer(this); + m_torrent.reset(); + } + else + { + TORRENT_ASSERT(m_download_queue.empty()); + TORRENT_ASSERT(m_request_queue.empty()); + } + +#if defined TORRENT_DEBUG && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS + // since this connection doesn't have a torrent reference + // no torrent should have a reference to this connection either + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer(this)); +#endif + + m_disconnecting = true; + error_code e; + + async_shutdown(*m_socket, m_socket); + + m_ses.close_connection(this, ec); + + // we should only disconnect while we still have + // at least one reference left to the connection + TORRENT_ASSERT(refcount() > 0); + } + + int peer_connection::get_upload_limit() const + { + return m_upload_limit; + } + + int peer_connection::get_download_limit() const + { + return m_download_limit; + } + + void peer_connection::set_upload_limit(int limit) + { + TORRENT_ASSERT(limit >= -1); + if (limit < 0) limit = 0; + if (limit < 10 && limit > 0) limit = 10; + m_upload_limit = limit; + m_bandwidth_channel[upload_channel].throttle(m_upload_limit); + } + + void peer_connection::set_download_limit(int limit) + { + TORRENT_ASSERT(limit >= -1); + if (limit < 0) limit = 0; + if (limit < 10 && limit > 0) limit = 10; + m_download_limit = limit; + m_bandwidth_channel[download_channel].throttle(m_download_limit); + } + + bool peer_connection::ignore_unchoke_slots() const + { + return m_ignore_unchoke_slots + || (m_ses.settings().ignore_limits_on_local_network + && on_local_network() + && m_ses.m_local_upload_channel.throttle() == 0); + } + + // defined in upnp.cpp + bool is_local(address const& a); + + bool peer_connection::on_local_network() const + { + if (libtorrent::is_local(m_remote.address()) + || is_loopback(m_remote.address())) return true; + return false; + } + + void peer_connection::get_peer_info(peer_info& p) const + { + TORRENT_ASSERT(!associated_torrent().expired()); + + ptime now = time_now(); + + p.download_rate_peak = m_download_rate_peak; + p.upload_rate_peak = m_upload_rate_peak; + p.rtt = m_rtt; + p.down_speed = statistics().download_rate(); + p.up_speed = statistics().upload_rate(); + p.payload_down_speed = statistics().download_payload_rate(); + p.payload_up_speed = statistics().upload_payload_rate(); + p.pid = pid(); + p.ip = remote(); + p.pending_disk_bytes = m_outstanding_writing_bytes; + p.send_quota = m_quota[upload_channel]; + p.receive_quota = m_quota[download_channel]; + p.num_pieces = m_num_pieces; + if (m_download_queue.empty()) p.request_timeout = -1; + else p.request_timeout = int(total_seconds(m_requested - now) + m_ses.settings().request_timeout + + m_timeout_extend); +#ifndef TORRENT_DISABLE_GEO_IP + p.inet_as_name = m_inet_as_name; +#endif + + p.download_queue_time = download_queue_time(); + p.queue_bytes = m_outstanding_bytes; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + p.country[0] = m_country[0]; + p.country[1] = m_country[1]; +#else + std::fill(p.country, p.country + 2, 0); +#endif + + p.total_download = statistics().total_payload_download(); + p.total_upload = statistics().total_payload_upload(); + + if (m_bandwidth_channel[upload_channel].throttle() == 0) + p.upload_limit = -1; + else + p.upload_limit = m_bandwidth_channel[upload_channel].throttle(); + + if (m_bandwidth_channel[download_channel].throttle() == 0) + p.download_limit = -1; + else + p.download_limit = m_bandwidth_channel[download_channel].throttle(); + + p.download_queue_length = int(download_queue().size() + m_request_queue.size()); + p.requests_in_buffer = int(m_requests_in_buffer.size() + m_request_queue.size()); + p.target_dl_queue_length = int(desired_queue_size()); + p.upload_queue_length = int(upload_queue().size()); + p.timed_out_requests = 0; + p.busy_requests = 0; + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + if (i->timed_out) ++p.timed_out_requests; + if (i->busy) ++p.busy_requests; + } + + if (boost::optional ret = downloading_piece_progress()) + { + p.downloading_piece_index = ret->piece_index; + p.downloading_block_index = ret->block_index; + p.downloading_progress = ret->bytes_downloaded; + p.downloading_total = ret->full_block_bytes; + } + else + { + p.downloading_piece_index = -1; + p.downloading_block_index = -1; + p.downloading_progress = 0; + p.downloading_total = 0; + } + + p.pieces = get_bitfield(); + p.last_request = now - m_last_request; + p.last_active = now - (std::max)(m_last_sent, m_last_receive); + + // this will set the flags so that we can update them later + p.flags = 0; + get_specific_peer_info(p); + + p.flags |= is_seed() ? peer_info::seed : 0; + p.flags |= m_snubbed ? peer_info::snubbed : 0; + p.flags |= m_upload_only ? peer_info::upload_only : 0; + p.flags |= m_endgame_mode ? peer_info::endgame_mode : 0; + p.flags |= m_holepunch_mode ? peer_info::holepunched : 0; + if (peer_info_struct()) + { + policy::peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi->in_use); + p.source = pi->source; + p.failcount = pi->failcount; + p.num_hashfails = pi->hashfails; + p.flags |= pi->on_parole ? peer_info::on_parole : 0; + p.flags |= pi->optimistically_unchoked ? peer_info::optimistic_unchoke : 0; +#ifndef TORRENT_DISABLE_GEO_IP + p.inet_as = pi->inet_as ? pi->inet_as->first : 0xffff; +#else + p.inet_as = 0xffff; +#endif + } + else + { + p.source = 0; + p.failcount = 0; + p.num_hashfails = 0; + p.inet_as = 0xffff; + } + + p.remote_dl_rate = m_remote_dl_rate; + p.send_buffer_size = m_send_buffer.capacity(); + p.used_send_buffer = m_send_buffer.size(); + p.receive_buffer_size = m_recv_buffer.capacity() + m_disk_recv_buffer_size; + p.used_receive_buffer = m_recv_pos; + p.write_state = m_channel_state[upload_channel]; + p.read_state = m_channel_state[download_channel]; + + // pieces may be empty if we don't have metadata yet + if (p.pieces.size() == 0) + { + p.progress = 0.f; + p.progress_ppm = 0; + } + else + { +#if TORRENT_NO_FPU + p.progress = 0.f; +#else + p.progress = (float)p.pieces.count() / (float)p.pieces.size(); +#endif + p.progress_ppm = boost::uint64_t(p.pieces.count()) * 1000000 / p.pieces.size(); + } + + p.estimated_reciprocation_rate = m_est_reciprocation_rate; + + error_code ec; + p.local_endpoint = get_socket()->local_endpoint(ec); + } + + // allocates a disk buffer of size 'disk_buffer_size' and replaces the + // end of the current receive buffer with it. i.e. the receive pos + // must be <= packet_size - disk_buffer_size + // the disk buffer can be accessed through release_disk_receive_buffer() + // when it is queried, the responsibility to free it is transferred + // to the caller + bool peer_connection::allocate_disk_receive_buffer(int disk_buffer_size) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_packet_size > 0); + TORRENT_ASSERT(m_recv_pos <= m_packet_size - disk_buffer_size); + TORRENT_ASSERT(!m_disk_recv_buffer); + TORRENT_ASSERT(disk_buffer_size <= 16 * 1024); + + if (disk_buffer_size == 0) return true; + + if (disk_buffer_size > 16 * 1024) + { + disconnect(errors::invalid_piece_size, 2); + return false; + } + + // first free the old buffer + m_disk_recv_buffer.reset(); + // then allocate a new one + + m_disk_recv_buffer.reset(m_ses.allocate_disk_buffer("receive buffer")); + if (!m_disk_recv_buffer) + { + disconnect(errors::no_memory); + return false; + } + m_disk_recv_buffer_size = disk_buffer_size; + return true; + } + + char* peer_connection::release_disk_receive_buffer() + { + m_disk_recv_buffer_size = 0; + return m_disk_recv_buffer.release(); + } + + // size = the packet size to remove from the receive buffer + // packet_size = the next packet size to receive in the buffer + void peer_connection::cut_receive_buffer(int size, int packet_size, int offset) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(packet_size > 0); + TORRENT_ASSERT(int(m_recv_buffer.size()) >= size); + TORRENT_ASSERT(int(m_recv_buffer.size()) >= m_recv_pos); + TORRENT_ASSERT(m_recv_pos >= size + offset); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(size >= 0); + + if (size > 0) + { + if (m_recv_pos - size - offset > 0) + std::memmove(&m_recv_buffer[0] + offset, &m_recv_buffer[0] + offset + size, m_recv_pos - size - offset); + m_recv_pos -= size; + + // defensive. If this actually happens, we would have triggered + // an assert already (if asserts are enabled). + if (m_recv_pos < 0) m_recv_pos = 0; + } + +#ifdef TORRENT_DEBUG + std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0); +#endif + + m_packet_size = packet_size; + } + + void peer_connection::superseed_piece(int replace_piece, int new_piece) + { + if (new_piece == -1) + { + if (m_superseed_piece[0] == -1) return; + m_superseed_piece[0] = -1; + m_superseed_piece[1] = -1; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** ending super seed mode"); +#endif + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i] || !t->have_piece(i)) continue; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d] (ending super seed)", i); +#endif + write_have(i); + } + + return; + } + + assert(!has_piece(new_piece)); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> HAVE [ piece: %d ] (super seed)", new_piece); +#endif + write_have(new_piece); + + if (replace_piece >= 0) + { + // move the piece we're replacing to the tail + if (m_superseed_piece[0] == replace_piece) + std::swap(m_superseed_piece[0], m_superseed_piece[1]); + } + + m_superseed_piece[1] = m_superseed_piece[0]; + m_superseed_piece[0] = new_piece; + } + + void peer_connection::max_out_request_queue(int s) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** MAX OUT QUEUE SIZE [ %d -> %d ]" + , m_max_out_request_queue, s); +#endif + m_max_out_request_queue = s; + } + + int peer_connection::max_out_request_queue() const + { + return m_max_out_request_queue; + } + + void peer_connection::update_desired_queue_size() + { + if (m_snubbed) + { + m_desired_queue_size = 1; + return; + } + + int download_rate = statistics().download_payload_rate(); + + // calculate the desired download queue size + const int queue_time = m_ses.settings().request_queue_time; + // (if the latency is more than this, the download will stall) + // so, the queue size is queue_time * down_rate / 16 kiB + // (16 kB is the size of each request) + // the minimum number of requests is 2 and the maximum is 48 + // the block size doesn't have to be 16. So we first query the + // torrent for it + boost::shared_ptr t = m_torrent.lock(); + const int block_size = t->block_size(); + + TORRENT_ASSERT(block_size > 0); + + m_desired_queue_size = queue_time * download_rate / block_size; + + if (m_desired_queue_size > m_max_out_request_queue) + m_desired_queue_size = m_max_out_request_queue; + if (m_desired_queue_size < min_request_queue) + m_desired_queue_size = min_request_queue; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** UPDATE_QUEUE_SIZE [ dqs: %d max: %d dl: %d qt: %d snubbed: %d ]" + , m_desired_queue_size, m_max_out_request_queue + , download_rate, queue_time, int(m_snubbed)); +#endif + } + + void peer_connection::second_tick(int tick_interval_ms) + { + ptime now = time_now(); + boost::intrusive_ptr me(self()); + + // the invariant check must be run before me is destructed + // in case the peer got disconnected + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + + // drain the IP overhead from the bandwidth limiters + if (m_ses.m_settings.rate_limit_ip_overhead) + { + int download_overhead = m_statistics.download_ip_overhead(); + int upload_overhead = m_statistics.upload_ip_overhead(); + m_bandwidth_channel[download_channel].use_quota(download_overhead); + m_bandwidth_channel[upload_channel].use_quota(upload_overhead); + + bandwidth_channel* upc = 0; + bandwidth_channel* downc = 0; + if (m_ignore_bandwidth_limits) + { + upc = &m_ses.m_local_upload_channel; + downc = &m_ses.m_local_download_channel; + } + else + { + upc = &m_ses.m_upload_channel; + downc = &m_ses.m_download_channel; + } + + int up_limit = m_bandwidth_channel[upload_channel].throttle(); + int down_limit = m_bandwidth_channel[download_channel].throttle(); + + if (t) + { + if (!m_ignore_bandwidth_limits) + { + t->m_bandwidth_channel[download_channel].use_quota(download_overhead); + t->m_bandwidth_channel[upload_channel].use_quota(upload_overhead); + } + + if (down_limit > 0 + && download_overhead >= down_limit + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && upload_overhead >= up_limit + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::upload_limit_too_low)); + } + } + downc->use_quota(download_overhead); + upc->use_quota(upload_overhead); + } + + if (!t || m_disconnecting) + { + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + disconnect(errors::torrent_aborted); + return; + } + + if (m_endgame_mode + && m_interesting + && m_download_queue.empty() + && m_request_queue.empty() + && total_seconds(now - m_last_request) >= 5) + { + // this happens when we're in strict end-game + // mode and the peer could not request any blocks + // because they were all taken but there were still + // unrequested blocks. Now, 5 seconds later, there + // might not be any unrequested blocks anymore, so + // we should try to pick another block to see + // if we can pick a busy one +#ifdef TORRENT_STATS + ++m_ses.m_end_game_piece_picks; +#endif + m_last_request = now; + request_a_block(*t, *this); + if (m_disconnecting) return; + send_block_requests(); + } + + if (t->super_seeding() + && !m_peer_interested + && m_became_uninterested + seconds(10) < now) + { + // maybe we need to try another piece, to see if the peer + // become interested in us then + superseed_piece(-1, t->get_piece_to_super_seed(m_have_piece)); + } + + on_tick(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->tick(); + } + if (is_disconnecting()) return; +#endif + + // if the peer hasn't said a thing for a certain + // time, it is considered to have timed out + time_duration d; + d = (std::min)(now - m_last_receive, now - m_last_sent); + + // if we can't read, it means we're blocked on the rate-limiter + // or the disk, not the peer itself. In this case, don't blame + // the peer and disconnect it + bool may_timeout = (m_channel_state[download_channel] & peer_info::bw_network) != 0; + + if (may_timeout && d > seconds(m_timeout) && !m_connecting + && can_disconnect(error_code(errors::timed_out_inactivity, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** LAST ACTIVITY [ %d seconds ago ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_inactivity); + return; + } + + // do not stall waiting for a handshake + if (may_timeout + && !m_connecting + && in_handshake() + && d > seconds(m_ses.settings().handshake_timeout)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** NO HANDSHAKE [ waited %d seconds ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_no_handshake); + return; + } + + // disconnect peers that we unchoked, but + // they didn't send a request within 20 seconds. + // but only if we're a seed + d = now - (std::max)(m_last_unchoke, m_last_incoming_request); + if (may_timeout + && !m_connecting + && m_requests.empty() + && m_reading_bytes == 0 + && !m_choked + && m_peer_interested + && t && t->is_upload_only() + && d > seconds(20) + && can_disconnect(error_code(errors::timed_out_no_request, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** NO REQUEST [ waited %d seconds ] ***", int(total_seconds(d))); +#endif + disconnect(errors::timed_out_no_request); + return; + } + + // if the peer hasn't become interested and we haven't + // become interested in the peer for 10 minutes, it + // has also timed out. + time_duration d1; + time_duration d2; + d1 = now - m_became_uninterested; + d2 = now - m_became_uninteresting; + time_duration time_limit = seconds( + m_ses.settings().inactivity_timeout); + + // don't bother disconnect peers we haven't been interested + // in (and that hasn't been interested in us) for a while + // unless we have used up all our connection slots + if (may_timeout + && !m_interesting + && !m_peer_interested + && d1 > time_limit + && d2 > time_limit + && (m_ses.num_connections() >= m_ses.settings().connections_limit + || (t && t->num_peers() >= t->max_connections())) + && can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** MUTUAL NO INTEREST [ t1: %d t2: %d ]" + , total_seconds(d1), total_seconds(d2)); +#endif + disconnect(errors::timed_out_no_interest); + return; + } + + if (may_timeout + && !m_download_queue.empty() + && m_quota[download_channel] > 0 + && now > m_requested + seconds(m_ses.settings().request_timeout + + m_timeout_extend)) + { + snub_peer(); + } + + // if we haven't sent something in too long, send a keep-alive + keep_alive(); + + m_ignore_bandwidth_limits = m_ses.settings().ignore_limits_on_local_network + && on_local_network(); + + m_statistics.second_tick(tick_interval_ms); + + if (m_statistics.upload_payload_rate() > m_upload_rate_peak) + { + m_upload_rate_peak = m_statistics.upload_payload_rate(); + } + if (m_statistics.download_payload_rate() > m_download_rate_peak) + { + m_download_rate_peak = m_statistics.download_payload_rate(); +#ifndef TORRENT_DISABLE_GEO_IP + if (peer_info_struct()) + { + std::pair* as_stats = peer_info_struct()->inet_as; + if (as_stats && as_stats->second < m_download_rate_peak) + as_stats->second = m_download_rate_peak; + } +#endif + } + if (is_disconnecting()) return; + + if (!t->ready_for_connections()) return; + + update_desired_queue_size(); + + if (m_desired_queue_size == m_max_out_request_queue + && t->alerts().should_post()) + { + t->alerts().post_alert(performance_alert(t->get_handle() + , performance_alert::outstanding_request_limit_reached)); + } + + int piece_timeout = m_ses.settings().piece_timeout; + int rate_limit = INT_MAX; + if (m_bandwidth_channel[download_channel].throttle() > 0) + rate_limit = (std::min)(m_bandwidth_channel[download_channel].throttle(), rate_limit); + if (t->bandwidth_throttle(download_channel) > 0) + rate_limit = (std::min)(t->bandwidth_throttle(download_channel) / t->num_peers(), rate_limit); + if (m_ses.m_download_channel.throttle() > 0) + rate_limit = (std::min)(m_ses.m_download_channel.throttle() + / m_ses.num_connections(), rate_limit); + + // rate_limit is an approximation of what this connection is + // allowed to download. If it is impossible to beat the piece + // timeout at this rate, adjust it to be realistic + + const int block_size = t->block_size(); + int rate_limit_timeout = rate_limit / block_size; + if (piece_timeout < rate_limit_timeout) piece_timeout = rate_limit_timeout; + + if (!m_download_queue.empty() + && m_quota[download_channel] > 0 + && now - m_last_piece > seconds(piece_timeout + m_timeout_extend)) + { + // this peer isn't sending the pieces we've + // requested (this has been observed by BitComet) + // in this case we'll clear our download queue and + // re-request the blocks. +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** PIECE_REQUEST TIMED OUT [ %d time: %d to: %d extend: %d ]" + , (int)m_download_queue.size(), total_seconds(now - m_last_piece) + , piece_timeout, m_timeout_extend); +#endif + + snub_peer(); + } + + // update once every minute + if (now - m_remote_dl_update >= seconds(60)) + { + boost::int64_t piece_size = t->torrent_file().piece_length(); + + if (m_remote_dl_rate > 0) + m_remote_dl_rate = int((m_remote_dl_rate * 2 / 3) + + ((boost::int64_t(m_remote_pieces_dled) * piece_size / 3) / 60)); + else + m_remote_dl_rate = int(boost::int64_t(m_remote_pieces_dled) + * piece_size / 60); + + m_remote_pieces_dled = 0; + m_remote_dl_update = now; + } + + fill_send_buffer(); + } + + void peer_connection::snub_peer() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + if (!m_snubbed) + { + m_snubbed = true; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(peer_snubbed_alert(t->get_handle() + , m_remote, m_peer_id)); + } + } + m_desired_queue_size = 1; + + if (on_parole()) + { + m_timeout_extend += m_ses.settings().request_timeout; + return; + } + if (!t->has_picker()) return; + piece_picker& picker = t->picker(); + + // first, if we have any unsent requests, just + // wipe those out + while (!m_request_queue.empty()) + { + t->picker().abort_download(m_request_queue.back().block, peer_info_struct()); + m_request_queue.pop_back(); + } + m_queued_time_critical = 0; + + TORRENT_ASSERT(!m_download_queue.empty()); + + // request a new block before removing the previous + // one, in order to prevent it from + // picking the same block again, stalling the + // same piece indefinitely. + m_desired_queue_size = 2; +#ifdef TORRENT_STATS + ++m_ses.m_snubbed_piece_picks; +#endif + request_a_block(*t, *this); + + // the block we just picked (potentially) + // hasn't been put in m_download_queue yet. + // it's in m_request_queue and will be sent + // once send_block_requests() is called. + + m_desired_queue_size = 1; + + // time out the last request eligible + // block in the queue + int i = m_download_queue.size() - 1; + for (; i >= 0; --i) + { + if (!m_download_queue[i].timed_out + && !m_download_queue[i].not_wanted) + break; + } + + if (i >= 0) + { + pending_block& qe = m_download_queue[i]; + piece_block r = qe.block; + + // only time out a request if it blocks the piece + // from being completed (i.e. no free blocks to + // request from it) + piece_picker::downloading_piece p; + picker.piece_info(qe.block.piece_index, p); + int free_blocks = picker.blocks_in_piece(qe.block.piece_index) + - p.finished - p.writing - p.requested; + if (free_blocks > 0) + { + m_timeout_extend += m_ses.settings().request_timeout; + return; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(block_timeout_alert(t->get_handle() + , remote(), pid(), qe.block.block_index, qe.block.piece_index)); + } + qe.timed_out = true; + picker.abort_download(r, peer_info_struct()); + } + + send_block_requests(); + } + + std::pair peer_connection::preferred_caching() const + { + int line_size = 0; + int expiry = 0; + if (m_ses.m_settings.guided_read_cache) + { + boost::shared_ptr t = m_torrent.lock(); + int upload_rate = m_statistics.upload_payload_rate(); + if (upload_rate == 0) upload_rate = 1; + + int num_uploads = m_ses.num_uploads(); + if (num_uploads == 0) num_uploads = 1; + + // assume half of the cache is write cache if we're downloading + // this torrent as well + int cache_size = m_ses.m_settings.cache_size / num_uploads; + if (!t->is_upload_only()) cache_size /= 2; + // cache_size is the amount of cache we have per peer. The + // cache line should not be greater than this + + // try to avoid locking caches for more than a couple of seconds + expiry = cache_size * 16 * 1024 / upload_rate; + if (expiry < 1) expiry = 1; + else if (expiry > 10) expiry = 10; + + line_size = cache_size; + } + return std::make_pair(line_size, expiry); + } + + void peer_connection::fill_send_buffer() + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + bool sent_a_piece = false; + boost::shared_ptr t = m_torrent.lock(); + if (!t || t->is_aborted()) return; + + // only add new piece-chunks if the send buffer is small enough + // otherwise there will be no end to how large it will be! + + boost::uint64_t upload_rate = int(m_statistics.upload_rate()); + + int buffer_size_watermark = int(upload_rate + * m_ses.settings().send_buffer_watermark_factor / 100); + + if (buffer_size_watermark < m_ses.settings().send_buffer_low_watermark) + { + buffer_size_watermark = m_ses.settings().send_buffer_low_watermark; + } + else if (buffer_size_watermark > m_ses.settings().send_buffer_watermark) + { + buffer_size_watermark = m_ses.settings().send_buffer_watermark; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> SEND_BUFFER_WATERMARK [ %d max: %d min: %d factor: %d ]" + , buffer_size_watermark, m_ses.settings().send_buffer_watermark + , m_ses.settings().send_buffer_low_watermark, m_ses.settings().send_buffer_watermark_factor); +#endif + + while (!m_requests.empty() + && (send_buffer_size() + m_reading_bytes < buffer_size_watermark)) + { + TORRENT_ASSERT(t->ready_for_connections()); + peer_request& r = m_requests.front(); + + TORRENT_ASSERT(r.piece >= 0); + TORRENT_ASSERT(r.piece < (int)m_have_piece.size()); + TORRENT_ASSERT(t->have_piece(r.piece)); + TORRENT_ASSERT(r.start + r.length <= t->torrent_file().piece_size(r.piece)); + TORRENT_ASSERT(r.length > 0 && r.start >= 0); + + std::pair cache = preferred_caching(); + + if (!t->seed_mode() || t->verified_piece(r.piece)) + { + t->filesystem().async_read(r, boost::bind(&peer_connection::on_disk_read_complete + , self(), _1, _2, r), cache.first, cache.second); + } + else + { + // this means we're in seed mode and we haven't yet + // verified this piece (r.piece) + t->filesystem().async_read_and_hash(r, boost::bind(&peer_connection::on_disk_read_complete + , self(), _1, _2, r), cache.second); + t->verified(r.piece); + } + + m_reading_bytes += r.length; + + m_requests.erase(m_requests.begin()); + sent_a_piece = true; + } + + if (t->share_mode() && sent_a_piece) + t->recalc_share_mode(); + } + + void peer_connection::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r) + { + // flush send buffer at the end of this scope + // TODO: peers should really be corked/uncorked outside of + // all completed disk operations + cork _c(*this); + +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_disk_read_counter]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + m_reading_bytes -= r.length; + + disk_buffer_holder buffer(m_ses, j.buffer); +#if TORRENT_DISK_STATS + if (j.buffer) m_ses.m_disk_thread.rename_buffer(j.buffer, "received send buffer"); +#endif + + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + disconnect(j.error); + return; + } + + if (ret != r.length) + { + if (ret == -3) + { +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> REJECT_PIECE [ piece: %d s: %d l: %d ]" + , r.piece , r.start , r.length); +#endif + write_reject_request(r); + if (t->seed_mode()) t->leave_seed_mode(false); + } + else + { + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); + } + return; + } + + if (t) + { + if (t->seed_mode() && t->all_verified()) + t->leave_seed_mode(true); + } + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("==> PIECE [ piece: %d s: %d l: %d ]" + , r.piece, r.start, r.length); +#endif + +#if TORRENT_DISK_STATS + if (j.buffer) m_ses.m_disk_thread.rename_buffer(j.buffer, "dispatched send buffer"); +#endif + write_piece(r, buffer); + } + + void peer_connection::assign_bandwidth(int channel, int amount) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("%s ASSIGN BANDWIDHT [ bytes: %d ]" + , channel == upload_channel ? ">>>" : "<<<", amount); +#endif + + TORRENT_ASSERT(amount > 0 || is_disconnecting()); + m_quota[channel] += amount; + TORRENT_ASSERT(m_channel_state[channel] & peer_info::bw_limit); + m_channel_state[channel] &= ~peer_info::bw_limit; + +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + + if (is_disconnecting()) return; + if (channel == upload_channel) + { + setup_send(); + } + else if (channel == download_channel) + { + setup_receive(); + } + } + + int peer_connection::request_upload_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 + , bandwidth_channel* bwc3 + , bandwidth_channel* bwc4) + { + // we can only have one outstanding bandwidth request at a time + if (m_channel_state[upload_channel] & peer_info::bw_limit) return 0; + + int bytes = (std::max)(m_send_buffer.size() + , int(boost::int64_t(m_statistics.upload_rate()) * 2 + * m_ses.m_settings.tick_interval / 1000)); + + // we already have quota for the bytes we want to send + if (m_quota[upload_channel] >= bytes) return 0; + + // deduct the bytes we already have quota for + bytes -= m_quota[upload_channel]; + + shared_ptr t = m_torrent.lock(); + int priority; + if (t && m_ses.m_settings.choking_algorithm == session_settings::bittyrant_choker + && !t->upload_mode() && !t->is_upload_only()) + { + // when we use the bittyrant choker, the priority of a peer + // is decided based on the estimated reciprocation rate and + // the share it represents of the total upload rate capacity + // the torrent priority is taken into account when unchoking peers + int upload_capacity = m_ses.settings().upload_rate_limit; + if (upload_capacity == 0) + { + // we don't know at what rate we can upload. If we have a + // measurement of the peak, use that + 10kB/s, otherwise + // assume 20 kB/s + upload_capacity = (std::max)(20000, m_ses.m_peak_up_rate + 10000); + } + int estimated_reciprocation_rate = m_est_reciprocation_rate; + // we cannot send faster than our upload rate anyway + if (estimated_reciprocation_rate < upload_capacity) + estimated_reciprocation_rate = upload_capacity; + + priority = (boost::uint64_t(estimated_reciprocation_rate) << 14) / upload_capacity; + if (priority > 0xffff) priority = 0xffff; + } + else + { + priority = 1 + is_interesting() * 2 + m_requests_in_buffer.size(); + if (priority > 255) priority = 255; + priority += t ? t->priority() << 8 : 0; + } + TORRENT_ASSERT(priority <= 0xffff); + + // peers that we are not interested in are non-prioritized +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> REQUEST_BANDWIDTH [ upload: %d prio: %d " + "channels: %p %p %p %p limits: %d %d %d %d ignore: %d ]" + , int(m_send_buffer.size()), priority + , bwc1, bwc2, bwc3, bwc4 + , (bwc1?bwc1->throttle():0) + , (bwc2?bwc2->throttle():0) + , (bwc3?bwc3->throttle():0) + , (bwc4?bwc4->throttle():0) + , m_ignore_bandwidth_limits); +#endif + + int ret = m_ses.m_upload_rate.request_bandwidth(self() + , bytes + , priority + , bwc1, bwc2, bwc3, bwc4); + + if (ret == 0) + { + m_channel_state[upload_channel] |= peer_info::bw_limit; + } + else + { + m_quota[upload_channel] += ret; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> ASSIGN BANDWIDTH [ bytes: %d ]", ret); +#endif + } + return ret; + } + + int peer_connection::request_download_bandwidth( + bandwidth_channel* bwc1 + , bandwidth_channel* bwc2 + , bandwidth_channel* bwc3 + , bandwidth_channel* bwc4) + { + INVARIANT_CHECK; + // we can only have one outstanding bandwidth request at a time + if (m_channel_state[download_channel] & peer_info::bw_limit) return 0; + + int bytes = (std::max)((std::max)(m_outstanding_bytes, m_packet_size - m_recv_pos) + 30 + , int(boost::int64_t(m_statistics.download_rate()) * 2 + * m_ses.m_settings.tick_interval / 1000)); + + // we already have enough quota + if (m_quota[download_channel] >= bytes) return 0; + + // deduct the bytes we already have quota for + bytes -= m_quota[download_channel]; + + shared_ptr t = m_torrent.lock(); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< REQUEST_BANDWIDTH [ download: %d prio: %d " + "channels: %p %p %p %p limits: %d %d %d %d ignore: %d ]" + , int(m_download_queue.size() * 16 * 1024 + 30), m_priority + , bwc1, bwc2, bwc3, bwc4 + , (bwc1?bwc1->throttle():0) + , (bwc2?bwc2->throttle():0) + , (bwc3?bwc3->throttle():0) + , (bwc4?bwc4->throttle():0) + , m_ignore_bandwidth_limits); +#endif + + TORRENT_ASSERT(m_priority <= 255); + int priority = m_priority + (t ? (t->priority() << 8) : 0); + + TORRENT_ASSERT(m_outstanding_bytes >= 0); + TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_limit) == 0); + + int ret = m_ses.m_download_rate.request_bandwidth(self() + , bytes, priority, bwc1, bwc2, bwc3, bwc4); + if (ret == 0) + { + m_channel_state[download_channel] |= peer_info::bw_limit; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ASSIGN BANDWIDTH [ bytes: %d ]", ret); +#endif + m_quota[download_channel] += ret; + } + return ret; + } + + void peer_connection::uncork_socket() + { + if (!m_corked) return; + m_corked = false; + setup_send(); + } + + void peer_connection::setup_send() + { + if (m_disconnecting) return; + + shared_ptr t = m_torrent.lock(); + + // we may want to request more quota at this point + bool utp = m_socket->get() != 0; + bool ignore_limits = m_ignore_bandwidth_limits + || (!m_ses.m_settings.rate_limit_utp && utp); + if (!ignore_limits) + { + // in this case, we have data to send, but no + // bandwidth. So, we simply request bandwidth + // from the bandwidth manager + request_upload_bandwidth( + &m_ses.m_upload_channel + , t ? &t->m_bandwidth_channel[upload_channel] : 0 + , &m_bandwidth_channel[upload_channel] + , !utp ? &m_ses.m_tcp_upload_channel : 0); + } + else + { + // in this case, we're a local peer, and the settings + // are set to ignore rate limits for local peers. So, + // instead we rate limit ourself against the special + // global bandwidth channel for local peers, which defaults + // to unthrottled + request_upload_bandwidth(&m_ses.m_local_upload_channel + , &m_bandwidth_channel[upload_channel]); + } + + if (m_channel_state[upload_channel] & peer_info::bw_network) return; + + if (m_quota[upload_channel] == 0 + && !m_send_buffer.empty() + && !m_connecting) + { + return; + } + + int quota_left = m_quota[upload_channel]; + + if (m_send_buffer.empty() + && m_reading_bytes > 0 + && quota_left > 0) + { + if ((m_channel_state[upload_channel] & peer_info::bw_disk) == 0) + m_ses.inc_disk_queue(upload_channel); + m_channel_state[upload_channel] |= peer_info::bw_disk; + + if (!m_connecting + && !m_requests.empty() + && m_reading_bytes > m_ses.settings().send_buffer_watermark - 0x4000) + { + // we're stalled on the disk. We want to write and we can write + // but our send buffer is empty, waiting to be refilled from the disk + // this either means the disk is slower than the network connection + // or that our send buffer watermark is too small, because we can + // send it all before the disk gets back to us. That's why we only + // trigger this if we've also filled the allowed send buffer. The + // first request would not fill it all the way up because of the + // upload rate being virtually 0. If m_requests is empty, it doesn't + // matter anyway, because we don't have any more requests from the + // peer to hang on to the disk + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(performance_alert(t->get_handle() + , performance_alert::send_buffer_watermark_too_low)); + } + } + } + else + { + if (m_channel_state[upload_channel] & peer_info::bw_disk) + m_ses.dec_disk_queue(upload_channel); + m_channel_state[upload_channel] &= ~peer_info::bw_disk; + } + + if (!can_write()) + { +#ifdef TORRENT_VERBOSE_LOGGING + if (m_send_buffer.empty()) + { + peer_log(">>> SEND BUFFER DEPLETED [" + " quota: %d ignore: %s buf: %d connecting: %s disconnecting: %s pending_disk: %d ]" + , m_quota[upload_channel], m_ignore_bandwidth_limits?"yes":"no" + , int(m_send_buffer.size()), m_connecting?"yes":"no" + , m_disconnecting?"yes":"no", m_reading_bytes); + } + else + { + peer_log(">>> CANNOT WRITE [" + " quota: %d ignore: %s buf: %d connecting: %s disconnecting: %s pending_disk: %d ]" + , m_quota[upload_channel], m_ignore_bandwidth_limits?"yes":"no" + , int(m_send_buffer.size()), m_connecting?"yes":"no" + , m_disconnecting?"yes":"no", m_reading_bytes); + } +#endif + return; + } + + // send the actual buffer + int amount_to_send = m_send_buffer.size(); + if (amount_to_send > quota_left) + amount_to_send = quota_left; + + TORRENT_ASSERT(amount_to_send > 0); + + if (m_corked) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> CORKED WRITE [ bytes: %d ]", amount_to_send); +#endif + return; + } + + TORRENT_ASSERT((m_channel_state[upload_channel] & peer_info::bw_network) == 0); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> ASYNC_WRITE [ bytes: %d ]", amount_to_send); +#endif + std::list const& vec = m_send_buffer.build_iovec(amount_to_send); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_send_data"); +#endif + m_socket->async_write_some( + vec, make_write_handler(boost::bind( + &peer_connection::on_send_data, self(), _1, _2))); + + m_channel_state[upload_channel] |= peer_info::bw_network; + } + + void peer_connection::on_disk() + { + if ((m_channel_state[download_channel] & peer_info::bw_disk) == 0) return; + boost::intrusive_ptr me(this); + + m_ses.dec_disk_queue(download_channel); + m_channel_state[download_channel] &= ~peer_info::bw_disk; + setup_receive(read_async); + } + + void peer_connection::setup_receive(sync_t sync) + { + INVARIANT_CHECK; + + if (m_disconnecting) return; + + shared_ptr t = m_torrent.lock(); + + // we may want to request more quota at this point + bool utp = m_socket->get() != 0; + bool ignore_limits = m_ignore_bandwidth_limits + || (!m_ses.m_settings.rate_limit_utp && utp); + if (!ignore_limits) + { + // in this case, we have outstanding data to + // receive, but no bandwidth quota. So, we simply + // request bandwidth from the bandwidth manager + request_download_bandwidth( + &m_ses.m_download_channel + , t ? &t->m_bandwidth_channel[download_channel] : 0 + , &m_bandwidth_channel[download_channel] + , !utp ? &m_ses.m_tcp_download_channel : 0); + } + else + { + // in this case, we're a local peer, and the settings + // are set to ignore rate limits for local peers. So, + // instead we rate limit ourself against the special + // global bandwidth channel for local peers, which defaults + // to unthrottled + request_download_bandwidth(&m_ses.m_local_download_channel + , &m_bandwidth_channel[download_channel]); + } + + if (m_channel_state[download_channel] & peer_info::bw_network) return; + + if (m_quota[download_channel] == 0 + && !m_connecting) + { + return; + } + + if (!can_read(&m_channel_state[download_channel])) + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< CANNOT READ [ quota: %d ignore: %s " + "can-write-to-disk: %s queue-limit: %d disconnecting: %s ]" + , m_quota[download_channel] + , (m_ignore_bandwidth_limits?"yes":"no") + , (m_ses.can_write_to_disk()?"yes":"no") + , m_ses.settings().max_queued_disk_bytes + , (m_disconnecting?"yes":"no")); +#endif + // if we block reading, waiting for the disk, we will wake up + // by the disk_io_thread posting a message every time it drops + // from being at or exceeding the limit down to below the limit + return; + } + error_code ec; + try_read(read_async, ec); + } + + size_t peer_connection::try_read(sync_t s, error_code& ec) + { + TORRENT_ASSERT(m_packet_size > 0); + int max_receive = m_packet_size - m_recv_pos; + TORRENT_ASSERT(max_receive >= 0); + + if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; + if (m_soft_packet_size && max_receive > m_soft_packet_size - m_recv_pos) + max_receive = m_soft_packet_size - m_recv_pos; + int quota_left = m_quota[download_channel]; + if (max_receive > quota_left) + max_receive = quota_left; + + if (max_receive == 0) + { + ec = asio::error::would_block; + return 0; + } + + TORRENT_ASSERT(m_recv_pos >= 0); + TORRENT_ASSERT(m_packet_size > 0); + + if (!can_read()) + { + ec = asio::error::would_block; + return 0; + } + + int regular_buffer_size = m_packet_size - m_disk_recv_buffer_size; + + if (int(m_recv_buffer.size()) < regular_buffer_size) + m_recv_buffer.resize(round_up8(regular_buffer_size)); + + boost::array vec; + int num_bufs = 0; + if (!m_disk_recv_buffer || regular_buffer_size >= m_recv_pos + max_receive) + { + // only receive into regular buffer + TORRENT_ASSERT(m_recv_pos + max_receive <= int(m_recv_buffer.size())); + vec[0] = asio::buffer(&m_recv_buffer[m_recv_pos], max_receive); + num_bufs = 1; + } + else if (m_recv_pos >= regular_buffer_size) + { + // only receive into disk buffer + TORRENT_ASSERT(m_recv_pos - regular_buffer_size >= 0); + TORRENT_ASSERT(m_recv_pos - regular_buffer_size + max_receive <= m_disk_recv_buffer_size); + vec[0] = asio::buffer(m_disk_recv_buffer.get() + m_recv_pos - regular_buffer_size, max_receive); + num_bufs = 1; + } + else + { + // receive into both regular and disk buffer + TORRENT_ASSERT(max_receive + m_recv_pos > regular_buffer_size); + TORRENT_ASSERT(m_recv_pos < regular_buffer_size); + TORRENT_ASSERT(max_receive - regular_buffer_size + + m_recv_pos <= m_disk_recv_buffer_size); + + vec[0] = asio::buffer(&m_recv_buffer[m_recv_pos] + , regular_buffer_size - m_recv_pos); + vec[1] = asio::buffer(m_disk_recv_buffer.get() + , max_receive - regular_buffer_size + m_recv_pos); + num_bufs = 2; + } + + if (s == read_async) + { + TORRENT_ASSERT((m_channel_state[download_channel] & peer_info::bw_network) == 0); + m_channel_state[download_channel] |= peer_info::bw_network; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ASYNC_READ [ max: %d bytes ]", max_receive); +#endif + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_receive_data"); +#endif + if (num_bufs == 1) + { + m_socket->async_read_some( + asio::mutable_buffers_1(vec[0]), make_read_handler( + boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); + } + else + { + m_socket->async_read_some( + vec, make_read_handler( + boost::bind(&peer_connection::on_receive_data, self(), _1, _2))); + } + return 0; + } + + size_t ret = 0; + if (num_bufs == 1) + { + ret = m_socket->read_some(asio::mutable_buffers_1(vec[0]), ec); + } + else + { + ret = m_socket->read_some(vec, ec); + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< SYNC_READ [ max: %d ret: %d e: %s ]", max_receive, ret, ec ? ec.message().c_str() : ""); +#endif + return ret; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + // returns the last 'bytes' from the receive buffer + std::pair peer_connection::wr_recv_buffers(int bytes) + { + TORRENT_ASSERT(bytes <= m_recv_pos); + + std::pair vec; + int regular_buffer_size = m_packet_size - m_disk_recv_buffer_size; + TORRENT_ASSERT(regular_buffer_size >= 0); + if (!m_disk_recv_buffer || regular_buffer_size >= m_recv_pos) + { + vec.first = buffer::interval(&m_recv_buffer[0] + + m_recv_pos - bytes, &m_recv_buffer[0] + m_recv_pos); + vec.second = buffer::interval(0,0); + } + else if (m_recv_pos - bytes >= regular_buffer_size) + { + vec.first = buffer::interval(m_disk_recv_buffer.get() + m_recv_pos + - regular_buffer_size - bytes, m_disk_recv_buffer.get() + m_recv_pos + - regular_buffer_size); + vec.second = buffer::interval(0,0); + } + else + { + TORRENT_ASSERT(m_recv_pos - bytes < regular_buffer_size); + TORRENT_ASSERT(m_recv_pos > regular_buffer_size); + vec.first = buffer::interval(&m_recv_buffer[0] + m_recv_pos - bytes + , &m_recv_buffer[0] + regular_buffer_size); + vec.second = buffer::interval(m_disk_recv_buffer.get() + , m_disk_recv_buffer.get() + m_recv_pos - regular_buffer_size); + } + TORRENT_ASSERT(vec.first.left() + vec.second.left() == bytes); + return vec; + } +#endif + + void peer_connection::reset_recv_buffer(int packet_size) + { + TORRENT_ASSERT(packet_size > 0); + if (m_recv_pos > m_packet_size) + { + cut_receive_buffer(m_packet_size, packet_size); + return; + } + m_recv_pos = 0; + m_packet_size = packet_size; + } + + void nop(char*) {} + + void peer_connection::append_const_send_buffer(char const* buffer, int size) + { + m_send_buffer.append_buffer((char*)buffer, size, size, &nop); +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " append_const_send_buffer: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + } + + void peer_connection::send_buffer(char const* buf, int size, int flags + , void (*fun)(char*, int, void*), void* userdata) + { + if (flags == message_type_request) + m_requests_in_buffer.push_back(m_send_buffer.size() + size); + + int free_space = m_send_buffer.space_in_last_buffer(); + if (free_space > size) free_space = size; + if (free_space > 0) + { + char* dst = m_send_buffer.append(buf, free_space); + TORRENT_ASSERT(dst != 0); + if (fun) fun(dst, free_space, userdata); + size -= free_space; + buf += free_space; +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer: " + << free_space << std::endl; + m_ses.log_buffer_usage(); +#endif + } + if (size <= 0) return; + +#if defined TORRENT_STATS && defined TORRENT_DISK_STATS + m_ses.m_buffer_usage_logger << log_time() << " send_buffer_alloc: " << size << std::endl; + m_ses.log_buffer_usage(); +#endif + int i = 0; + while (size > 0) + { + char* chain_buf = m_ses.allocate_buffer(); + if (chain_buf == 0) + { + disconnect(errors::no_memory); + return; + } + + int buf_size = (std::min)(int(aux::session_impl::send_buffer_size), size); + memcpy(chain_buf, buf, buf_size); + if (fun) fun(chain_buf, buf_size, userdata); + buf += buf_size; + size -= buf_size; + m_send_buffer.append_buffer(chain_buf, aux::session_impl::send_buffer_size, buf_size + , boost::bind(&session_impl::free_buffer, boost::ref(m_ses), _1)); + ++i; + } + setup_send(); + } + + template + struct set_to_zero + { + set_to_zero(T& v, bool cond): m_val(v), m_cond(cond) {} + void fire() { if (!m_cond) return; m_cond = false; m_val = 0; } + ~set_to_zero() { if (m_cond) m_val = 0; } + private: + T& m_val; + bool m_cond; + }; + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void peer_connection::on_receive_data(const error_code& error + , std::size_t bytes_transferred) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_read_counter]; + int size = 8; + int index = 0; + while (bytes_transferred > size + 13) { size <<= 1; ++index; } + int num_max = sizeof(m_ses.m_recv_buffer_sizes)/sizeof(m_ses.m_recv_buffer_sizes[0]); + if (index >= num_max) index = num_max - 1; + ++m_ses.m_recv_buffer_sizes[index]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + + // keep ourselves alive in until this function exits in + // case we disconnect + // this needs to be created before the invariant check, + // to keep the object alive through the exit check + boost::intrusive_ptr me(self()); + + // flush the send buffer at the end of this function + cork _c(*this); + + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< ON_RECEIVE_DATA [ bytes: %d error: %s ]" + , bytes_transferred, error.message().c_str()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_receive_data"); +#endif + + // leave this bit set until we're done looping, reading from the socket. + // that way we don't trigger any async read calls until the end of this + // function. + TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); + + int bytes_in_loop = bytes_transferred; + + if (m_extension_outstanding_bytes > 0) + m_extension_outstanding_bytes -= (std::min)(m_extension_outstanding_bytes, int(bytes_transferred)); + + if (error) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("*** ERROR [ in peer_connection::on_receive_data error: %s ]" + , error.message().c_str()); +#endif + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + on_receive(error, bytes_transferred); + disconnect(error); + return; + } + + int num_loops = 0; + do + { + TORRENT_ASSERT(int(m_recv_pos + bytes_transferred) <= m_packet_size); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<<< read %d bytes", int(bytes_transferred)); +#endif + // correct the dl quota usage, if not all of the buffer was actually read + TORRENT_ASSERT(int(bytes_transferred) <= m_quota[download_channel]); + m_quota[download_channel] -= bytes_transferred; + + if (m_disconnecting) + { + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + return; + } + + TORRENT_ASSERT(m_packet_size > 0); + TORRENT_ASSERT(bytes_transferred > 0); + + m_last_receive = time_now(); + m_recv_pos += bytes_transferred; + TORRENT_ASSERT(m_recv_pos <= int(m_recv_buffer.size() + + m_disk_recv_buffer_size)); + +#if TORRENT_USE_ASSERTS + size_type cur_payload_dl = m_statistics.last_payload_downloaded(); + size_type cur_protocol_dl = m_statistics.last_protocol_downloaded(); +#endif + { + INVARIANT_CHECK; + on_receive(error, bytes_transferred); + } +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0); + size_type stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl + + m_statistics.last_protocol_downloaded() - cur_protocol_dl; + TORRENT_ASSERT(stats_diff == int(bytes_transferred)); +#endif + if (m_disconnecting) return; + + TORRENT_ASSERT(m_packet_size > 0); + + if (m_peer_choked + && m_recv_pos == 0 + && (m_recv_buffer.capacity() - m_packet_size) > 128) + { + // round up to an even 8 bytes since that's the RC4 blocksize + buffer(round_up8(m_packet_size)).swap(m_recv_buffer); + } + + if (m_recv_pos >= m_soft_packet_size) m_soft_packet_size = 0; + + if (num_loops > 20) break; + + error_code ec; + bytes_transferred = try_read(read_sync, ec); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + if (ec && ec != asio::error::would_block) + { + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + disconnect(ec); + return; + } + if (ec == asio::error::would_block) break; + bytes_in_loop += bytes_transferred; + ++num_loops; + } + while (bytes_transferred > 0); + + if (is_seed()) + { + boost::shared_ptr t = m_torrent.lock(); + if (t) t->seen_complete(); + } + + m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); + + // allow reading from the socket again + TORRENT_ASSERT(m_channel_state[download_channel] & peer_info::bw_network); + m_channel_state[download_channel] &= ~peer_info::bw_network; + + setup_receive(read_async); + } + + bool peer_connection::can_write() const + { + // if we have requests or pending data to be sent or announcements to be made + // we want to send data + return !m_send_buffer.empty() + && m_quota[upload_channel] > 0 + && !m_connecting; + } + + bool peer_connection::can_read(boost::uint8_t* state) const + { + boost::shared_ptr t = m_torrent.lock(); + + bool bw_limit = m_quota[download_channel] > 0; + + if (!bw_limit) return false; + + bool disk = m_ses.settings().max_queued_disk_bytes == 0 + || m_ses.can_write_to_disk() + // don't block this peer because of disk saturation + // if we're not downloading any pieces from it + || m_outstanding_bytes == 0; + + if (!disk) + { + if (state) + { + if ((*state & peer_info::bw_disk) == 0) + m_ses.inc_disk_queue(download_channel); + *state |= peer_info::bw_disk; + } + return false; + } + + return !m_connecting && !m_disconnecting; + } + + void peer_connection::on_connect(int ticket) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if TORRENT_USE_ASSERTS + // in case we disconnect here, we need to + // keep the connection alive until the + // exit invariant check is run + boost::intrusive_ptr me(self()); +#endif + INVARIANT_CHECK; + + error_code ec; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << time_now_string() << " ON_CONNECT: " << print_endpoint(m_remote) << "\n"; +#endif + + if (ticket == -1) + { + disconnect(asio::error::operation_aborted); + return; + } + + m_connection_ticket = ticket; + boost::shared_ptr t = m_torrent.lock(); + + m_queued = false; + + if (!t) + { + TORRENT_ASSERT(!m_connecting); + disconnect(errors::torrent_aborted); + return; + } + + TORRENT_ASSERT(m_connecting); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> OPEN [ protocol: %s ]", (m_remote.address().is_v4()?"IPv4":"IPv6")); +#endif + m_socket->open(m_remote.protocol(), ec); + if (ec) + { + disconnect(ec); + return; + } + + tcp::endpoint bind_interface = t->get_interface(); + + std::pair const& out_ports = m_ses.settings().outgoing_ports; + if (out_ports.first > 0 && out_ports.second >= out_ports.first) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> SET_REUSE_ADDRESS"); +#endif + m_socket->set_option(socket_acceptor::reuse_address(true), ec); + // ignore errors because the underlying socket may not + // be opened yet. This happens when we're routing through + // a proxy. In that case, we don't yet know the address of + // the proxy server, and more importantly, we don't know + // the address family of its address. This means we can't + // open the socket yet. The socks abstraction layer defers + // opening it. + ec.clear(); + bind_interface.port(m_ses.next_port()); + } + + // if we're not binding to a specific interface, bind + // to the same protocol family as the target endpoint + if (is_any(bind_interface.address())) + { +#if TORRENT_USE_IPV6 + if (m_remote.address().is_v6()) + bind_interface.address(address_v6::any()); + else +#endif + bind_interface.address(address_v4::any()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log(">>> BIND [ ep: %s ]", print_endpoint(bind_interface).c_str()); +#endif + m_socket->bind(bind_interface, ec); + if (ec) + { + disconnect(ec); + return; + } +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> ASYNC_CONNECT [ dst: %s ]", print_endpoint(m_remote).c_str()); +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("peer_connection::on_connection_complete"); +#endif + m_socket->async_connect(m_remote + , boost::bind(&peer_connection::on_connection_complete, self(), _1)); + m_connect = time_now_hires(); + m_statistics.sent_syn(m_remote.address().is_v6()); + + if (t->alerts().should_post()) + { + t->alerts().post_alert(peer_connect_alert( + t->get_handle(), remote(), pid(), m_socket->type())); + } +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** LOCAL ENDPOINT[ e: %s ]", print_endpoint(m_socket->local_endpoint(ec)).c_str()); +#endif + } + + void peer_connection::on_connection_complete(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_connection_complete"); +#endif + ptime completed = time_now_hires(); + + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + m_rtt = boost::uint16_t(total_milliseconds(completed - m_connect)); + +#ifdef TORRENT_USE_OPENSSL + // add this RTT to the PRNG seed, to add more unpredictability + boost::uint64_t now = total_microseconds(completed - m_connect); + // assume 12 bits of entropy (i.e. about 8 milliseconds) + RAND_add(&now, 8, 1.5); +#endif + + if (m_disconnecting) return; + + error_code ec; + if (e) + { + connect_failed(e); + return; + } + + // if t is NULL, we better not be connecting, since + // we can't decrement the connecting counter + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t || !m_connecting); + if (m_connecting && t) + { + t->dec_num_connecting(); + m_connecting = false; + } + if (m_connection_ticket != -1) + { + if (m_ses.m_half_open.done(m_connection_ticket)) + m_connection_ticket = -1; + } + + if (m_disconnecting) return; + m_last_receive = time_now(); + + if (is_utp(*m_socket) && m_peer_info) + { + m_peer_info->confirmed_supports_utp = true; + m_peer_info->supports_utp = false; + } + + // this means the connection just succeeded + + m_statistics.received_synack(m_remote.address().is_v6()); + + TORRENT_ASSERT(m_socket); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> COMPLETED [ ep: %s rtt: %d ]", print_endpoint(m_remote).c_str(), m_rtt); +#endif + + // set the socket to non-blocking, so that we can + // read the entire buffer on each read event we get + tcp::socket::non_blocking_io ioc(true); +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** SET NON-BLOCKING"); +#endif + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec); + return; + } + + if (m_remote == m_socket->local_endpoint(ec)) + { + // if the remote endpoint is the same as the local endpoint, we're connected + // to ourselves + if (m_peer_info && t) t->get_policy().ban_peer(m_peer_info); + disconnect(errors::self_connection, 1); + return; + } + + if (m_remote.address().is_v4() && m_ses.settings().peer_tos != 0) + { + error_code ec; + m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); +#if defined TORRENT_VERBOSE_LOGGING + peer_log(">>> SET_TOS[ tos: %d e: %s ]", m_ses.settings().peer_tos, ec.message().c_str()); +#endif + } +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + else if (m_remote.address().is_v6() && m_ses.settings().peer_tos != 0) + { + m_socket->set_option(traffic_class(m_ses.settings().peer_tos), ec); + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->on_connected(); + } +#endif + + on_connected(); + setup_send(); + setup_receive(); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + void peer_connection::on_send_data(error_code const& error + , std::size_t bytes_transferred) + { +#ifdef TORRENT_STATS + ++m_ses.m_num_messages[aux::session_impl::on_write_counter]; + int size = 8; + int index = 0; + while (bytes_transferred > size + 13) { size <<= 1; ++index; } + int num_max = sizeof(m_ses.m_send_buffer_sizes)/sizeof(m_ses.m_send_buffer_sizes[0]); + if (index >= num_max) index = num_max - 1; + ++m_ses.m_send_buffer_sizes[index]; +#endif + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if defined TORRENT_VERBOSE_LOGGING + peer_log("*** ON_SEND_DATA [ bytes: %d error: %s ]" + , int(bytes_transferred), error.message().c_str()); +#endif + + INVARIANT_CHECK; + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("peer_connection::on_send_data"); +#endif + // keep ourselves alive in until this function exits in + // case we disconnect + boost::intrusive_ptr me(self()); + + TORRENT_ASSERT(m_channel_state[upload_channel] & peer_info::bw_network); + + m_send_buffer.pop_front(bytes_transferred); + + for (std::vector::iterator i = m_requests_in_buffer.begin() + , end(m_requests_in_buffer.end()); i != end; ++i) + *i -= bytes_transferred; + + while (!m_requests_in_buffer.empty() + && m_requests_in_buffer.front() <= 0) + m_requests_in_buffer.erase(m_requests_in_buffer.begin()); + + m_channel_state[upload_channel] &= ~peer_info::bw_network; + + TORRENT_ASSERT(int(bytes_transferred) <= m_quota[upload_channel]); + m_quota[upload_channel] -= bytes_transferred; + + m_statistics.trancieve_ip_packet(bytes_transferred, m_remote.address().is_v6()); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log(">>> wrote %d bytes", int(bytes_transferred)); +#endif + + if (error) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + peer_log("**ERROR**: %s [in peer_connection::on_send_data]", error.message().c_str()); +#endif + disconnect(error); + return; + } + if (m_disconnecting) return; + + TORRENT_ASSERT(!m_connecting); + TORRENT_ASSERT(bytes_transferred > 0); + + m_last_sent = time_now(); + +#if TORRENT_USE_ASSERTS + size_type cur_payload_ul = m_statistics.last_payload_uploaded(); + size_type cur_protocol_ul = m_statistics.last_protocol_uploaded(); +#endif + on_sent(error, bytes_transferred); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_statistics.last_payload_uploaded() - cur_payload_ul >= 0); + TORRENT_ASSERT(m_statistics.last_protocol_uploaded() - cur_protocol_ul >= 0); + size_type stats_diff = m_statistics.last_payload_uploaded() - cur_payload_ul + + m_statistics.last_protocol_uploaded() - cur_protocol_ul; + TORRENT_ASSERT(stats_diff == int(bytes_transferred)); +#endif + + fill_send_buffer(); + + setup_send(); + } + +#if TORRENT_USE_INVARIANT_CHECKS + struct peer_count_t + { + peer_count_t(): num_peers(0), num_peers_with_timeouts(0), num_peers_with_nowant(0), num_not_requested(0) {} + int num_peers; + int num_peers_with_timeouts; + int num_peers_with_nowant; + int num_not_requested; +// std::vector peers; + }; + + void peer_connection::check_invariant() const + { + TORRENT_ASSERT(m_in_use == 1337); + TORRENT_ASSERT(m_queued_time_critical <= int(m_request_queue.size())); + TORRENT_ASSERT(m_accept_fast.size() == m_accept_fast_piece_cnt.size()); + + TORRENT_ASSERT(bool(m_disk_recv_buffer) == (m_disk_recv_buffer_size > 0)); + + TORRENT_ASSERT(m_upload_limit >= 0); + TORRENT_ASSERT(m_download_limit >= 0); + + // It's not obvious why this invariant breaks when the peer disconnects + if (!m_disconnecting) + { + if (m_channel_state[upload_channel] & peer_info::bw_limit) + TORRENT_ASSERT(m_ses.m_upload_rate.is_queued(this)); + if (m_channel_state[download_channel] & peer_info::bw_limit) + TORRENT_ASSERT(m_ses.m_download_rate.is_queued(this)); + } + + boost::shared_ptr t = m_torrent.lock(); + + if (!m_disconnect_started && m_initialized) + { + // none of this matters if we're disconnecting anyway + if (t->is_finished()) + TORRENT_ASSERT(!is_interesting()); + if (is_seed()) + TORRENT_ASSERT(upload_only()); + } + + if (m_disconnecting) + { + TORRENT_ASSERT(m_download_queue.empty()); + TORRENT_ASSERT(m_request_queue.empty()); + TORRENT_ASSERT(!t); + TORRENT_ASSERT(m_disconnect_started); + } + else if (!m_in_constructor) + { + TORRENT_ASSERT(m_ses.has_peer((peer_connection*)this)); + } + + TORRENT_ASSERT(m_outstanding_bytes >= 0); + if (t && t->valid_metadata() && !m_disconnecting) + { + torrent_info const& ti = t->torrent_file(); + // if the piece is fully downloaded, we might have popped it from the + // download queue already + int outstanding_bytes = 0; +// bool in_download_queue = false; + int block_size = t->block_size(); + piece_block last_block(ti.num_pieces()-1 + , (ti.piece_size(ti.num_pieces()-1) + block_size - 1) / block_size); + for (std::vector::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + TORRENT_ASSERT(i->block.piece_index <= last_block.piece_index); + TORRENT_ASSERT(i->block.piece_index < last_block.piece_index + || i->block.block_index <= last_block.block_index); + if (m_received_in_piece && i == m_download_queue.begin()) + { +// in_download_queue = true; + // this assert is not correct since block may have different sizes + // and may not be returned in the order they were requested +// TORRENT_ASSERT(t->to_req(i->block).length >= m_received_in_piece); + outstanding_bytes += t->to_req(i->block).length - m_received_in_piece; + } + else + { + outstanding_bytes += t->to_req(i->block).length; + } + } + //if (p && p->bytes_downloaded < p->full_block_bytes) TORRENT_ASSERT(in_download_queue); + + TORRENT_ASSERT(m_outstanding_bytes == outstanding_bytes); + } + + std::set unique; + std::transform(m_download_queue.begin(), m_download_queue.end() + , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); + std::transform(m_request_queue.begin(), m_request_queue.end() + , std::inserter(unique, unique.begin()), boost::bind(&pending_block::block, _1)); + TORRENT_ASSERT(unique.size() == m_download_queue.size() + m_request_queue.size()); + if (m_peer_info) + { + TORRENT_ASSERT(m_peer_info->prev_amount_upload == 0); + TORRENT_ASSERT(m_peer_info->prev_amount_download == 0); + TORRENT_ASSERT(m_peer_info->connection == this + || m_peer_info->connection == 0); + + if (m_peer_info->optimistically_unchoked) + TORRENT_ASSERT(!is_choked()); + } + + TORRENT_ASSERT(m_have_piece.count() == m_num_pieces); + + if (!t) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // since this connection doesn't have a torrent reference + // no torrent should have a reference to this connection either + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer((peer_connection*)this)); +#endif + return; + } + + if (t->ready_for_connections() && m_initialized) + TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size())); + + // in share mode we don't close redundant connections + if (m_ses.settings().close_redundant_connections && !t->share_mode()) + { + bool ok_to_disconnect = + can_disconnect(error_code(errors::upload_upload_connection, get_libtorrent_category())) + || can_disconnect(error_code(errors::uninteresting_upload_peer, get_libtorrent_category())) + || can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_no_request, get_libtorrent_category())) + || can_disconnect(error_code(errors::timed_out_inactivity, get_libtorrent_category())); + + // make sure upload only peers are disconnected + if (t->is_upload_only() + && m_upload_only + && t->valid_metadata() + && has_metadata() + && ok_to_disconnect) + TORRENT_ASSERT(m_disconnect_started || t->graceful_pause() || t->has_error()); + + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked() + && t->valid_metadata() + && has_metadata() + && ok_to_disconnect) + TORRENT_ASSERT(m_disconnect_started); + } + + if (!m_disconnect_started && m_initialized && m_ses.settings().close_redundant_connections) + { + // none of this matters if we're disconnecting anyway + if (t->is_upload_only()) + TORRENT_ASSERT(!m_interesting || t->graceful_pause() || t->has_error()); + if (is_seed()) + TORRENT_ASSERT(m_upload_only); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + if (t->has_picker()) + { + std::map num_requests; + for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) + { + // make sure this peer is not a dangling pointer +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif + peer_connection const& p = *(*i); + for (std::vector::const_iterator i = p.request_queue().begin() + , end(p.request_queue().end()); i != end; ++i) + { + ++num_requests[i->block].num_peers; + ++num_requests[i->block].num_peers_with_timeouts; + ++num_requests[i->block].num_peers_with_nowant; + ++num_requests[i->block].num_not_requested; +// num_requests[i->block].peers.push_back(&p); + } + for (std::vector::const_iterator i = p.download_queue().begin() + , end(p.download_queue().end()); i != end; ++i) + { + if (!i->not_wanted && !i->timed_out) ++num_requests[i->block].num_peers; + if (i->timed_out) ++num_requests[i->block].num_peers_with_timeouts; + if (i->not_wanted) ++num_requests[i->block].num_peers_with_nowant; +// num_requests[i->block].peers.push_back(&p); + } + } + for (std::map::iterator i = num_requests.begin() + , end(num_requests.end()); i != end; ++i) + { + piece_block b = i->first; + peer_count_t const& pc = i->second; + int count = pc.num_peers; + int count_with_timeouts = pc.num_peers_with_timeouts; + int count_with_nowant = pc.num_peers_with_nowant; + (void)count_with_timeouts; + (void)count_with_nowant; + int picker_count = t->picker().num_peers(b); + if (!t->picker().is_downloaded(b)) + TORRENT_ASSERT(picker_count == count); + } + } + + if (m_peer_info && type() == bittorrent_connection) + { + policy::const_iterator i = t->get_policy().begin_peer(); + policy::const_iterator end = t->get_policy().end_peer(); + for (; i != end; ++i) + { + if (*i == m_peer_info) break; + } + TORRENT_ASSERT(i != end); + } +#endif +/* + if (t->has_picker() && !t->is_aborted()) + { + // make sure that pieces that have completed the download + // of all their blocks are in the disk io thread's queue + // to be checked. + const std::vector& dl_queue + = t->picker().get_download_queue(); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + const int blocks_per_piece = t->picker().blocks_in_piece(i->index); + + bool complete = true; + for (int j = 0; j < blocks_per_piece; ++j) + { + if (i->info[j].state == piece_picker::block_info::state_finished) + continue; + complete = false; + break; + } + TORRENT_ASSERT(complete); + } + } +*/ +// extremely expensive invariant check +/* + if (!t->is_seed()) + { + piece_picker& p = t->picker(); + const std::vector& dlq = p.get_download_queue(); + const int blocks_per_piece = static_cast( + t->torrent_file().piece_length() / t->block_size()); + + for (std::vector::const_iterator i = + dlq.begin(); i != dlq.end(); ++i) + { + for (int j = 0; j < blocks_per_piece; ++j) + { + if (std::find(m_request_queue.begin(), m_request_queue.end() + , piece_block(i->index, j)) != m_request_queue.end() + || + std::find(m_download_queue.begin(), m_download_queue.end() + , piece_block(i->index, j)) != m_download_queue.end()) + { + TORRENT_ASSERT(i->info[j].peer == m_remote); + } + else + { + TORRENT_ASSERT(i->info[j].peer != m_remote || i->info[j].finished); + } + } + } + } +*/ + } +#endif + + peer_connection::peer_speed_t peer_connection::peer_speed() + { + shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + + int download_rate = int(statistics().download_payload_rate()); + int torrent_download_rate = int(t->statistics().download_payload_rate()); + + if (download_rate > 512 && download_rate > torrent_download_rate / 16) + m_speed = fast; + else if (download_rate > 4096 && download_rate > torrent_download_rate / 64) + m_speed = medium; + else if (download_rate < torrent_download_rate / 15 && m_speed == fast) + m_speed = medium; + else + m_speed = slow; + + return m_speed; + } + + void peer_connection::keep_alive() + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + time_duration d; + d = time_now() - m_last_sent; + if (total_seconds(d) < m_timeout / 2) return; + + if (m_connecting) return; + if (in_handshake()) return; + + // if the last send has not completed yet, do not send a keep + // alive + if (m_channel_state[upload_channel] & peer_info::bw_network) return; + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> KEEPALIVE"); +#endif + + m_last_sent = time_now(); + write_keepalive(); + } + + bool peer_connection::is_seed() const + { + // if m_num_pieces == 0, we probably don't have the + // metadata yet. + boost::shared_ptr t = m_torrent.lock(); + return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0 && t && t->valid_metadata(); + } + + void peer_connection::set_share_mode(bool u) + { + // if the peer is a seed, ignore share mode messages + if (is_seed()) return; + + m_share_mode = u; + } + + void peer_connection::set_upload_only(bool u) + { + // if the peer is a seed, don't allow setting + // upload_only to false + if (m_upload_only || is_seed()) return; + + m_upload_only = u; + boost::shared_ptr t = associated_torrent().lock(); + t->get_policy().set_seed(m_peer_info, u); + disconnect_if_redundant(); + } + +} diff --git a/apps/Launcher/ext/libtorrent/src/piece_picker.cpp b/apps/Launcher/ext/libtorrent/src/piece_picker.cpp new file mode 100644 index 0000000000..acc5852bf7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/piece_picker.cpp @@ -0,0 +1,2731 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include +#include + +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/bitfield.hpp" +#include "libtorrent/random.hpp" + +#if TORRENT_USE_ASSERTS +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/policy.hpp" // for policy::peer +#endif + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +#include "libtorrent/invariant_check.hpp" + +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +//#define TORRENT_NO_EXPENSIVE_INVARIANT_CHECK +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK + +//#define TORRENT_PICKER_LOG + +namespace libtorrent +{ + + const piece_block piece_block::invalid(0x7FFFF, 0x1FFF); + + piece_picker::piece_picker() + : m_seeds(0) + , m_priority_boundries(1, int(m_pieces.size())) + , m_blocks_per_piece(0) + , m_blocks_in_last_piece(0) + , m_num_filtered(0) + , m_num_have_filtered(0) + , m_num_have(0) + , m_cursor(0) + , m_reverse_cursor(0) + , m_sparse_regions(1) + , m_num_pad_files(0) + , m_dirty(false) + { +#ifdef TORRENT_PICKER_LOG + std::cerr << "new piece_picker" << std::endl; +#endif +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void piece_picker::init(int blocks_per_piece, int blocks_in_last_piece, int total_num_pieces) + { + TORRENT_ASSERT(blocks_per_piece > 0); + TORRENT_ASSERT(total_num_pieces > 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::init()" << std::endl; +#endif + // allocate the piece_map to cover all pieces + // and make them invalid (as if we don't have a single piece) + m_piece_map.resize(total_num_pieces, piece_pos(0, 0)); + m_reverse_cursor = int(m_piece_map.size()); + m_cursor = 0; + + m_downloads.clear(); + m_block_info.clear(); + + m_num_filtered += m_num_have_filtered; + m_num_have_filtered = 0; + m_num_have = 0; + m_dirty = true; + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + i->peer_count = 0; + i->downloading = 0; + i->index = 0; +#ifdef TORRENT_DEBUG_REFCOUNTS + i->have_peers.clear(); +#endif + } + + for (std::vector::iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + for (std::vector::reverse_iterator i = m_piece_map.rend() + - m_reverse_cursor; m_reverse_cursor > 0 && (i->have() || i->filtered()); + ++i, --m_reverse_cursor); + + // the piece index is stored in 20 bits, which limits the allowed + // number of pieces somewhat + TORRENT_ASSERT(m_piece_map.size() < piece_pos::we_have_index); + + m_blocks_per_piece = blocks_per_piece; + m_blocks_in_last_piece = blocks_in_last_piece; + if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; + + TORRENT_ASSERT(m_blocks_in_last_piece <= m_blocks_per_piece); + } + + void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + if (m_piece_map[index].downloading) + { + std::vector::const_iterator piece = find_dl_piece(index); + TORRENT_ASSERT(piece != m_downloads.end()); + st = *piece; + return; + } + st.info = 0; + st.index = index; + st.writing = 0; + st.requested = 0; + if (m_piece_map[index].have()) + { + st.finished = blocks_in_piece(index); + return; + } + st.finished = 0; + } + + piece_picker::downloading_piece& piece_picker::add_download_piece(int piece) + { + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(piece < int(m_piece_map.size())); + int num_downloads = m_downloads.size(); + int block_index = num_downloads * m_blocks_per_piece; + if (int(m_block_info.size()) < block_index + m_blocks_per_piece) + { + block_info* base = 0; + if (!m_block_info.empty()) base = &m_block_info[0]; + m_block_info.resize(block_index + m_blocks_per_piece); + if (!m_downloads.empty() && &m_block_info[0] != base) + { + // this means the memory was reallocated, update the pointers + for (int i = 0; i < int(m_downloads.size()); ++i) + m_downloads[i].info = &m_block_info[m_downloads[i].info - base]; + } + } + downloading_piece ret; + ret.index = piece; + std::vector::iterator i = std::lower_bound(m_downloads.begin() + , m_downloads.end(), ret); + TORRENT_ASSERT(i == m_downloads.end() || i->index != piece); + ret.info = &m_block_info[block_index]; + TORRENT_ASSERT(ret.info >= &m_block_info[0]); + TORRENT_ASSERT(ret.info < &m_block_info[0] + m_block_info.size()); +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(piece); + VALGRIND_CHECK_VALUE_IS_DEFINED(block_index); +#endif + for (int i = 0; i < m_blocks_per_piece; ++i) + { + ret.info[i].num_peers = 0; + ret.info[i].state = block_info::state_none; + ret.info[i].peer = 0; +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info[i].peer); +#endif +#if TORRENT_USE_ASSERTS + ret.info[i].piece_index = piece; +#endif + } + +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.finished); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.writing); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.requested); + VALGRIND_CHECK_VALUE_IS_DEFINED(ret.state); +#endif + i = m_downloads.insert(i, ret); + return *i; + } + + void piece_picker::erase_download_piece(std::vector::iterator i) + { + std::vector::iterator other = std::find_if( + m_downloads.begin(), m_downloads.end() + , boost::bind(&downloading_piece::info, _1) + == &m_block_info[(m_downloads.size() - 1) * m_blocks_per_piece]); + TORRENT_ASSERT(other != m_downloads.end()); + + if (i != other) + { + std::copy(other->info, other->info + m_blocks_per_piece, i->info); + other->info = i->info; + } + m_piece_map[i->index].downloading = false; + m_downloads.erase(i); + } + +#if TORRENT_USE_INVARIANT_CHECKS + + void piece_picker::verify_pick(std::vector const& picked + , bitfield const& bits) const + { + TORRENT_ASSERT(bits.size() == m_piece_map.size()); + for (std::vector::const_iterator i = picked.begin() + , end(picked.end()); i != end; ++i) + { + TORRENT_ASSERT(i->piece_index >= 0); + TORRENT_ASSERT(i->piece_index < bits.size()); + TORRENT_ASSERT(bits[i->piece_index]); + TORRENT_ASSERT(!m_piece_map[i->piece_index].have()); + TORRENT_ASSERT(!m_piece_map[i->piece_index].filtered()); + } + } + + void piece_picker::verify_priority(int range_start, int range_end, int prio) const + { + TORRENT_ASSERT(range_start <= range_end); + TORRENT_ASSERT(range_end <= int(m_pieces.size())); + for (std::vector::const_iterator i = m_pieces.begin() + range_start + , end(m_pieces.begin() + range_end); i != end; ++i) + { + int index = *i; + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + int p = m_piece_map[index].priority(this); + TORRENT_ASSERT(p == prio); + } + } + +#if defined TORRENT_PICKER_LOG + void piece_picker::print_pieces() const + { + for (std::vector::const_iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + std::cerr << *i << " "; + } + std::cout << std::endl; + int index = 0; + std::vector::const_iterator j = m_priority_boundries.begin(); + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i, ++index) + { + if (*i == -1) break; + while (j != m_priority_boundries.end() && *j <= index) + { + std::cerr << "| "; + ++j; + } + std::cerr << *i << "(" << m_piece_map[*i].index << ") "; + } + std::cerr << std::endl; + } +#endif // TORRENT_PIECE_PICKER +#endif // TORRENT_USE_INVARIANT_CHECKS + +#if TORRENT_USE_INVARIANT_CHECKS + void piece_picker::check_invariant(const torrent* t) const + { +#ifndef TORRENT_DEBUG_REFCOUNTS +#if TORRENT_COMPACT_PICKER + TORRENT_ASSERT(sizeof(piece_pos) == 4); +#else + TORRENT_ASSERT(sizeof(piece_pos) == 8); +#endif +#endif + TORRENT_ASSERT(m_num_have >= 0); + TORRENT_ASSERT(m_num_have_filtered >= 0); + TORRENT_ASSERT(m_num_filtered >= 0); + TORRENT_ASSERT(m_seeds >= 0); + + if (!m_downloads.empty()) + { + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end() - 1; ++i) + { + downloading_piece const& dp = *i; + downloading_piece const& next = *(i + 1); +// TORRENT_ASSERT(dp.finished + dp.writing >= next.finished + next.writing); + TORRENT_ASSERT(dp.index < next.index); + } + } + + if (t != 0) + TORRENT_ASSERT((int)m_piece_map.size() == t->torrent_file().num_pieces()); + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + bool blocks_requested = false; + int num_blocks = blocks_in_piece(i->index); + int num_requested = 0; + int num_finished = 0; + int num_writing = 0; + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == i->index); + TORRENT_ASSERT(i->info[k].peer == 0 || static_cast(i->info[k].peer)->in_use); + if (i->info[k].state == block_info::state_finished) + { + ++num_finished; + TORRENT_ASSERT(i->info[k].num_peers == 0); + } + else if (i->info[k].state == block_info::state_requested) + { + ++num_requested; + blocks_requested = true; + TORRENT_ASSERT(i->info[k].num_peers > 0); + } + else if (i->info[k].state == block_info::state_writing) + { + ++num_writing; + TORRENT_ASSERT(i->info[k].num_peers == 0); + } + } + TORRENT_ASSERT(blocks_requested == (i->state != none)); + TORRENT_ASSERT(num_requested == i->requested); + TORRENT_ASSERT(num_writing == i->writing); + TORRENT_ASSERT(num_finished == i->finished); + if (m_piece_map[i->index].full) + TORRENT_ASSERT(num_finished + num_writing + num_requested == num_blocks); + } + int num_pieces = int(m_piece_map.size()); + TORRENT_ASSERT(m_cursor >= 0); + TORRENT_ASSERT(m_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor <= num_pieces); + TORRENT_ASSERT(m_reverse_cursor >= 0); + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces && m_reverse_cursor == 0)); + +#ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK + return; +#endif + + if (!m_dirty) + { + TORRENT_ASSERT(!m_priority_boundries.empty()); + int prio = 0; + int start = 0; + for (std::vector::const_iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + verify_priority(start, *i, prio); + ++prio; + start = *i; + } + TORRENT_ASSERT(m_priority_boundries.back() == int(m_pieces.size())); + } + int index = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++index); + TORRENT_ASSERT(m_cursor == index); + index = num_pieces; + if (num_pieces > 0) + { + for (std::vector::reverse_iterator i = m_piece_map.rend() + - index; index > 0 && (i->have() || i->filtered()); ++i, --index); + TORRENT_ASSERT(index == num_pieces + || m_piece_map[index].have() + || m_piece_map[index].filtered()); + TORRENT_ASSERT(m_reverse_cursor == index); + } + else + { + TORRENT_ASSERT(m_reverse_cursor == 0); + } + + int num_filtered = 0; + int num_have_filtered = 0; + int num_have = 0; + for (std::vector::const_iterator i = m_piece_map.begin(); + i != m_piece_map.end(); ++i) + { + int index = static_cast(i - m_piece_map.begin()); + piece_pos const& p = *i; + + if (p.filtered()) + { + if (p.index != piece_pos::we_have_index) + ++num_filtered; + else + ++num_have_filtered; + } + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.size() == p.peer_count + m_seeds); +#endif + + if (p.index == piece_pos::we_have_index) + ++num_have; + +#if 0 + if (t != 0) + { + int actual_peer_count = 0; + for (torrent::const_peer_iterator peer = t->begin(); + peer != t->end(); ++peer) + { + if (peer->second->has_piece(index)) actual_peer_count++; + } + + TORRENT_ASSERT((int)i->peer_count == actual_peer_count); +/* + int num_downloaders = 0; + for (std::vector::const_iterator peer = t->begin(); + peer != t->end(); + ++peer) + { + const std::vector& queue = (*peer)->download_queue(); + if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue; + + ++num_downloaders; + } + + if (i->downloading) + { + TORRENT_ASSERT(num_downloaders == 1); + } + else + { + TORRENT_ASSERT(num_downloaders == 0); + } +*/ + } +#endif + + if (p.index == piece_pos::we_have_index) + { + TORRENT_ASSERT(t == 0 || t->have_piece(index)); + TORRENT_ASSERT(p.downloading == 0); + } + + if (t != 0) + TORRENT_ASSERT(!t->have_piece(index)); + + int prio = p.priority(this); + TORRENT_ASSERT(prio == -1 || p.downloading == (prio % piece_picker::prio_factor == 0)); + + if (!m_dirty) + { + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + if (prio >= 0) + { + TORRENT_ASSERT(p.index < m_pieces.size()); + TORRENT_ASSERT(m_pieces[p.index] == index); + } + else + { + TORRENT_ASSERT(prio == -1); + // make sure there's no entry + // with this index. (there shouldn't + // be since the priority is -1) + TORRENT_ASSERT(std::find(m_pieces.begin(), m_pieces.end(), index) + == m_pieces.end()); + } + } + + int count = std::count_if(m_downloads.begin(), m_downloads.end() + , has_index(index)); + if (i->downloading == 1) + { + TORRENT_ASSERT(count == 1); + } + else + { + TORRENT_ASSERT(count == 0); + } + } + TORRENT_ASSERT(num_have == m_num_have); + TORRENT_ASSERT(num_filtered == m_num_filtered); + TORRENT_ASSERT(num_have_filtered == m_num_have_filtered); + + if (!m_dirty) + { + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i) + { + TORRENT_ASSERT(m_piece_map[*i].priority(this) >= 0); + } + } + } +#endif + + std::pair piece_picker::distributed_copies() const + { + TORRENT_ASSERT(m_seeds >= 0); + const int num_pieces = m_piece_map.size(); + + if (num_pieces == 0) return std::make_pair(1, 0); + int min_availability = piece_pos::max_peer_count; + // find the lowest availability count + // count the number of pieces that have that availability + // and also the number of pieces that have more than that. + int integer_part = 0; + int fraction_part = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int peer_count = int(i->peer_count); + // take ourself into account + if (i->have()) ++peer_count; + if (min_availability > peer_count) + { + min_availability = peer_count; + fraction_part += integer_part; + integer_part = 1; + } + else if (peer_count == min_availability) + { + ++integer_part; + } + else + { + TORRENT_ASSERT(peer_count > min_availability); + ++fraction_part; + } + } + TORRENT_ASSERT(integer_part + fraction_part == num_pieces); + return std::make_pair(min_availability + m_seeds, fraction_part * 1000 / num_pieces); + } + + void piece_picker::priority_range(int prio, int* start, int* end) + { + TORRENT_ASSERT(prio >= 0); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + if (prio == 0) *start = 0; + else *start = m_priority_boundries[prio - 1]; + *end = m_priority_boundries[prio]; + TORRENT_ASSERT(*start <= *end); + } + + void piece_picker::add(int index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(!p.filtered()); + TORRENT_ASSERT(!p.have()); + + int priority = p.priority(this); + TORRENT_ASSERT(priority >= 0); + if (int(m_priority_boundries.size()) <= priority) + m_priority_boundries.resize(priority + 1, m_pieces.size()); + + TORRENT_ASSERT(int(m_priority_boundries.size()) >= priority); + + int range_start, range_end; + priority_range(priority, &range_start, &range_end); + int new_index; + if (range_end == range_start) new_index = range_start; + else new_index = random() % (range_end - range_start + 1) + range_start; + +#ifdef TORRENT_PICKER_LOG + std::cerr << "add " << index << " (" << priority << ")" << std::endl; + print_pieces(); +#endif + m_pieces.push_back(-1); + + for (;;) + { + TORRENT_ASSERT(new_index < int(m_pieces.size())); + int temp = m_pieces[new_index]; + m_pieces[new_index] = index; + m_piece_map[index].index = new_index; + index = temp; + do + { + temp = m_priority_boundries[priority]++; + ++priority; + } while (temp == new_index && priority < int(m_priority_boundries.size())); + new_index = temp; +#ifdef TORRENT_PICKER_LOG + print_pieces(); + std::cerr << " index: " << index + << " prio: " << priority + << " new_index: " << new_index + << std::endl; +#endif + if (priority >= int(m_priority_boundries.size())) break; + TORRENT_ASSERT(temp >= 0); + } + if (index != -1) + { + TORRENT_ASSERT(new_index == int(m_pieces.size() - 1)); + m_pieces[new_index] = index; + m_piece_map[index].index = new_index; + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif +// shuffle(priority, new_index); +#ifdef TORRENT_PICKER_LOG +// print_pieces(); +#endif + } + } + + void piece_picker::remove(int priority, int elem_index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + TORRENT_ASSERT(elem_index >= 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "remove " << m_pieces[elem_index] << " (" << priority << ")" << std::endl; +#endif + int next_index = elem_index; + TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == -1); + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + int temp; + do + { + temp = --m_priority_boundries[priority]; + ++priority; + } while (next_index == temp && priority < int(m_priority_boundries.size())); + if (next_index == temp) break; + next_index = temp; + + int piece = m_pieces[next_index]; + m_pieces[elem_index] = piece; + m_piece_map[piece].index = elem_index; + TORRENT_ASSERT(m_piece_map[piece].priority(this) == priority - 1); + TORRENT_ASSERT(elem_index < int(m_pieces.size() - 1)); + elem_index = next_index; + + if (priority == int(m_priority_boundries.size())) + break; + } + m_pieces.pop_back(); + TORRENT_ASSERT(next_index == int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + } + + // will update the piece with the given properties (priority, elem_index) + // to place it at the correct position + void piece_picker::update(int priority, int elem_index) + { + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index >= 0); + + TORRENT_ASSERT(int(m_priority_boundries.size()) > priority); + + int index = m_pieces[elem_index]; + // update the piece_map + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(int(p.index) == elem_index || p.have()); + + int new_priority = p.priority(this); + + if (new_priority == priority) return; + + if (new_priority == -1) + { + remove(priority, elem_index); + return; + } + + if (int(m_priority_boundries.size()) <= new_priority) + m_priority_boundries.resize(new_priority + 1, m_pieces.size()); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "update " << index << " (" << priority << "->" << new_priority << ")" << std::endl; +#endif + if (priority > new_priority) + { + int new_index; + int temp = index; + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + --priority; + new_index = m_priority_boundries[priority]++; + TORRENT_ASSERT(new_index < int(m_pieces.size())); + if (temp != m_pieces[new_index]) + { + temp = m_pieces[new_index]; + m_pieces[elem_index] = temp; + m_piece_map[temp].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + } + elem_index = new_index; + if (priority == new_priority) break; + } +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + m_pieces[elem_index] = index; + m_piece_map[index].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + shuffle(priority, elem_index); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); + } + else + { + int new_index; + int temp = index; + for (;;) + { +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + new_index = --m_priority_boundries[priority]; + TORRENT_ASSERT(new_index < int(m_pieces.size())); + if (temp != m_pieces[new_index]) + { + temp = m_pieces[new_index]; + m_pieces[elem_index] = temp; + m_piece_map[temp].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + } + elem_index = new_index; + ++priority; + if (priority == new_priority) break; + } +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + m_pieces[elem_index] = index; + m_piece_map[index].index = elem_index; + TORRENT_ASSERT(elem_index < int(m_pieces.size())); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + shuffle(priority, elem_index); +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + TORRENT_ASSERT(m_piece_map[index].priority(this) == priority); + } + } + + void piece_picker::shuffle(int priority, int elem_index) + { +#ifdef TORRENT_PICKER_LOG + std::cerr << "shuffle()" << std::endl; +#endif + + TORRENT_ASSERT(!m_dirty); + TORRENT_ASSERT(priority >= 0); + TORRENT_ASSERT(elem_index >= 0); + TORRENT_ASSERT(elem_index < int(m_pieces.size())); + TORRENT_ASSERT(m_piece_map[m_pieces[elem_index]].priority(this) == priority); + + int range_start, range_end; + priority_range(priority, &range_start, &range_end); + TORRENT_ASSERT(range_start < range_end); + int other_index = random() % (range_end - range_start) + range_start; + + if (other_index == elem_index) return; + + // swap other_index with elem_index + piece_pos& p1 = m_piece_map[m_pieces[other_index]]; + piece_pos& p2 = m_piece_map[m_pieces[elem_index]]; + + int temp = p1.index; + p1.index = p2.index; + p2.index = temp; + std::swap(m_pieces[other_index], m_pieces[elem_index]); + } +/* + void piece_picker::sort_piece(std::vector::iterator dp) + { + TORRENT_ASSERT(m_piece_map[dp->index].downloading); + int complete = dp->writing + dp->finished; + if (dp != m_downloads.begin()) + { + for (std::vector::iterator j(dp-1); + dp != m_downloads.begin(); --dp, --j) + { + TORRENT_ASSERT(j >= m_downloads.begin()); + if (j->finished + j->writing >= complete) break; + using std::swap; + swap(*j, *dp); + if (j == m_downloads.begin()) return; + } + } + + TORRENT_ASSERT(dp != m_downloads.end()); + for (std::vector::iterator j(dp+1); + dp != m_downloads.end() - 1; ++dp, ++j) + { + TORRENT_ASSERT(j < m_downloads.end()); + if (j->finished + j->writing <= complete) break; + using std::swap; + swap(*j, *dp); + if (j == m_downloads.end() - 1) return; + } + } +*/ + void piece_picker::restore_piece(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_piece_map.size())); + + TORRENT_ASSERT(m_piece_map[index].downloading == 1); + + std::vector::iterator i = find_dl_piece(index); + + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); +#ifdef TORRENT_DEBUG + int num_blocks = blocks_in_piece(i->index); + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == index); + TORRENT_ASSERT(i->info[k].state == block_info::state_finished); + TORRENT_ASSERT(i->info[k].num_peers == 0); + } +#endif + + piece_pos& p = m_piece_map[index]; + int prev_priority = p.priority(this); + erase_download_piece(i); + int new_priority = p.priority(this); + + if (new_priority == prev_priority) return; + if (m_dirty) return; + if (prev_priority == -1) add(index); + else update(prev_priority, p.index); + } + + void piece_picker::inc_refcount_all(const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + ++m_seeds; + if (m_seeds == 1) + { + // when m_seeds is increased from 0 to 1 + // we may have to add pieces that previously + // didn't have any peers + m_dirty = true; + } + +#ifdef TORRENT_DEBUG_REFCOUNTS + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + TORRENT_ASSERT(i->have_peers.count(peer) == 0); + i->have_peers.insert(peer); + } +#endif + } + + void piece_picker::dec_refcount_all(const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + if (m_seeds > 0) + { + --m_seeds; + if (m_seeds == 0) + { + // when m_seeds is decreased from 1 to 0 + // we may have to remove pieces that previously + // didn't have any peers + m_dirty = true; + } +#ifdef TORRENT_DEBUG_REFCOUNTS + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + TORRENT_ASSERT(i->have_peers.count(peer) == 1); + i->have_peers.erase(peer); + } +#endif + return; + } + TORRENT_ASSERT(m_seeds == 0); + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(i->have_peers.count(peer) == 1); + i->have_peers.erase(peer); +#endif + + TORRENT_ASSERT(i->peer_count > 0); + --i->peer_count; + } + + m_dirty = true; + } + + void piece_picker::inc_refcount(int index, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + piece_pos& p = m_piece_map[index]; + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.count(peer) == 0); + p.have_peers.insert(peer); +#endif + + int prev_priority = p.priority(this); + ++p.peer_count; + if (m_dirty) return; + int new_priority = p.priority(this); + if (prev_priority == new_priority) return; + if (prev_priority == -1) + add(index); + else + update(prev_priority, p.index); + } + + // this function decrements the m_seeds counter + // and increments the peer counter on every piece + // instead. Sometimes of we connect to a seed that + // later sends us a dont-have message, we'll need to + // turn that m_seed into counts on the pieces since + // they can't be negative + void piece_picker::break_one_seed() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + TORRENT_ASSERT(m_seeds > 0); + --m_seeds; + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + ++i->peer_count; + } + + m_dirty = true; + } + + void piece_picker::dec_refcount(int index, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + piece_pos& p = m_piece_map[index]; + +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(p.have_peers.count(peer) == 1); + p.have_peers.erase(peer); +#endif + + if (p.peer_count == 0) + { + TORRENT_ASSERT(m_seeds > 0); + // this is the case where we have one or more + // seeds, and one of them saying: I don't have this + // piece anymore. we need to break up one of the seed + // counters into actual peer counters on the pieces + break_one_seed(); + } + + int prev_priority = p.priority(this); + TORRENT_ASSERT(p.peer_count > 0); + --p.peer_count; + if (m_dirty) return; + if (prev_priority >= 0) update(prev_priority, p.index); + } + + void piece_picker::inc_refcount(bitfield const& bitmask, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(bitmask.size() == m_piece_map.size()); + + int index = 0; + bool updated = false; + for (bitfield::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if (*i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(m_piece_map[index].have_peers.count(peer) == 0); + m_piece_map[index].have_peers.insert(peer); +#endif + + ++m_piece_map[index].peer_count; + updated = true; + } + } + + if (updated) m_dirty = true; + } + + void piece_picker::dec_refcount(bitfield const& bitmask, const void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(bitmask.size() <= m_piece_map.size()); + + int index = 0; + bool updated = false; +#if TORRENT_USE_ASSERTS + bool seed_broken = false; +#endif + for (bitfield::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if (*i) + { +#ifdef TORRENT_DEBUG_REFCOUNTS + TORRENT_ASSERT(m_piece_map[index].have_peers.count(peer) == 1); + m_piece_map[index].have_peers.erase(peer); +#endif + piece_pos& p = m_piece_map[index]; + + if (p.peer_count == 0) + { + TORRENT_ASSERT(!seed_broken); + TORRENT_ASSERT(m_seeds > 0); + // this is the case where we have one or more + // seeds, and one of them saying: I don't have this + // piece anymore. we need to break up one of the seed + // counters into actual peer counters on the pieces + break_one_seed(); +#if TORRENT_USE_ASSERTS + seed_broken = true; +#endif + } + + --p.peer_count; + updated = true; + } + } + + if (updated) m_dirty = true; + } + + void piece_picker::update_pieces() const + { + TORRENT_ASSERT(m_dirty); + if (m_priority_boundries.empty()) m_priority_boundries.resize(1, 0); +#ifdef TORRENT_PICKER_LOG + std::cerr << "update_pieces" << std::endl; +#endif + std::fill(m_priority_boundries.begin(), m_priority_boundries.end(), 0); + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prio = i->priority(this); + if (prio == -1) continue; + if (prio >= int(m_priority_boundries.size())) + m_priority_boundries.resize(prio + 1, 0); + i->index = m_priority_boundries[prio]; + ++m_priority_boundries[prio]; + } + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + + int index = 0; + for (std::vector::iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + *i += index; + index = *i; + } + m_pieces.resize(index, 0); + +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + + index = 0; + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i, ++index) + { + piece_pos& p = *i; + int prio = p.priority(this); + if (prio == -1) continue; + int new_index = (prio == 0 ? 0 : m_priority_boundries[prio - 1]) + p.index; + m_pieces[new_index] = index; + } + + int start = 0; + for (std::vector::iterator i = m_priority_boundries.begin() + , end(m_priority_boundries.end()); i != end; ++i) + { + if (start == *i) continue; + std::random_shuffle(&m_pieces[0] + start, &m_pieces[0] + *i); + start = *i; + } + + index = 0; + for (std::vector::const_iterator i = m_pieces.begin() + , end(m_pieces.end()); i != end; ++i, ++index) + { + TORRENT_ASSERT(*i >= 0 && *i < int(m_piece_map.size())); + m_piece_map[*i].index = index; + } + + m_dirty = false; +#ifdef TORRENT_PICKER_LOG + print_pieces(); +#endif + } + + void piece_picker::we_dont_have(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(p.downloading == 0); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_dont_have(" << index << ")" << std::endl; +#endif + if (!p.have()) return; + + if (p.filtered()) + { + ++m_num_filtered; + --m_num_have_filtered; + } + else + { + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } + + --m_num_have; + p.set_not_have(); + + if (m_dirty) return; + if (p.priority(this) >= 0) add(index); + } + + // this is used to indicate that we succesfully have + // downloaded a piece, and that no further attempts + // to pick that piece should be made. The piece will + // be removed from the available piece list. + void piece_picker::we_have(int index) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + +#ifdef TORRENT_PICKER_LOG + std::cerr << "piece_picker::we_have(" << index << ")" << std::endl; +#endif + piece_pos& p = m_piece_map[index]; + int info_index = p.index; + int priority = p.priority(this); + TORRENT_ASSERT(priority < int(m_priority_boundries.size()) || m_dirty); + + if (p.downloading) + { + std::vector::iterator i + = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + erase_download_piece(i); + } + + TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end()); + + if (p.have()) return; + +// maintain sparse_regions + if (index == 0) + { + if (index == int(m_piece_map.size()) - 1 + || m_piece_map[index + 1].have()) + --m_sparse_regions; + } + else if (index == int(m_piece_map.size() - 1)) + { + if (index == 0 + || m_piece_map[index - 1].have()) + --m_sparse_regions; + } + else + { + bool have_before = m_piece_map[index-1].have(); + bool have_after = m_piece_map[index+1].have(); + if (have_after && have_before) --m_sparse_regions; + else if (!have_after && !have_before) ++m_sparse_regions; + } + + if (p.filtered()) + { + --m_num_filtered; + ++m_num_have_filtered; + } + ++m_num_have; + p.set_have(); + if (m_cursor == m_reverse_cursor - 1 && + m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + TORRENT_ASSERT(num_pieces() > 0); + } + else if (m_cursor == index) + { + ++m_cursor; + for (std::vector::const_iterator i = m_piece_map.begin() + m_cursor + , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); + ++i, ++m_cursor); + } + else if (m_reverse_cursor - 1 == index) + { + --m_reverse_cursor; + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + for (std::vector::const_iterator i = m_piece_map.begin() + + m_reverse_cursor - 1; m_reverse_cursor > 0 && (i->have() || i->filtered()); + --i, --m_reverse_cursor); + TORRENT_ASSERT(m_piece_map[m_reverse_cursor].have() + || m_piece_map[m_reverse_cursor].filtered()); + } + TORRENT_ASSERT(m_reverse_cursor > m_cursor + || (m_cursor == num_pieces() && m_reverse_cursor == 0)); + if (priority == -1) return; + if (m_dirty) return; + remove(priority, info_index); + TORRENT_ASSERT(p.priority(this) == -1); + } + + bool piece_picker::set_piece_priority(int index, int new_piece_priority) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(new_piece_priority >= 0); + TORRENT_ASSERT(new_piece_priority <= 7); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + + // if the priority isn't changed, don't do anything + if (new_piece_priority == int(p.piece_priority)) return false; + + int prev_priority = p.priority(this); + TORRENT_ASSERT(m_dirty || prev_priority < int(m_priority_boundries.size())); + + bool ret = false; + if (new_piece_priority == piece_pos::filter_priority + && p.piece_priority != piece_pos::filter_priority) + { + // the piece just got filtered + if (p.have()) + { + ++m_num_have_filtered; + } + else + { + ++m_num_filtered; + + // update m_cursor + if (m_cursor == m_reverse_cursor - 1 && m_cursor == index) + { + m_cursor = int(m_piece_map.size()); + m_reverse_cursor = 0; + } + else if (m_cursor == index) + { + ++m_cursor; + while (m_cursor < int(m_piece_map.size()) + && (m_piece_map[m_cursor].have() + || m_piece_map[m_cursor].filtered())) + ++m_cursor; + } + else if (m_reverse_cursor == index + 1) + { + --m_reverse_cursor; + while (m_reverse_cursor > 0 + && (m_piece_map[m_reverse_cursor-1].have() + || m_piece_map[m_reverse_cursor-1].filtered())) + --m_reverse_cursor; + } + } + ret = true; + } + else if (new_piece_priority != piece_pos::filter_priority + && p.piece_priority == piece_pos::filter_priority) + { + // the piece just got unfiltered + if (p.have()) + { + --m_num_have_filtered; + } + else + { + --m_num_filtered; + // update cursors + if (index < m_cursor) + m_cursor = index; + if (index >= m_reverse_cursor) + m_reverse_cursor = index + 1; + if (m_reverse_cursor == m_cursor) + { + m_reverse_cursor = 0; + m_cursor = num_pieces(); + } + } + ret = true; + } + TORRENT_ASSERT(m_num_filtered >= 0); + TORRENT_ASSERT(m_num_have_filtered >= 0); + + p.piece_priority = new_piece_priority; + int new_priority = p.priority(this); + + if (prev_priority == new_priority) return ret; + + if (m_dirty) return ret; + if (prev_priority == -1) + { + add(index); + } + else + { + update(prev_priority, p.index); + } + return ret; + } + + int piece_picker::piece_priority(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size()); + + return m_piece_map[index].piece_priority; + } + + void piece_picker::piece_priorities(std::vector& pieces) const + { + pieces.resize(m_piece_map.size()); + std::vector::iterator j = pieces.begin(); + for (std::vector::const_iterator i = m_piece_map.begin(), + end(m_piece_map.end()); i != end; ++i, ++j) + { + *j = i->piece_priority; + } + } + + // ============ start deprecation ============== + + void piece_picker::filtered_pieces(std::vector& mask) const + { + mask.resize(m_piece_map.size()); + std::vector::iterator j = mask.begin(); + for (std::vector::const_iterator i = m_piece_map.begin(), + end(m_piece_map.end()); i != end; ++i, ++j) + { + *j = i->filtered(); + } + } + + // ============ end deprecation ============== + + namespace + { + int append_blocks(std::vector& dst, std::vector& src + , int num_blocks) + { + if (src.empty()) return num_blocks; + int to_copy; +// if (prefer_whole_pieces == 0) + to_copy = (std::min)(int(src.size()), num_blocks); +// else +// to_copy = int(src.size()); + + dst.insert(dst.end() + , src.begin(), src.begin() + to_copy); + src.clear(); + return num_blocks - to_copy; + } + } + + // pieces describes which pieces the peer we're requesting from + // has. + // interesting_blocks is an out parameter, and will be filled + // with (up to) num_blocks of interesting blocks that the peer has. + // prefer_whole_pieces can be set if this peer should download + // whole pieces rather than trying to download blocks from the + // same piece as other peers. + // the void* is the pointer to the policy::peer of the peer we're + // picking pieces from. This is used when downloading whole pieces, + // to only pick from the same piece the same peer is downloading + // from. state is supposed to be set to fast if the peer is downloading + // relatively fast, by some notion. Slow peers will prefer not + // to pick blocks from the same pieces as fast peers, and vice + // versa. Downloading pieces are marked as being fast, medium + // or slow once they're started. + + // options are: + // * rarest_first + // pick the rarest pieces first + // * reverse + // reverse the piece picking. Pick the most common + // pieces first or the last pieces (if picking sequential) + // * sequential + // download pieces in-order + // * on_parole + // the peer is on parole, only pick whole pieces which + // has only been downloaded and requested from the same + // peer + // * prioritize_partials + // pick blocks from downloading pieces first + // * speed_affinity + // have an affinity to pick pieces in the same speed + // category. + // * ignore_whole_pieces + // ignores the prefer_whole_pieces parameter (as if + // it was 0) + + // only one of rarest_first, sequential can be set + + void piece_picker::pick_pieces(bitfield const& pieces + , std::vector& interesting_blocks, int num_blocks + , int prefer_whole_pieces, void* peer, piece_state_t speed + , int options, std::vector const& suggested_pieces + , int num_peers) const + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + // prevent the number of partial pieces to grow indefinitely + // make this scale by the number of peers we have. For large + // scale clients, we would have more peers, and allow a higher + // threshold for the number of partials + if (int(m_downloads.size()) > m_num_pad_files + num_peers * 3 / 2) options |= prioritize_partials; + + if (options & ignore_whole_pieces) prefer_whole_pieces = 0; + + // only one of rarest_first and sequential can be set. + TORRENT_ASSERT(((options & rarest_first) ? 1 : 0) + + ((options & sequential) ? 1 : 0) <= 1); +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + TORRENT_ASSERT(num_blocks > 0); + TORRENT_ASSERT(pieces.size() == m_piece_map.size()); + + TORRENT_ASSERT(!m_priority_boundries.empty() + || m_dirty); + + // this will be filled with blocks that we should not request + // unless we can't find num_blocks among the other ones. + // blocks that belong to pieces with a mismatching speed + // category for instance, or if we prefer whole pieces, + // blocks belonging to a piece that others have + // downloaded to + std::vector backup_blocks; + std::vector backup_blocks2; + const std::vector empty_vector; + + // When prefer_whole_pieces is set (usually set when downloading from + // fast peers) the partial pieces will not be prioritized, but actually + // ignored as long as possible. All blocks found in downloading + // pieces are regarded as backup blocks + + if (options & prioritize_partials) + { + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) && piece_priority(i->index) != 7) + continue; + + if (!is_piece_free(i->index, pieces)) continue; + if (m_piece_map[i->index].full + && int(backup_blocks.size()) >= num_blocks + && int(backup_blocks2.size()) >= num_blocks) + continue; + + num_blocks = add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + if (num_blocks <= 0) return; + } + + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2 + , num_blocks); + if (num_blocks <= 0) return; + } + + if (!suggested_pieces.empty()) + { + for (std::vector::const_iterator i = suggested_pieces.begin(); + i != suggested_pieces.end(); ++i) + { + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) && piece_priority(*i) != 7) + continue; + + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, empty_vector + , speed, options); + if (num_blocks <= 0) return; + } + } + + if (options & sequential) + { + if (m_dirty) update_pieces(); + TORRENT_ASSERT(!m_dirty); + + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end() && piece_priority(*i) == 7; ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + + // in time critical mode, only pick prio 7 pieces + if ((options & time_critical_mode) == 0) + { + if (options & reverse) + { + for (int i = m_reverse_cursor - 1; i >= m_cursor; --i) + { + if (!is_piece_free(i, pieces)) continue; + // we've already added prio 7 pieces + if (piece_priority(i) == 7) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + else + { + for (int i = m_cursor; i < m_reverse_cursor; ++i) + { + if (!is_piece_free(i, pieces)) continue; + // we've already added prio 7 pieces + if (piece_priority(i) == 7) continue; + num_blocks = add_blocks(i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + } + } + else if (options & rarest_first) + { + if (m_dirty) update_pieces(); + TORRENT_ASSERT(!m_dirty); + + // in time critical mode, we're only allowed to pick prio 7 + // pieces. This is why reverse mode is disabled when we're in + // time-critical mode, because all prio 7 pieces are at the front + // of the list + if ((options & reverse) && (options & time_critical_mode) == 0) + { + // it's a bit complicated in order to always prioritize + // partial pieces, and respect priorities. Every chunk + // of 4 priority levels are traversed in forward order, but otherwise + // they are traversed in reverse order + // round up to an even 4 priority boundry, to make it simpler + // to do the akward reverse traversing +#define div_round_up(n, d) (((n) + (d) - 1) / (d)) + m_priority_boundries.resize(div_round_up(m_priority_boundries.size() + , prio_factor) * prio_factor, m_priority_boundries.back()); + for (int i = m_priority_boundries.size() - 1; i >= 0; --i) + { + int prio = (i / prio_factor) * prio_factor + + prio_factor - 1 - (i % prio_factor); + + TORRENT_ASSERT(prio >= 0); + TORRENT_ASSERT(prio < int(m_priority_boundries.size())); + int start = prio == 0 ? 0 : m_priority_boundries[prio - 1]; + for (int p = start; p < m_priority_boundries[prio]; ++p) + { + if (!is_piece_free(m_pieces[p], pieces)) continue; + num_blocks = add_blocks(m_pieces[p], pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } +#undef div_round_up + } + else + { + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end(); ++i) + { + // in time critical mode, only pick prio 7 pieces + // it's safe to break here because in this mode we + // pick pieces in priority order. Once we hit a lower priority + // piece, we won't encounter any more prio 7 ones + if ((options & time_critical_mode) && piece_priority(*i) != 7) + break; + + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + } + else if (options & time_critical_mode) + { + // if we're in time-critical mode, we are only allowed to pick + // prio 7 pieces. + for (std::vector::const_iterator i = m_pieces.begin(); + i != m_pieces.end() && piece_priority(*i) == 7; ++i) + { + if (!is_piece_free(*i, pieces)) continue; + num_blocks = add_blocks(*i, pieces + , interesting_blocks, backup_blocks + , backup_blocks2, num_blocks + , prefer_whole_pieces, peer, suggested_pieces + , speed, options); + if (num_blocks <= 0) return; + } + } + else + { + + // we're not using rarest first (only for the first + // bucket, since that's where the currently downloading + // pieces are) + int start_piece = random() % m_piece_map.size(); + + int piece = start_piece; + while (num_blocks > 0) + { + bool done = false; + // skip pieces we can't pick, and suggested pieces + // since we've already picked those + while (!can_pick(piece, pieces) + || std::find(suggested_pieces.begin() + , suggested_pieces.end(), piece) + != suggested_pieces.end()) + { + ++piece; + if (piece == int(m_piece_map.size())) piece = 0; + // could not find any more pieces + if (piece == start_piece) { done = true; break; } + } + if (done) break; + + TORRENT_ASSERT(can_pick(piece, pieces)); + TORRENT_ASSERT(m_piece_map[piece].downloading == false); + + int start, end; + boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); + for (int k = start; k < end; ++k) + { + TORRENT_ASSERT(m_piece_map[k].downloading == false); + TORRENT_ASSERT(m_piece_map[k].priority(this) >= 0); + int num_blocks_in_piece = blocks_in_piece(k); + if (prefer_whole_pieces == 0 && num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + TORRENT_ASSERT(is_piece_free(k, pieces)); + interesting_blocks.push_back(piece_block(k, j)); + --num_blocks; + } + } + piece = end; + if (piece == int(m_piece_map.size())) piece = 0; + // could not find any more pieces + if (piece == start_piece) break; + } + + } + + if (num_blocks <= 0) return; + + // we might have to re-pick some backup blocks + // from full pieces, since we skipped those the + // first pass over + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + // we've already considered the non-full pieces + if (!m_piece_map[i->index].full) continue; + std::vector temp; + add_blocks_downloading(*i, pieces + , temp, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + } + +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(interesting_blocks, pieces); + verify_pick(backup_blocks, pieces); + verify_pick(backup_blocks2, pieces); +#endif + + std::vector temp; + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + if (piece_priority(i->index) == 0) continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + + // fill in with blocks requested from other peers + // as backups + bool done = false; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == i->index); + if (info.state != block_info::state_requested + || info.peer == peer) + continue; + temp.push_back(piece_block(i->index, j)); + done = true; + } + if (done) break; + } + + num_blocks = append_blocks(interesting_blocks, backup_blocks + , num_blocks); + if (num_blocks <= 0) return; + + num_blocks = append_blocks(interesting_blocks, backup_blocks2, num_blocks); + if (num_blocks <= 0) return; + + // don't double-pick anything if the peer is on parole + if (options & on_parole) return; + + // pick one random block from the first busy piece we encountered + // none of these blocks have more than one request to them + if (!temp.empty()) interesting_blocks.push_back(temp[random() % temp.size()]); + +#ifdef TORRENT_DEBUG +// make sure that we at this point have added requests to all unrequested blocks +// in all downloading pieces + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + if (!pieces[i->index]) continue; + if (piece_priority(i->index) == 0) continue; + + if ((options & time_critical_mode) && piece_priority(i->index) != 7) + continue; + + int num_blocks_in_piece = blocks_in_piece(i->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = i->info[j]; + TORRENT_ASSERT(info.piece_index == i->index); + if (info.state != block_info::state_none) continue; + std::vector::iterator k = std::find( + interesting_blocks.begin(), interesting_blocks.end() + , piece_block(i->index, j)); + if (k != interesting_blocks.end()) continue; + + fprintf(stderr, "interesting blocks:\n"); + for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) + fprintf(stderr, "(%d, %d)", k->piece_index, k->block_index); + fprintf(stderr, "\nnum_blocks: %d\n", num_blocks); + + for (std::vector::const_iterator l = m_downloads.begin() + , end(m_downloads.end()); l != end; ++l) + { + fprintf(stderr, "%d : ", l->index); + int num_blocks_in_piece = blocks_in_piece(l->index); + for (int m = 0; m < num_blocks_in_piece; ++m) + fprintf(stderr, "%d", l->info[m].state); + fprintf(stderr, "\n"); + } + + TORRENT_ASSERT(false); + } + } + + if (interesting_blocks.empty() && !(options & time_critical_mode)) + { +// print_pieces(); + for (int i = 0; i < num_pieces(); ++i) + { + if (!pieces[i]) continue; + if (m_piece_map[i].priority(this) <= 0) continue; + if (have_piece(i)) continue; + + std::vector::const_iterator k = find_dl_piece(i); + + TORRENT_ASSERT(k != m_downloads.end()); + if (k == m_downloads.end()) continue; + + // this assert is not valid for web_seeds + /* + int num_blocks_in_piece = blocks_in_piece(k->index); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = k->info[j]; + TORRENT_ASSERT(info.piece_index == k->index); + if (info.state == block_info::state_finished) continue; + TORRENT_ASSERT(info.peer != 0); + } + */ + } + } +#endif + + } + + int piece_picker::blocks_in_piece(int index) const + { + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < (int)m_piece_map.size() || m_piece_map.empty()); + if (index+1 == (int)m_piece_map.size()) + return m_blocks_in_last_piece; + else + return m_blocks_per_piece; + } + + bool piece_picker::is_piece_free(int piece, bitfield const& bitmask) const + { + TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); + return bitmask[piece] + && !m_piece_map[piece].have() + && !m_piece_map[piece].filtered(); + } + + bool piece_picker::can_pick(int piece, bitfield const& bitmask) const + { + TORRENT_ASSERT(piece >= 0 && piece < int(m_piece_map.size())); + return bitmask[piece] + && !m_piece_map[piece].have() + && !m_piece_map[piece].downloading + && !m_piece_map[piece].filtered(); + } + + void piece_picker::clear_peer(void* peer) + { + for (std::vector::iterator i = m_block_info.begin() + , end(m_block_info.end()); i != end; ++i) + { + TORRENT_ASSERT(i->peer == 0 || static_cast(i->peer)->in_use); + if (i->peer == peer) i->peer = 0; + } + } + + namespace + { + // the first bool is true if this is the only peer that has requested and downloaded + // blocks from this piece. + // the second bool is true if this is the only active peer that is requesting + // and downloading blocks from this piece. Active means having a connection. + boost::tuple requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, void* peer) + { + bool exclusive = true; + bool exclusive_active = true; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + piece_picker::block_info const& info = p.info[j]; + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == p.index); + if (info.state != piece_picker::block_info::state_none + && info.peer != peer) + { + exclusive = false; + if (info.state == piece_picker::block_info::state_requested + && info.peer != 0) + { + exclusive_active = false; + return boost::make_tuple(exclusive, exclusive_active); + } + } + } + return boost::make_tuple(exclusive, exclusive_active); + } + } + + int piece_picker::add_blocks(int piece + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, std::vector const& ignore + , piece_state_t speed, int options) const + { + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(piece < (int)m_piece_map.size()); + TORRENT_ASSERT(is_piece_free(piece, pieces)); + +// std::cout << "add_blocks(" << piece << ")" << std::endl; +// std::cout << " num_blocks " << num_blocks << std::endl; + + // ignore pieces found in the ignore list + if (std::find(ignore.begin(), ignore.end(), piece) != ignore.end()) return num_blocks; + + TORRENT_ASSERT(m_piece_map[piece].priority(this) >= 0); + if (m_piece_map[piece].downloading) + { + if (m_piece_map[piece].full) return num_blocks; + + // if we're prioritizing partials, we've already + // looked through the downloading pieces + if (options & prioritize_partials) return num_blocks; + + std::vector::const_iterator i = find_dl_piece(piece); + TORRENT_ASSERT(i != m_downloads.end()); + +// std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; + + return add_blocks_downloading(*i, pieces + , interesting_blocks, backup_blocks, backup_blocks2 + , num_blocks, prefer_whole_pieces, peer, speed, options); + } + + int num_blocks_in_piece = blocks_in_piece(piece); + + // pick a new piece + if (prefer_whole_pieces == 0) + { + if (num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + TORRENT_ASSERT(is_piece_free(piece, pieces)); + for (int j = 0; j < num_blocks_in_piece; ++j) + interesting_blocks.push_back(piece_block(piece, j)); + num_blocks -= num_blocks_in_piece; + } + else + { + int start, end; + boost::tie(start, end) = expand_piece(piece, prefer_whole_pieces, pieces); + for (int k = start; k < end; ++k) + { + TORRENT_ASSERT(m_piece_map[k].priority(this) > 0); + num_blocks_in_piece = blocks_in_piece(k); + TORRENT_ASSERT(is_piece_free(k, pieces)); + for (int j = 0; j < num_blocks_in_piece; ++j) + { + interesting_blocks.push_back(piece_block(k, j)); + --num_blocks; + } + } + } +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(interesting_blocks, pieces); +#endif + if (num_blocks <= 0) return 0; + return num_blocks; + } + + int piece_picker::add_blocks_downloading(downloading_piece const& dp + , bitfield const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , std::vector& backup_blocks2 + , int num_blocks, int prefer_whole_pieces + , void* peer, piece_state_t speed, int options) const + { + if (!pieces[dp.index]) return num_blocks; + if (m_piece_map[dp.index].filtered()) return num_blocks; + + int num_blocks_in_piece = blocks_in_piece(dp.index); + + // if all blocks have been requested (and we don't need any backup + // blocks), we might as well return immediately +/* if (int(backup_blocks2.size()) >= num_blocks + && int(backup_blocks.size()) >= num_blocks + && dp.requested + dp.writing + dp.finished == num_blocks_in_piece) + return num_blocks; +*/ + // is true if all the other pieces that are currently + // requested from this piece are from the same + // peer as 'peer'. + bool exclusive; + bool exclusive_active; + boost::tie(exclusive, exclusive_active) + = requested_from(dp, num_blocks_in_piece, peer); + + // peers on parole are only allowed to pick blocks from + // pieces that only they have downloaded/requested from + if ((options & on_parole) && !exclusive) return num_blocks; + + // we prefer whole blocks, but there are other peers + // downloading from this piece, add it as backups + if (prefer_whole_pieces > 0 && !exclusive_active) + { + if (int(backup_blocks2.size()) >= num_blocks) + return num_blocks; + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks and already requested blocks + block_info const& info = dp.info[j]; + TORRENT_ASSERT(info.piece_index == dp.index); + if (info.state != block_info::state_none) continue; + backup_blocks2.push_back(piece_block(dp.index, j)); + } + return num_blocks; + } + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks and already requested blocks + block_info const& info = dp.info[j]; + TORRENT_ASSERT(info.piece_index == dp.index); + if (info.state != block_info::state_none) continue; + + // if the piece is fast and the peer is slow, or vice versa, + // add the block as a backup. + // override this behavior if all the other blocks + // have been requested from the same peer or + // if the state of the piece is none (the + // piece will in that case change state). + if (dp.state != none && dp.state != speed + && !exclusive_active && (options & speed_affinity)) + { + if (abs(dp.state - speed) == 1) + { + // don't pick too many back-up blocks + if (int(backup_blocks.size()) >= num_blocks) return num_blocks; + backup_blocks.push_back(piece_block(dp.index, j)); + } + else + { + // don't pick too many back-up blocks + if (int(backup_blocks2.size()) >= num_blocks) return num_blocks; + backup_blocks2.push_back(piece_block(dp.index, j)); + } + continue; + } + + // this block is interesting (we don't have it + // yet). + interesting_blocks.push_back(piece_block(dp.index, j)); + // we have found a block that's free to download + num_blocks--; + // if we prefer whole pieces, continue picking from this + // piece even though we have num_blocks + if (prefer_whole_pieces > 0) continue; + TORRENT_ASSERT(num_blocks >= 0); + if (num_blocks <= 0) return num_blocks; + } + + TORRENT_ASSERT(num_blocks >= 0 || prefer_whole_pieces > 0); + + if (num_blocks <= 0) return 0; + if (options & on_parole) return num_blocks; + + if (int(backup_blocks.size()) >= num_blocks) return num_blocks; + +#if TORRENT_USE_INVARIANT_CHECKS + verify_pick(backup_blocks, pieces); +#endif + return num_blocks; + } + + std::pair piece_picker::expand_piece(int piece, int whole_pieces + , bitfield const& have) const + { + if (whole_pieces == 0) return std::make_pair(piece, piece + 1); + + int start = piece - 1; + int lower_limit = piece - whole_pieces; + if (lower_limit < -1) lower_limit = -1; + while (start > lower_limit + && can_pick(start, have)) + --start; + ++start; + TORRENT_ASSERT(start >= 0); + int end = piece + 1; + int upper_limit = start + whole_pieces; + if (upper_limit > int(m_piece_map.size())) upper_limit = int(m_piece_map.size()); + while (end < upper_limit + && can_pick(end, have)) + ++end; + return std::make_pair(start, end); + } + + bool piece_picker::is_piece_finished(int index) const + { + TORRENT_ASSERT(index < (int)m_piece_map.size()); + TORRENT_ASSERT(index >= 0); + + if (m_piece_map[index].downloading == 0) + { + TORRENT_ASSERT(find_dl_piece(index) == m_downloads.end()); + return false; + } + std::vector::const_iterator i = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT((int)i->finished <= m_blocks_per_piece); + int max_blocks = blocks_in_piece(index); + if (int(i->finished) + int(i->writing) < max_blocks) return false; + TORRENT_ASSERT(int(i->finished) + int(i->writing) == max_blocks); + +#ifdef TORRENT_DEBUG + for (int k = 0; k < max_blocks; ++k) + { + TORRENT_ASSERT(i->info[k].piece_index == index); + TORRENT_ASSERT(i->info[k].state == block_info::state_finished + || i->info[k].state == block_info::state_writing); + } +#endif + + return true; + } + + std::vector::iterator piece_picker::find_dl_piece(int index) + { +// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + downloading_piece cmp; + cmp.index = index; + std::vector::iterator i = std::lower_bound( + m_downloads.begin(), m_downloads.end(), cmp); + if (i == m_downloads.end()) return i; + if (i->index == index) return i; + return m_downloads.end(); + } + + std::vector::const_iterator piece_picker::find_dl_piece(int index) const + { +// return std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + downloading_piece cmp; + cmp.index = index; + std::vector::const_iterator i = std::lower_bound( + m_downloads.begin(), m_downloads.end(), cmp); + if (i == m_downloads.end()) return i; + if (i->index == index) return i; + return m_downloads.end(); + } + + void piece_picker::update_full(downloading_piece& dp) + { + int num_blocks = blocks_in_piece(dp.index); + m_piece_map[dp.index].full = dp.requested + dp.finished + dp.writing == num_blocks; + } + + bool piece_picker::is_requested(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_requested; + } + + bool piece_picker::is_downloaded(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_finished + || i->info[block.block_index].state == block_info::state_writing; + } + + bool piece_picker::is_finished(piece_block block) const + { +#ifdef TORRENT_USE_VALGRIND + VALGRIND_CHECK_VALUE_IS_DEFINED(block); +#endif + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + + piece_pos const& p = m_piece_map[block.piece_index]; + if (p.index == piece_pos::we_have_index) return true; + if (p.downloading == 0) return false; + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + return i->info[block.block_index].state == block_info::state_finished; + } + + bool piece_picker::mark_as_downloading(piece_block block + , void* peer, piece_state_t state) + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + TORRENT_ASSERT(state != piece_picker::none); + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + TORRENT_ASSERT(!m_piece_map[block.piece_index].have()); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = state; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.state = block_info::state_requested; + info.peer = peer; + info.num_peers = 1; + ++dp.requested; + update_full(dp); + } + else + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + if (info.state == block_info::state_writing + || info.state == block_info::state_finished) + return false; + TORRENT_ASSERT(info.state == block_info::state_none + || (info.state == block_info::state_requested + && (info.num_peers > 0))); + info.peer = peer; + if (info.state != block_info::state_requested) + { + info.state = block_info::state_requested; + ++i->requested; + update_full(*i); + } + ++info.num_peers; + if (i->state == none) i->state = state; + } + return true; + } + + int piece_picker::num_peers(piece_block block) const + { + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos const& p = m_piece_map[block.piece_index]; + if (!p.downloading) return 0; + + std::vector::const_iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + + block_info const& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + return info.num_peers; + } + + void piece_picker::get_availability(std::vector& avail) const + { + TORRENT_ASSERT(m_seeds >= 0); + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + avail.resize(m_piece_map.size()); + std::vector::iterator j = avail.begin(); + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i, ++j) + *j = i->peer_count + m_seeds; + } + + bool piece_picker::mark_as_writing(piece_block block, void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { + // if we already have this piece, just ignore this + if (have_piece(block.piece_index)) return false; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + // prio being -1 can happen if a block is requested before + // the piece priority was set to 0 + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = none; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.state = block_info::state_writing; + info.peer = peer; + info.num_peers = 0; + dp.writing = 1; + update_full(dp); +// sort_piece(m_downloads.end()-1); + } + else + { + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + info.peer = peer; + if (info.state == block_info::state_requested) --i->requested; + TORRENT_ASSERT(i->requested >= 0); + if (info.state == block_info::state_writing + || info.state == block_info::state_finished) + return false; + + ++i->writing; + info.state = block_info::state_writing; + TORRENT_ASSERT(info.piece_index == block.piece_index); + + // all other requests for this block should have been + // cancelled now + info.num_peers = 0; + + if (i->requested == 0) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } +// sort_piece(i); + } + return true; + } + + void piece_picker::write_failed(piece_block block) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + if (i == m_downloads.end()) return; + + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + TORRENT_ASSERT(info.state == block_info::state_writing); + TORRENT_ASSERT(info.num_peers == 0); + + TORRENT_ASSERT(i->writing > 0); + TORRENT_ASSERT(info.state == block_info::state_writing); + + if (info.state == block_info::state_finished) return; + if (info.state == block_info::state_writing) --i->writing; + + info.peer = 0; + + info.state = block_info::state_none; + + update_full(*i); + + if (i->finished + i->writing + i->requested == 0) + { + piece_pos& p = m_piece_map[block.piece_index]; + int prev_priority = p.priority(this); + erase_download_piece(i); + int new_priority = p.priority(this); + + if (m_dirty) return; + if (new_priority == prev_priority) return; + if (prev_priority == -1) add(block.piece_index); + else update(prev_priority, p.index); + } + else + { +// sort_piece(i); + } + } + + void piece_picker::mark_as_finished(piece_block block, void* peer) + { + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + TORRENT_ASSERT(block.piece_index >= 0); + TORRENT_ASSERT(block.block_index >= 0); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + + if (p.downloading == 0) + { + // if we already have this piece, just ignore this + if (have_piece(block.piece_index)) return; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0); + int prio = p.priority(this); + TORRENT_ASSERT(prio < int(m_priority_boundries.size()) + || m_dirty); + p.downloading = 1; + if (prio >= 0 && !m_dirty) update(prio, p.index); + + downloading_piece& dp = add_download_piece(block.piece_index); + dp.state = none; + block_info& info = dp.info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + info.peer = peer; + TORRENT_ASSERT(info.state == block_info::state_none); + TORRENT_ASSERT(info.num_peers == 0); + if (info.state != block_info::state_finished) + { + ++dp.finished; +// sort_piece(m_downloads.end() - 1); + } + info.state = block_info::state_finished; + } + else + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + if (info.state == block_info::state_finished) return; + + TORRENT_ASSERT(info.num_peers == 0); + + // peers may have been disconnected in between mark_as_writing + // and mark_as_finished. When a peer disconnects, its m_peer_info + // pointer is set to NULL. If so, preserve the previous peer + // pointer, instead of forgetting who we downloaded this block from + if (info.state != block_info::state_writing || peer != 0) + info.peer = peer; + + TORRENT_ASSERT(info.state == block_info::state_writing + || peer == 0); + TORRENT_ASSERT(i->writing >= 0); + ++i->finished; + if (info.state == block_info::state_writing) + { + --i->writing; + info.state = block_info::state_finished; + } + else + { + TORRENT_ASSERT(info.state == block_info::state_none); + info.state = block_info::state_finished; +// sort_piece(i); + } + } + } + + void piece_picker::get_downloaders(std::vector& d, int index) const + { + TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size()); + std::vector::const_iterator i = find_dl_piece(index); + TORRENT_ASSERT(i != m_downloads.end()); + TORRENT_ASSERT(i->info >= &m_block_info[0] + && i->info < &m_block_info[0] + m_block_info.size()); + + d.clear(); + for (int j = 0, end(blocks_in_piece(index)); j != end; ++j) + { + TORRENT_ASSERT(i->info[j].peer == 0 || static_cast(i->info[j].peer)->in_use); + d.push_back(i->info[j].peer); + } + } + + void* piece_picker::get_downloader(piece_block block) const + { + std::vector::const_iterator i = find_dl_piece(block.piece_index); + + if (i == m_downloads.end()) return 0; + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + + TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); + if (i->info[block.block_index].state == block_info::state_none) + return 0; + + void* peer = i->info[block.block_index].peer; + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + return peer; + } + + // this is called when a request is rejected or when + // a peer disconnects. The piece might be in any state + void piece_picker::abort_download(piece_block block, void* peer) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_PIECE_PICKER_INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); + + TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); + TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); + TORRENT_ASSERT(block.piece_index < m_piece_map.size()); + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + if (m_piece_map[block.piece_index].downloading == 0) + { + TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end()); + return; + } + + std::vector::iterator i = find_dl_piece(block.piece_index); + TORRENT_ASSERT(i != m_downloads.end()); + + block_info& info = i->info[block.block_index]; + TORRENT_ASSERT(&info >= &m_block_info[0]); + TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); + TORRENT_ASSERT(info.peer == 0 || static_cast(info.peer)->in_use); + TORRENT_ASSERT(info.piece_index == block.piece_index); + + TORRENT_ASSERT(info.state != block_info::state_none); + + if (info.state == block_info::state_finished + || info.state == block_info::state_none + || info.state == block_info::state_writing) + return; + + if (info.state == block_info::state_requested) + { + TORRENT_ASSERT(info.num_peers > 0); + if (info.num_peers > 0) --info.num_peers; + if (info.peer == peer) info.peer = 0; + + TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); + + // if there are other peers, leave the block requested + if (info.num_peers > 0) return; + + // clear the downloader of this block + info.peer = 0; + + // clear this block as being downloaded + info.state = block_info::state_none; + --i->requested; + update_full(*i); + } + + // if there are no other blocks in this piece + // that's being downloaded, remove it from the list + if (i->requested + i->finished + i->writing == 0) + { + piece_pos& p = m_piece_map[block.piece_index]; + int prev_prio = p.priority(this); + TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size()) + || m_dirty); + erase_download_piece(i); + if (!m_dirty) + { + int prio = p.priority(this); + if (prev_prio == -1 && prio >= 0) add(block.piece_index); + else if (prev_prio >= 0) update(prev_prio, p.index); + } + + TORRENT_ASSERT(find_dl_piece(block.piece_index) == m_downloads.end()); + } + else if (i->requested == 0) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } + } + + int piece_picker::unverified_blocks() const + { + int counter = 0; + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end(); ++i) + { + counter += (int)i->finished; + } + return counter; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/policy.cpp b/apps/Launcher/ext/libtorrent/src/policy.cpp new file mode 100644 index 0000000000..396a9eb8a4 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/policy.cpp @@ -0,0 +1,2029 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/extensions.hpp" + +#ifdef TORRENT_DEBUG +#include "libtorrent/bt_peer_connection.hpp" +#endif + +namespace +{ + using namespace libtorrent; + + struct match_peer_endpoint + { + match_peer_endpoint(tcp::endpoint const& ep) + : m_ep(ep) + {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->address() == m_ep.address() && p->port == m_ep.port(); + } + + tcp::endpoint const& m_ep; + }; + +#if TORRENT_USE_ASSERTS + struct match_peer_connection + { + match_peer_connection(peer_connection const& c) : m_conn(c) {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->connection == &m_conn; + } + + peer_connection const& m_conn; + }; + + struct match_peer_connection_or_endpoint + { + match_peer_connection_or_endpoint(peer_connection const& c) : m_conn(c) {} + + bool operator()(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + return p->connection == &m_conn + || (p->ip() == m_conn.remote() + && p->connectable); + } + + peer_connection const& m_conn; + }; +#endif + +} + +namespace libtorrent +{ + + void apply_mask(boost::uint8_t* b, boost::uint8_t const* mask, int size) + { + for (int i = 0; i < size; ++i) + { + *b &= *mask; + ++b; + ++mask; + } + } + + // 1. if the IP addresses are identical, hash the ports in 16 bit network-order + // binary representation, ordered lowest first. + // 2. if the IPs are in the same /24, hash the IPs ordered, lowest first. + // 3. if the IPs are in the ame /16, mask the IPs by 0xffffff55, hash them + // ordered, lowest first. + // 4. if IPs are not in the same /16, mask the IPs by 0xffff5555, hash them + // ordered, lowest first. + // + // * for IPv6 peers, just use the first 64 bits and widen the masks. + // like this: 0xffff5555 -> 0xffffffff55555555 + // the lower 64 bits are always unmasked + // + // * for IPv6 addresses, compare /32 and /48 instead of /16 and /24 + // + // * the two IP addresses that are used to calculate the rank must + // always be of the same address family + // + // * all IP addresses are in network byte order when hashed + boost::uint32_t peer_priority(tcp::endpoint e1, tcp::endpoint e2) + { + TORRENT_ASSERT(e1.address().is_v4() == e2.address().is_v4()); + + using std::swap; + + // this is the crc32c (Castagnoli) polynomial + boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc; + + if (e1.address() == e2.address()) + { + if (e1.port() > e2.port()) + swap(e1, e2); + boost::uint16_t p[2]; + p[0] = htons(e1.port()); + p[1] = htons(e2.port()); + crc.process_bytes((char const*)&p[0], 4); + } +#if TORRENT_USE_IPV6 + else if (e1.address().is_v6()) + { + const static boost::uint8_t v6mask[][8] = { + { 0xff, 0xff, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + + if (e2 < e1) swap(e1, e2); + address_v6::bytes_type b1 = e1.address().to_v6().to_bytes(); + address_v6::bytes_type b2 = e2.address().to_v6().to_bytes(); + int mask = memcmp(&b1[0], &b2[0], 4) ? 0 + : memcmp(&b1[0], &b2[0], 6) ? 1 : 2; + apply_mask(&b1[0], v6mask[mask], 8); + apply_mask(&b2[0], v6mask[mask], 8); + + crc.process_bytes((char const*)&b1[0], 16); + crc.process_bytes((char const*)&b2[0], 16); + } +#endif + else + { + const static boost::uint8_t v4mask[][4] = { + { 0xff, 0xff, 0x55, 0x55 }, + { 0xff, 0xff, 0xff, 0x55 }, + { 0xff, 0xff, 0xff, 0xff } + }; + + if (e2 < e1) swap(e1, e2); + address_v4::bytes_type b1 = e1.address().to_v4().to_bytes(); + address_v4::bytes_type b2 = e2.address().to_v4().to_bytes(); + int mask = memcmp(&b1[0], &b2[0], 2) ? 0 + : memcmp(&b1[0], &b2[0], 3) ? 1 : 2; + apply_mask(&b1[0], v4mask[mask], 4); + apply_mask(&b2[0], v4mask[mask], 4); + + crc.process_bytes((char const*)&b1[0], 4); + crc.process_bytes((char const*)&b2[0], 4); + } + + return crc.checksum(); + } + + // returns the rank of a peer's source. We have an affinity + // to connecting to peers with higher rank. This is to avoid + // problems when our peer list is diluted by stale peers from + // the resume data for instance + int source_rank(int source_bitmask) + { + int ret = 0; + if (source_bitmask & peer_info::tracker) ret |= 1 << 5; + if (source_bitmask & peer_info::lsd) ret |= 1 << 4; + if (source_bitmask & peer_info::dht) ret |= 1 << 3; + if (source_bitmask & peer_info::pex) ret |= 1 << 2; + return ret; + } + + // the case where ignore_peer is motivated is if two peers + // have only one piece that we don't have, and it's the + // same piece for both peers. Then they might get into an + // infinite loop, fighting to request the same blocks. + void request_a_block(torrent& t, peer_connection& c) + { + if (t.is_seed()) return; + if (c.no_download()) return; + if (t.upload_mode()) return; + if (c.is_disconnecting()) return; + + // don't request pieces before we have the metadata + if (!t.valid_metadata()) return; + + // don't request pieces before the peer is properly + // initialized after we have the metadata + if (!t.are_files_checked()) return; + + TORRENT_ASSERT(t.valid_metadata()); + TORRENT_ASSERT(c.peer_info_struct() != 0 || c.type() != peer_connection::bittorrent_connection); + + bool time_critical_mode = t.num_time_critical_pieces() > 0; + + int desired_queue_size = c.desired_queue_size(); + + // in time critical mode, only have 1 outstanding request at a time + // via normal requests + if (time_critical_mode) + desired_queue_size = (std::min)(1, desired_queue_size); + + int num_requests = desired_queue_size + - (int)c.download_queue().size() + - (int)c.request_queue().size(); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** PIECE_PICKER [ req: %d engame: %d ]", num_requests, c.endgame()); +#endif + TORRENT_ASSERT(c.desired_queue_size() > 0); + // if our request queue is already full, we + // don't have to make any new requests yet + if (num_requests <= 0) return; + + piece_picker& p = t.picker(); + std::vector interesting_pieces; + interesting_pieces.reserve(100); + + int prefer_whole_pieces = c.prefer_whole_pieces(); + + if (prefer_whole_pieces == 0 && !time_critical_mode) + { + prefer_whole_pieces = c.statistics().download_payload_rate() + * t.settings().whole_pieces_threshold + > t.torrent_file().piece_length() ? 1 : 0; + } + + // if we prefer whole pieces, the piece picker will pick at least + // the number of blocks we want, but it will try to make the picked + // blocks be from whole pieces, possibly by returning more blocks + // than we requested. +#ifdef TORRENT_DEBUG + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); +#endif + + aux::session_impl& ses = t.session(); + + std::vector const& dq = c.download_queue(); + std::vector const& rq = c.request_queue(); + + std::vector const& suggested = c.suggested_pieces(); + bitfield const* bits = &c.get_bitfield(); + bitfield fast_mask; + + if (c.has_peer_choked()) + { + // if we are choked we can only pick pieces from the + // allowed fast set. The allowed fast set is sorted + // in ascending priority order + std::vector const& allowed_fast = c.allowed_fast(); + + // build a bitmask with only the allowed pieces in it + fast_mask.resize(c.get_bitfield().size(), false); + for (std::vector::const_iterator i = allowed_fast.begin() + , end(allowed_fast.end()); i != end; ++i) + if ((*bits)[*i]) fast_mask.set_bit(*i); + bits = &fast_mask; + } + + piece_picker::piece_state_t state; + peer_connection::peer_speed_t speed = c.peer_speed(); + if (speed == peer_connection::fast) state = piece_picker::fast; + else if (speed == peer_connection::medium) state = piece_picker::medium; + else state = piece_picker::slow; + + // picks the interesting pieces from this peer + // the integer is the number of pieces that + // should be guaranteed to be available for download + // (if num_requests is too big, too many pieces are + // picked and cpu-time is wasted) + // the last argument is if we should prefer whole pieces + // for this peer. If we're downloading one piece in 20 seconds + // then use this mode. + p.pick_pieces(*bits, interesting_pieces + , num_requests, prefer_whole_pieces, c.peer_info_struct() + , state, c.picker_options(), suggested, t.num_peers()); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** PIECE_PICKER [ prefer_whole: %d picked: %d ]" + , prefer_whole_pieces, int(interesting_pieces.size())); +#endif + + // if the number of pieces we have + the number of pieces + // we're requesting from is less than the number of pieces + // in the torrent, there are still some unrequested pieces + // and we're not strictly speaking in end-game mode yet + // also, if we already have at least one outstanding + // request, we shouldn't pick any busy pieces either + // in time critical mode, it's OK to request busy blocks + bool dont_pick_busy_blocks = ((ses.m_settings.strict_end_game_mode + && p.num_downloading_pieces() < p.num_want_left()) + || dq.size() + rq.size() > 0) + && !time_critical_mode; + + // this is filled with an interesting piece + // that some other peer is currently downloading + piece_block busy_block = piece_block::invalid; + + for (std::vector::iterator i = interesting_pieces.begin(); + i != interesting_pieces.end(); ++i) + { +#ifdef TORRENT_STATS + ++ses.m_piece_picker_blocks; +#endif + + if (prefer_whole_pieces == 0 && num_requests <= 0) break; + + if (time_critical_mode && p.piece_priority(i->piece_index) != 7) + { + // assume the subsequent pieces are not prio 7 and + // be done + break; + } + + int num_block_requests = p.num_peers(*i); + if (num_block_requests > 0) + { + // have we picked enough pieces? + if (num_requests <= 0) break; + + // this block is busy. This means all the following blocks + // in the interesting_pieces list are busy as well, we might + // as well just exit the loop + if (dont_pick_busy_blocks) break; + + TORRENT_ASSERT(p.num_peers(*i) > 0); + busy_block = *i; + continue; + } + + TORRENT_ASSERT(p.num_peers(*i) == 0); + + // don't request pieces we already have in our request queue + // This happens when pieces time out or the peer sends us + // pieces we didn't request. Those aren't marked in the + // piece picker, but we still keep track of them in the + // download queue + if (std::find_if(dq.begin(), dq.end(), has_block(*i)) != dq.end() + || std::find_if(rq.begin(), rq.end(), has_block(*i)) != rq.end()) + { +#ifdef TORRENT_DEBUG + std::vector::const_iterator j + = std::find_if(dq.begin(), dq.end(), has_block(*i)); + if (j != dq.end()) TORRENT_ASSERT(j->timed_out || j->not_wanted); +#endif + continue; + } + + // ok, we found a piece that's not being downloaded + // by somebody else. request it from this peer + // and return + if (!c.add_request(*i, 0)) continue; + TORRENT_ASSERT(p.num_peers(*i) == 1); + TORRENT_ASSERT(p.is_requested(*i)); + num_requests--; + } + + // we have picked as many blocks as we should + // we're done! + if (num_requests <= 0) + { + // since we could pick as many blocks as we + // requested without having to resort to picking + // busy ones, we're not in end-game mode + c.set_endgame(false); + return; + } + + // we did not pick as many pieces as we wanted, because + // there aren't enough. This means we're in end-game mode + // as long as we have at least one request outstanding, + // we shouldn't pick another piece + // if we are attempting to download 'allowed' pieces + // and can't find any, that doesn't count as end-game + if (!c.has_peer_choked()) + c.set_endgame(true); + + // if we don't have any potential busy blocks to request + // or if we already have outstanding requests, don't + // pick a busy piece + if (busy_block == piece_block::invalid + || dq.size() + rq.size() > 0) + { + return; + } + +#ifdef TORRENT_STATS + ++ses.m_end_game_piece_picker_blocks; +#endif + +#ifdef TORRENT_DEBUG + piece_picker::downloading_piece st; + p.piece_info(busy_block.piece_index, st); + TORRENT_ASSERT(st.requested + st.finished + st.writing + == p.blocks_in_piece(busy_block.piece_index)); +#endif + TORRENT_ASSERT(p.is_requested(busy_block)); + TORRENT_ASSERT(!p.is_downloaded(busy_block)); + TORRENT_ASSERT(!p.is_finished(busy_block)); + TORRENT_ASSERT(p.num_peers(busy_block) > 0); + + c.add_request(busy_block, peer_connection::req_busy); + } + + policy::policy(torrent* t) + : m_torrent(t) + , m_locked_peer(NULL) + , m_round_robin(0) + , m_num_connect_candidates(0) + , m_num_seeds(0) + , m_finished(false) + { TORRENT_ASSERT(t); } + + void policy::clear_peer_prio() + { + for (peers_t::iterator i = m_peers.begin() + , end(m_peers.end()); i != end; ++i) + (*i)->peer_rank = 0; + } + + // disconnects and removes all peers that are now filtered + void policy::ip_filter_updated() + { + INVARIANT_CHECK; + + aux::session_impl& ses = m_torrent->session(); + if (!m_torrent->apply_ip_filter()) return; + + for (iterator i = m_peers.begin(); i != m_peers.end();) + { + if ((ses.m_ip_filter.access((*i)->address()) & ip_filter::blocked) == 0) + { + ++i; + continue; + } + + if (*i == m_locked_peer) + { + ++i; + continue; + } + + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , (*i)->address(), peer_blocked_alert::ip_filter)); + + int current = i - m_peers.begin(); + TORRENT_ASSERT(current >= 0); + TORRENT_ASSERT(m_peers.size() > 0); + TORRENT_ASSERT(i != m_peers.end()); + + if ((*i)->connection) + { + // disconnecting the peer here may also delete the + // peer_info_struct. If that is the case, just continue + int count = m_peers.size(); + peer_connection* p = (*i)->connection; + + p->disconnect(errors::banned_by_ip_filter); + // what *i refers to has changed, i.e. cur was deleted + if (int(m_peers.size()) < count) + { + i = m_peers.begin() + current; + continue; + } + TORRENT_ASSERT((*i)->connection == 0 + || (*i)->connection->peer_info_struct() == 0); + } + + erase_peer(i); + i = m_peers.begin() + current; + } + } + + void policy::erase_peer(policy::peer* p) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + + std::pair range = find_peers(p->address()); + iterator iter = std::find_if(range.first, range.second, match_peer_endpoint(p->ip())); + if (iter == range.second) return; + erase_peer(iter); + } + + // any peer that is erased from m_peers will be + // erased through this function. This way we can make + // sure that any references to the peer are removed + // as well, such as in the piece picker. + void policy::erase_peer(iterator i) + { + INVARIANT_CHECK; + TORRENT_ASSERT(i != m_peers.end()); + TORRENT_ASSERT(m_locked_peer != *i); + + if (m_torrent->has_picker()) + m_torrent->picker().clear_peer(*i); + if ((*i)->seed) --m_num_seeds; + if (is_connect_candidate(**i, m_finished)) + { + TORRENT_ASSERT(m_num_connect_candidates > 0); + --m_num_connect_candidates; + } + TORRENT_ASSERT(m_num_connect_candidates < int(m_peers.size())); + if (m_round_robin > i - m_peers.begin()) --m_round_robin; + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT((*i)->in_use); + (*i)->in_use = false; +#endif + +#if TORRENT_USE_IPV6 + if ((*i)->is_v6_addr) + { + TORRENT_ASSERT(m_torrent->session().m_ipv6_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_ipv6_peer_pool.destroy( + static_cast(*i)); + } + else +#endif +#if TORRENT_USE_I2P + if ((*i)->is_i2p_addr) + { + TORRENT_ASSERT(m_torrent->session().m_i2p_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_i2p_peer_pool.destroy( + static_cast(*i)); + } + else +#endif + { + TORRENT_ASSERT(m_torrent->session().m_ipv4_peer_pool.is_from( + static_cast(*i))); + m_torrent->session().m_ipv4_peer_pool.destroy( + static_cast(*i)); + } + m_peers.erase(i); + } + + bool policy::should_erase_immediately(peer const& p) const + { + TORRENT_ASSERT(p.in_use); + if (&p == m_locked_peer) return false; + return p.source == peer_info::resume_data; + } + + bool policy::is_erase_candidate(peer const& pe, bool finished) const + { + TORRENT_ASSERT(pe.in_use); + if (&pe == m_locked_peer) return false; + if (pe.connection) return false; + if (is_connect_candidate(pe, finished)) return false; + + return (pe.failcount > 0) + || (pe.source == peer_info::resume_data); + } + + bool policy::is_force_erase_candidate(peer const& pe) const + { + TORRENT_ASSERT(pe.in_use); + if (&pe == m_locked_peer) return false; + return pe.connection == 0; + } + + void policy::erase_peers(int flags) + { + INVARIANT_CHECK; + + int max_peerlist_size = m_torrent->is_paused() + ? m_torrent->settings().max_paused_peerlist_size + : m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size == 0 || m_peers.empty()) return; + + int erase_candidate = -1; + int force_erase_candidate = -1; + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + + int round_robin = random() % m_peers.size(); + + int low_watermark = max_peerlist_size * 95 / 100; + if (low_watermark == max_peerlist_size) --low_watermark; + + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; --iterations) + { + if (int(m_peers.size()) < low_watermark) + break; + + if (round_robin == int(m_peers.size())) round_robin = 0; + + peer& pe = *m_peers[round_robin]; + TORRENT_ASSERT(pe.in_use); + int current = round_robin; + + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + if (force_erase_candidate > current) --force_erase_candidate; + TORRENT_ASSERT(current >= 0 && current < int(m_peers.size())); + erase_peer(m_peers.begin() + current); + continue; + } + else + { + erase_candidate = current; + } + } + if (is_force_erase_candidate(pe) + && (force_erase_candidate == -1 + || !compare_peer_erase(*m_peers[force_erase_candidate], pe))) + { + force_erase_candidate = current; + } + + ++round_robin; + } + + if (erase_candidate > -1) + { + TORRENT_ASSERT(erase_candidate >= 0 && erase_candidate < int(m_peers.size())); + erase_peer(m_peers.begin() + erase_candidate); + } + else if ((flags & force_erase) && force_erase_candidate > -1) + { + TORRENT_ASSERT(force_erase_candidate >= 0 && force_erase_candidate < int(m_peers.size())); + erase_peer(m_peers.begin() + force_erase_candidate); + } + } + + void policy::ban_peer(policy::peer* p) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + + if (!m_torrent->settings().ban_web_seeds && p->web_seed) + return; + + if (is_connect_candidate(*p, m_finished)) + --m_num_connect_candidates; + +#ifdef TORRENT_STATS + aux::session_impl& ses = m_torrent->session(); + ++ses.m_num_banned_peers; +#endif + + p->banned = true; + TORRENT_ASSERT(!is_connect_candidate(*p, m_finished)); + } + + void policy::set_connection(policy::peer* p, peer_connection* c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + TORRENT_ASSERT(c); + + const bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->connection = c; + if (was_conn_cand) --m_num_connect_candidates; + } + + void policy::set_failcount(policy::peer* p, int f) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(p->in_use); + const bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->failcount = f; + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + if (was_conn_cand) --m_num_connect_candidates; + else ++m_num_connect_candidates; + } + } + + bool policy::is_connect_candidate(peer const& p, bool finished) const + { + TORRENT_ASSERT(p.in_use); + if (p.connection + || p.banned + || p.web_seed + || !p.connectable + || (p.seed && finished) + || int(p.failcount) >= m_torrent->settings().max_failcount) + return false; + + aux::session_impl const& ses = m_torrent->session(); + if (ses.m_port_filter.access(p.port) & port_filter::blocked) + return false; + + // only apply this to peers we've only heard + // about from the DHT + if (ses.m_settings.no_connect_privileged_ports + && p.port < 1024 + && p.source == peer_info::dht) + return false; + + return true; + } + + policy::iterator policy::find_connect_candidate(int session_time) + { + INVARIANT_CHECK; + + int candidate = -1; + int erase_candidate = -1; + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + + int min_reconnect_time = m_torrent->settings().min_reconnect_time; + external_ip const& external = m_torrent->session().external_address(); + int external_port = m_torrent->session().listen_port(); + + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + +#ifndef TORRENT_DISABLE_DHT + bool pinged = false; +#endif + + int max_peerlist_size = m_torrent->is_paused() + ?m_torrent->settings().max_paused_peerlist_size + :m_torrent->settings().max_peerlist_size; + + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; --iterations) + { + if (m_round_robin >= int(m_peers.size())) m_round_robin = 0; + + peer& pe = *m_peers[m_round_robin]; + TORRENT_ASSERT(pe.in_use); + int current = m_round_robin; + +#ifndef TORRENT_DISABLE_DHT + // try to send a DHT ping to this peer + // as well, to figure out if it supports + // DHT (uTorrent and BitComet doesn't + // advertise support) + if (!pinged && !pe.added_to_dht) + { + udp::endpoint node(pe.address(), pe.port); + m_torrent->session().add_dht_node(node); + pe.added_to_dht = true; + pinged = true; + } +#endif + // if the number of peers is growing large + // we need to start weeding. + + if (int(m_peers.size()) >= max_peerlist_size * 0.95 + && max_peerlist_size > 0) + { + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + if (candidate > current) --candidate; + erase_peer(m_peers.begin() + current); + continue; + } + else + { + erase_candidate = current; + } + } + } + + ++m_round_robin; + + if (!is_connect_candidate(pe, m_finished)) continue; + + // compare peer returns true if lhs is better than rhs. In this + // case, it returns true if the current candidate is better than + // pe, which is the peer m_round_robin points to. If it is, just + // keep looking. + if (candidate != -1 + && compare_peer(*m_peers[candidate], pe, external, external_port)) continue; + + if (pe.last_connected + && session_time - pe.last_connected < + (int(pe.failcount) + 1) * min_reconnect_time) + continue; + + candidate = current; + } + + if (erase_candidate > -1) + { + if (candidate > erase_candidate) --candidate; + erase_peer(m_peers.begin() + erase_candidate); + } + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + if (candidate != -1) + { + (*m_torrent->session().m_logger) << time_now_string() + << " *** FOUND CONNECTION CANDIDATE [" + " ip: " << m_peers[candidate]->ip() << + " d: " << cidr_distance(external.external_address(m_peers[candidate]->address()), m_peers[candidate]->address()) << + " rank: " << m_peers[candidate]->rank(external, external_port) << + " external: " << external.external_address(m_peers[candidate]->address()) << + " t: " << (session_time - m_peers[candidate]->last_connected) << + " ]\n"; + } +#endif + + if (candidate == -1) return m_peers.end(); + return m_peers.begin() + candidate; + } + + bool policy::new_connection(peer_connection& c, int session_time) + { + TORRENT_ASSERT(!c.is_outgoing()); + + INVARIANT_CHECK; + + // if the connection comes from the tracker, + // it's probably just a NAT-check. Ignore the + // num connections constraint then. + + // TODO: only allow _one_ connection to use this + // override at a time + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); + TORRENT_ASSERT(!m_torrent->is_paused()); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (c.remote().address() == m_torrent->current_tracker().address()) + { + m_torrent->debug_log("overriding connection limit for tracker NAT-check"); + } +#endif + + iterator iter; + peer* i = 0; + + bool found = false; + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + tcp::endpoint remote = c.remote(); + std::pair range = find_peers(remote.address()); + iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); + + if (iter != range.second) + { + TORRENT_ASSERT((*iter)->in_use); + found = true; + } + } + else + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , c.remote().address(), peer_address_compare() + ); + + if (iter != m_peers.end() && (*iter)->address() == c.remote().address()) + { + TORRENT_ASSERT((*iter)->in_use); + found = true; + } + } + + // make sure the iterator we got is properly sorted relative + // to the connection's address +// TORRENT_ASSERT(m_peers.empty() +// || (iter == m_peers.end() && (*(iter-1))->address() < c.remote().address()) +// || (iter != m_peers.end() && c.remote().address() < (*iter)->address()) +// || (iter != m_peers.end() && iter != m_peers.begin() && (*(iter-1))->address() < c.remote().address())); + +#if !defined TORRENT_DISABLE_GEO_IP || TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + aux::session_impl& ses = m_torrent->session(); +#endif + + if (found) + { + i = *iter; + TORRENT_ASSERT(i->in_use); + TORRENT_ASSERT(i->connection != &c); + TORRENT_ASSERT(i->address() == c.remote().address()); + +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER [ this: \"%s\" that: \"%s\" ]" + , print_address(c.remote().address()).c_str() + , print_address(i->address()).c_str()); +#endif + if (i->banned) + { + c.disconnect(errors::peer_banned); + return false; + } + + if (i->connection != 0) + { + boost::shared_ptr other_socket + = i->connection->get_socket(); + boost::shared_ptr this_socket + = c.get_socket(); + + error_code ec1; + error_code ec2; + bool self_connection = + other_socket->remote_endpoint(ec2) == this_socket->local_endpoint(ec1) + || other_socket->local_endpoint(ec2) == this_socket->remote_endpoint(ec1); + + if (ec1) + { + c.disconnect(ec1); + return false; + } + + if (self_connection) + { + c.disconnect(errors::self_connection, 1); + i->connection->disconnect(errors::self_connection, 1); + TORRENT_ASSERT(i->connection == 0); + return false; + } + + TORRENT_ASSERT(i->connection != &c); + // the new connection is a local (outgoing) connection + // or the current one is already connected + if (ec2) + { + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(ec2); + TORRENT_ASSERT(i->connection == 0); + m_locked_peer = NULL; + } + else if (i->connection->is_outgoing() == c.is_outgoing()) + { + // if the other end connected to us both times, just drop + // the second one. Or if we made both connections. + c.disconnect(errors::duplicate_peer_id); + return false; + } + else + { + // at this point, we need to disconnect either + // i->connection or c. In order for both this client + // and the client on the other end to decide to + // disconnect the same one, we need a consistent rule to + // select which one. + + bool outgoing1 = c.is_outgoing(); + + // for this, we compare our endpoints (IP and port) + // and whoever has the lower IP,port should be the + // one keeping its outgoing connection. Since outgoing + // ports are selected at random by the OS, we need + // to be careful to only look at the target end of a + // connection for the endpoint. + + int our_port = outgoing1 ? other_socket->local_endpoint(ec1).port() : this_socket->local_endpoint(ec1).port(); + int other_port = outgoing1 ? this_socket->remote_endpoint(ec1).port() : other_socket->remote_endpoint(ec1).port(); + + if (our_port < other_port) + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port); +#endif + + // we should keep our outgoing connection + if (!outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port); +#endif + // they should keep their outgoing connection + if (outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = i; + i->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + } + } + } + + if (is_connect_candidate(*i, m_finished)) + { + m_num_connect_candidates--; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + } + else + { + // we don't have any info about this peer. + // add a new entry + error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); + + // max peerlist size 0 means infinite + int max_peerlist_size = m_torrent->is_paused() + ? m_torrent->settings().max_paused_peerlist_size + : m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size + && int(m_peers.size()) >= max_peerlist_size) + { + // this may invalidate our iterator! + erase_peers(force_erase); + if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size) + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + (*m_torrent->session().m_logger) << time_now_string() + << " *** TOO MANY CONNECTIONS [" + " torrent: " << m_torrent->name() << + " torrent peers: " << m_torrent->num_peers() << + " torrent limit: " << m_torrent->max_connections() << + " global peers: " << ses.num_connections() << + " global limit: " << ses.settings().connections_limit << + " global list peers " << int(m_peers.size()) << + " global list limit: " << m_torrent->settings().max_peerlist_size << + " ]\n"; +#endif + c.disconnect(errors::too_many_connections); + return false; + } + // restore it + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , c.remote().address(), peer_address_compare() + ); + } + +#if TORRENT_USE_IPV6 + bool is_v6 = c.remote().address().is_v6(); +#endif + peer* p = +#if TORRENT_USE_IPV6 + is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() : +#endif + (peer*)m_torrent->session().m_ipv4_peer_pool.malloc(); + if (p == 0) return false; + +#if TORRENT_USE_IPV6 + if (is_v6) + m_torrent->session().m_ipv6_peer_pool.set_next_size(500); + else +#endif + m_torrent->session().m_ipv4_peer_pool.set_next_size(500); + +#if TORRENT_USE_IPV6 + if (is_v6) + new (p) ipv6_peer(c.remote(), false, 0); + else +#endif + new (p) ipv4_peer(c.remote(), false, 0); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + iter = m_peers.insert(iter, p); + + if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; + + i = *iter; +#ifndef TORRENT_DISABLE_GEO_IP + int as = ses.as_for_ip(c.remote().address()); +#ifdef TORRENT_DEBUG + i->inet_as_num = as; +#endif + i->inet_as = ses.lookup_as(as); +#endif + i->source = peer_info::incoming; + } + + TORRENT_ASSERT(i); + c.set_peer_info(i); + TORRENT_ASSERT(i->connection == 0); + c.add_stat(size_type(i->prev_amount_download) << 10, size_type(i->prev_amount_upload) << 10); + + // restore transfer rate limits + int rate_limit; + rate_limit = i->upload_rate_limit; + if (rate_limit) c.set_upload_limit(rate_limit); + rate_limit = i->download_rate_limit; + if (rate_limit) c.set_download_limit(rate_limit); + + i->prev_amount_download = 0; + i->prev_amount_upload = 0; + i->connection = &c; + TORRENT_ASSERT(i->connection); + if (!c.fast_reconnect()) + i->last_connected = session_time; + + // this cannot be a connect candidate anymore, since i->connection is set + TORRENT_ASSERT(!is_connect_candidate(*i, m_finished)); + TORRENT_ASSERT(has_connection(&c)); + m_torrent->state_updated(); + return true; + } + + bool policy::update_peer_port(int port, policy::peer* p, int src) + { + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(p->connection); + TORRENT_ASSERT(p->in_use); + + INVARIANT_CHECK; + + if (p->port == port) return true; + + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + tcp::endpoint remote(p->address(), port); + std::pair range = find_peers(remote.address()); + iterator i = std::find_if(range.first, range.second + , match_peer_endpoint(remote)); + if (i != range.second) + { + policy::peer& pp = **i; + TORRENT_ASSERT(pp.in_use); + if (pp.connection) + { + bool was_conn_cand = is_connect_candidate(pp, m_finished); + // if we already have an entry with this + // new endpoint, disconnect this one + pp.connectable = true; + pp.source |= src; + if (!was_conn_cand && is_connect_candidate(pp, m_finished)) + ++m_num_connect_candidates; + // calling disconnect() on a peer, may actually end + // up "garbage collecting" its policy::peer entry + // as well, if it's considered useless (which this specific) + // case will, since it was an incoming peer that just disconnected + // and we allow multiple connections per IP. Because of that, + // we need to make sure we don't let it do that, by unlinking + // the peer_connection from the policy::peer first. + p->connection->set_peer_info(0); + TORRENT_ASSERT(m_locked_peer == NULL); + m_locked_peer = p; + p->connection->disconnect(errors::duplicate_peer_id); + m_locked_peer = NULL; + erase_peer(p); + return false; + } + erase_peer(i); + } + } +#ifdef TORRENT_DEBUG + else + { +#if TORRENT_USE_I2P + if (!p->is_i2p_addr) +#endif + { + std::pair range = find_peers(p->address()); + TORRENT_ASSERT(range.second - range.first == 1); + } + } +#endif + + bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->port = port; + p->source |= src; + p->connectable = true; + + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + m_num_connect_candidates += was_conn_cand ? -1 : 1; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + return true; + } + + // it's important that we don't dereference + // p here, since it is allowed to be a dangling + // pointer. see smart_ban.cpp + bool policy::has_peer(policy::peer const* p) const + { + TORRENT_ASSERT(p->in_use); + // find p in m_peers + for (const_iterator i = m_peers.begin() + , end(m_peers.end()); i != end; ++i) + { + if (*i == p) return true; + } + return false; + } + + void policy::set_seed(policy::peer* p, bool s) + { + if (p == 0) return; + TORRENT_ASSERT(p->in_use); + if (p->seed == s) return; + bool was_conn_cand = is_connect_candidate(*p, m_finished); + p->seed = s; + if (was_conn_cand && !is_connect_candidate(*p, m_finished)) + { + --m_num_connect_candidates; + TORRENT_ASSERT(m_num_connect_candidates >= 0); + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + + if (p->web_seed) return; + if (s) ++m_num_seeds; + else --m_num_seeds; + TORRENT_ASSERT(m_num_seeds >= 0); + TORRENT_ASSERT(m_num_seeds <= int(m_peers.size())); + } + + bool policy::insert_peer(policy::peer* p, iterator iter, int flags) + { + TORRENT_ASSERT(p); + TORRENT_ASSERT(p->in_use); + + int max_peerlist_size = m_torrent->is_paused() + ?m_torrent->settings().max_paused_peerlist_size + :m_torrent->settings().max_peerlist_size; + + if (max_peerlist_size + && int(m_peers.size()) >= max_peerlist_size) + { + if (p->source == peer_info::resume_data) return false; + + erase_peers(); + if (int(m_peers.size()) >= max_peerlist_size) + return 0; + + // since some peers were removed, we need to + // update the iterator to make it valid again +#if TORRENT_USE_I2P + if (p->is_i2p_addr) + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , p->dest(), peer_address_compare()); + } + else +#endif + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , p->address(), peer_address_compare()); + } + + iter = m_peers.insert(iter, p); + + if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (flags & 0x01) p->pe_support = true; +#endif + if (flags & 0x02) + { + p->seed = true; + ++m_num_seeds; + } + if (flags & 0x04) + p->supports_utp = true; + if (flags & 0x08) + p->supports_holepunch = true; + +#ifndef TORRENT_DISABLE_GEO_IP + int as = m_torrent->session().as_for_ip(p->address()); +#ifdef TORRENT_DEBUG + p->inet_as_num = as; +#endif + p->inet_as = m_torrent->session().lookup_as(as); +#endif + if (is_connect_candidate(*p, m_finished)) + ++m_num_connect_candidates; + + m_torrent->state_updated(); + + return true; + } + + void policy::update_peer(policy::peer* p, int src, int flags + , tcp::endpoint const& remote, char const* destination) + { + bool was_conn_cand = is_connect_candidate(*p, m_finished); + + TORRENT_ASSERT(p->in_use); + p->connectable = true; + + TORRENT_ASSERT(p->address() == remote.address()); + p->port = remote.port(); + p->source |= src; + + // if this peer has failed before, decrease the + // counter to allow it another try, since somebody + // else is appearantly able to connect to it + // only trust this if it comes from the tracker + if (p->failcount > 0 && src == peer_info::tracker) + --p->failcount; + + // if we're connected to this peer + // we already know if it's a seed or not + // so we don't have to trust this source + if ((flags & 0x02) && !p->connection) + { + if (!p->seed) ++m_num_seeds; + p->seed = true; + } + if (flags & 0x04) + p->supports_utp = true; + if (flags & 0x08) + p->supports_holepunch = true; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (p->connection) + { + // this means we're already connected + // to this peer. don't connect to + // it again. + + error_code ec; + char hex_pid[41]; + to_hex((char*)&p->connection->pid()[0], 20, hex_pid); + char msg[200]; + snprintf(msg, 200, "already connected to peer: %s %s" + , print_endpoint(remote).c_str(), hex_pid); + m_torrent->debug_log(msg); + + TORRENT_ASSERT(p->connection->associated_torrent().lock().get() == m_torrent); + } +#endif + + if (was_conn_cand != is_connect_candidate(*p, m_finished)) + { + m_num_connect_candidates += was_conn_cand ? -1 : 1; + if (m_num_connect_candidates < 0) m_num_connect_candidates = 0; + } + } + +#if TORRENT_USE_I2P + policy::peer* policy::add_i2p_peer(char const* destination, int src, char flags) + { + INVARIANT_CHECK; + + bool found = false; + iterator iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , destination, peer_address_compare() + ); + + if (iter != m_peers.end() && strcmp((*iter)->dest(), destination) == 0) + found = true; + + peer* p = 0; + + if (!found) + { + // we don't have any info about this peer. + // add a new entry + p = (peer*)m_torrent->session().m_i2p_peer_pool.malloc(); + if (p == 0) return 0; + m_torrent->session().m_i2p_peer_pool.set_next_size(500); + new (p) i2p_peer(destination, true, src); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + if (!insert_peer(p, iter, flags)) + { +#if TORRENT_USE_ASSERTS + p->in_use = false; +#endif + + m_torrent->session().m_i2p_peer_pool.destroy((i2p_peer*)p); + return 0; + } + } + else + { + p = *iter; + update_peer(p, src, flags, tcp::endpoint(), destination); + } + m_torrent->state_updated(); + return p; + } +#endif // TORRENT_USE_I2P + + policy::peer* policy::add_peer(tcp::endpoint const& remote, peer_id const& pid + , int src, char flags) + { + INVARIANT_CHECK; + + // just ignore the obviously invalid entries + if (remote.address() == address() || remote.port() == 0) + return 0; + +#if TORRENT_USE_IPV6 + // don't allow link-local IPv6 addresses since they + // can't be used like normal addresses, they require an interface + // and will just cause connect() to fail with EINVAL + if (remote.address().is_v6() && remote.address().to_v6().is_link_local()) + return 0; +#endif + + aux::session_impl& ses = m_torrent->session(); + + // if this is an i2p torrent, and we don't allow mixed mode + // no regular peers should ever be added! + if (!ses.m_settings.allow_i2p_mixed && m_torrent->torrent_file().is_i2p()) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::ip_filter)); + return 0; + } + + port_filter const& pf = ses.m_port_filter; + if (pf.access(remote.port()) & port_filter::blocked) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::port_filter)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + if (ses.m_settings.no_connect_privileged_ports && remote.port() < 1024) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::privileged_ports)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + // if the IP is blocked, don't add it + if (m_torrent->apply_ip_filter() + && (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked)) + { + if (ses.m_alerts.should_post()) + ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle() + , remote.address(), peer_blocked_alert::ip_filter)); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered); +#endif + return 0; + } + + iterator iter; + peer* p = 0; + + bool found = false; + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + std::pair range = find_peers(remote.address()); + iter = std::find_if(range.first, range.second, match_peer_endpoint(remote)); + if (iter != range.second) found = true; + } + else + { + iter = std::lower_bound( + m_peers.begin(), m_peers.end() + , remote.address(), peer_address_compare() + ); + + if (iter != m_peers.end() && (*iter)->address() == remote.address()) found = true; + } + + if (!found) + { + // we don't have any info about this peer. + // add a new entry + +#if TORRENT_USE_IPV6 + bool is_v6 = remote.address().is_v6(); +#endif + p = +#if TORRENT_USE_IPV6 + is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() : +#endif + (peer*)m_torrent->session().m_ipv4_peer_pool.malloc(); + if (p == 0) return 0; +#if TORRENT_USE_IPV6 + if (is_v6) + m_torrent->session().m_ipv6_peer_pool.set_next_size(500); + else +#endif + m_torrent->session().m_ipv4_peer_pool.set_next_size(500); + +#if TORRENT_USE_IPV6 + if (is_v6) + new (p) ipv6_peer(remote, true, src); + else +#endif + new (p) ipv4_peer(remote, true, src); + +#if TORRENT_USE_ASSERTS + p->in_use = true; +#endif + + if (!insert_peer(p, iter, flags)) + { +#if TORRENT_USE_ASSERTS + p->in_use = false; +#endif +#if TORRENT_USE_IPV6 + if (is_v6) m_torrent->session().m_ipv6_peer_pool.destroy((ipv6_peer*)p); + else +#endif + m_torrent->session().m_ipv4_peer_pool.destroy((ipv4_peer*)p); + return 0; + } +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::first_time); +#endif + } + else + { + p = *iter; + TORRENT_ASSERT(p->in_use); + update_peer(p, src, flags, remote, 0); +#ifndef TORRENT_DISABLE_EXTENSIONS + m_torrent->notify_extension_add_peer(remote, src, 0); +#endif + } + + return p; + } + + bool policy::connect_one_peer(int session_time) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(m_torrent->want_more_peers()); + + iterator i = find_connect_candidate(session_time); + if (i == m_peers.end()) return false; + peer& p = **i; + TORRENT_ASSERT(p.in_use); + + TORRENT_ASSERT(!p.banned); + TORRENT_ASSERT(!p.connection); + TORRENT_ASSERT(p.connectable); + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + TORRENT_ASSERT(is_connect_candidate(p, m_finished)); + if (!m_torrent->connect_to_peer(&p)) + { + // failcount is a 5 bit value + const bool was_conn_cand = is_connect_candidate(p, m_finished); + if (p.failcount < 31) ++p.failcount; + if (was_conn_cand && !is_connect_candidate(p, m_finished)) + --m_num_connect_candidates; + return false; + } + TORRENT_ASSERT(p.connection); + TORRENT_ASSERT(!is_connect_candidate(p, m_finished)); + return true; + } + + // this is called whenever a peer connection is closed + void policy::connection_closed(const peer_connection& c, int session_time) + { + INVARIANT_CHECK; + + peer* p = c.peer_info_struct(); + + // if we couldn't find the connection in our list, just ignore it. + if (p == 0) return; + + TORRENT_ASSERT(p->in_use); + + // web seeds are special, they're not connected via the peer list + // so they're not kept in m_peers + TORRENT_ASSERT(p->web_seed + || std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)) + != m_peers.end()); + + TORRENT_ASSERT(p->connection == &c); + TORRENT_ASSERT(!is_connect_candidate(*p, m_finished)); + + // save transfer rate limits + p->upload_rate_limit = c.upload_limit(); + p->download_rate_limit = c.download_limit(); + + p->connection = 0; + p->optimistically_unchoked = false; + + // if fast reconnect is true, we won't + // update the timestamp, and it will remain + // the time when we initiated the connection. + if (!c.fast_reconnect()) + p->last_connected = session_time; + + if (c.failed()) + { + // failcount is a 5 bit value + if (p->failcount < 31) ++p->failcount; + } + + if (is_connect_candidate(*p, m_finished)) + ++m_num_connect_candidates; + + // if we're already a seed, it's not as important + // to keep all the possibly stale peers + // if we're not a seed, but we have too many peers + // start weeding the ones we only know from resume + // data first + // at this point it may be tempting to erase peers + // from the peer list, but keep in mind that we might + // have gotten to this point through new_connection, just + // disconnecting an old peer, relying on this policy::peer + // to still exist when we get back there, to assign the new + // peer connection pointer to it. The peer list must + // be left intact. + + // if we allow multiple connections per IP, and this peer + // was incoming and it never advertised its listen + // port, we don't really know which peer it was. In order + // to avoid adding one entry for every single connection + // the peer makes to us, don't save this entry + if (m_torrent->settings().allow_multiple_connections_per_ip + && !p->connectable + && p != m_locked_peer) + { + erase_peer(p); + } + } + + void policy::peer_is_interesting(peer_connection& c) + { + INVARIANT_CHECK; + + // no peer should be interesting if we're finished + TORRENT_ASSERT(!m_torrent->is_finished()); + + if (c.in_handshake()) return; + c.send_interested(); + if (c.has_peer_choked() + && c.allowed_fast().empty()) + return; + request_a_block(*m_torrent, c); + c.send_block_requests(); + } + + void policy::recalculate_connect_candidates() + { + INVARIANT_CHECK; + + const bool is_finished = m_torrent->is_finished(); + if (is_finished == m_finished) return; + + m_num_connect_candidates = 0; + m_finished = is_finished; + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + m_num_connect_candidates += is_connect_candidate(**i, m_finished); + } + } + +#if TORRENT_USE_ASSERTS + bool policy::has_connection(const peer_connection* c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(c); + error_code ec; + if (c->remote() != c->get_socket()->remote_endpoint(ec) && !ec) + { + fprintf(stderr, "c->remote: %s\nc->get_socket()->remote_endpoint: %s\n" + , print_endpoint(c->remote()).c_str() + , print_endpoint(c->get_socket()->remote_endpoint(ec)).c_str()); + TORRENT_ASSERT(false); + } + + return std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection_or_endpoint(*c)) != m_peers.end(); + } +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void policy::check_invariant() const + { + TORRENT_ASSERT(m_num_connect_candidates >= 0); + TORRENT_ASSERT(m_num_connect_candidates <= int(m_peers.size())); + if (m_torrent->is_aborted()) return; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + int connected_peers = 0; + + int total_connections = 0; + int nonempty_connections = 0; + int connect_candidates = 0; + + std::set unique_test; + const_iterator prev = m_peers.end(); + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (prev != m_peers.end()) ++prev; + if (i == m_peers.begin() + 1) prev = m_peers.begin(); + if (prev != m_peers.end()) + { + if (m_torrent->settings().allow_multiple_connections_per_ip) + TORRENT_ASSERT(!((*i)->address() < (*prev)->address())); + else + TORRENT_ASSERT((*prev)->address() < (*i)->address()); + } + peer const& p = **i; + TORRENT_ASSERT(p.in_use); + if (is_connect_candidate(p, m_finished)) ++connect_candidates; +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASSERT(p.inet_as == 0 || p.inet_as->first == p.inet_as_num); +#endif + if (!m_torrent->settings().allow_multiple_connections_per_ip) + { + std::pair range = find_peers(p.address()); + TORRENT_ASSERT(range.second - range.first == 1); + } + else + { + TORRENT_ASSERT(unique_test.count(p.ip()) == 0); + unique_test.insert(p.ip()); +// TORRENT_ASSERT(p.connection == 0 || p.ip() == p.connection->remote()); + } + ++total_connections; + if (!p.connection) + { + continue; + } + if (p.optimistically_unchoked) + { + TORRENT_ASSERT(p.connection); + TORRENT_ASSERT(!p.connection->is_choked()); + } + TORRENT_ASSERT(p.connection->peer_info_struct() == 0 + || p.connection->peer_info_struct() == &p); + ++nonempty_connections; + if (!p.connection->is_disconnecting()) + ++connected_peers; + } + + TORRENT_ASSERT(m_num_connect_candidates == connect_candidates); + + int num_torrent_peers = 0; + for (torrent::const_peer_iterator i = m_torrent->begin(); + i != m_torrent->end(); ++i) + { + if ((*i)->is_disconnecting()) continue; + // ignore web_peer_connections since they are not managed + // by the policy class + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + ++num_torrent_peers; + } + + if (m_torrent->has_picker()) + { + piece_picker& p = m_torrent->picker(); + std::vector downloaders = p.get_download_queue(); + + std::set peer_set; + std::vector peers; + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + p.get_downloaders(peers, i->index); + std::copy(peers.begin(), peers.end() + , std::insert_iterator >(peer_set, peer_set.begin())); + } + + for (std::set::iterator i = peer_set.begin() + , end(peer_set.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + if (p->connection == 0) continue; + // web seeds are special, they're not connected via the peer list + // so they're not kept in m_peers + if (p->connection->type() != peer_connection::bittorrent_connection) continue; + TORRENT_ASSERT(std::find_if(m_peers.begin(), m_peers.end() + , match_peer_connection_or_endpoint(*p->connection)) != m_peers.end()); + } + } +#endif // TORRENT_EXPENSIVE_INVARIANT_CHECKS + + // this invariant is a bit complicated. + // the usual case should be that connected_peers + // == num_torrent_peers. But when there's an incoming + // connection, it will first be added to the policy + // and then be added to the torrent. + // When there's an outgoing connection, it will first + // be added to the torrent and then to the policy. + // that's why the two second cases are in there. +/* + TORRENT_ASSERT(connected_peers == num_torrent_peers + || (connected_peers == num_torrent_peers + 1 + && connected_peers > 0) + || (connected_peers + 1 == num_torrent_peers + && num_torrent_peers > 0)); +*/ + } +#endif // TORRENT_DEBUG + + policy::peer::peer(boost::uint16_t port, bool conn, int src) + : prev_amount_upload(0) + , prev_amount_download(0) + , connection(0) + , peer_rank(0) +#ifndef TORRENT_DISABLE_GEO_IP + , inet_as(0) +#endif + , last_optimistically_unchoked(0) + , last_connected(0) + , port(port) + , upload_rate_limit(0) + , download_rate_limit(0) + , hashfails(0) + , failcount(0) + , connectable(conn) + , optimistically_unchoked(false) + , seed(false) + , fast_reconnects(0) + , trust_points(0) + , source(src) +#ifndef TORRENT_DISABLE_ENCRYPTION + // assume no support in order to + // prefer opening non-encrypyed + // connections. If it fails, we'll + // retry with encryption + , pe_support(false) +#endif +#if TORRENT_USE_IPV6 + , is_v6_addr(false) +#endif +#if TORRENT_USE_I2P + , is_i2p_addr(false) +#endif + , on_parole(false) + , banned(false) +#ifndef TORRENT_DISABLE_DHT + , added_to_dht(false) +#endif + , supports_utp(true) // assume peers support utp + , confirmed_supports_utp(false) + , supports_holepunch(false) + , web_seed(false) +#if TORRENT_USE_ASSERTS + , in_use(false) +#endif + { + TORRENT_ASSERT((src & 0xff) == src); + } + + // TOOD: pass in both an IPv6 and IPv4 address here + boost::uint32_t policy::peer::rank(external_ip const& external, int external_port) const + { + if (peer_rank == 0) + peer_rank = peer_priority( + tcp::endpoint(external.external_address(this->address()), external_port) + , tcp::endpoint(this->address(), this->port)); + return peer_rank; + } + + size_type policy::peer::total_download() const + { + if (connection != 0) + { + TORRENT_ASSERT(prev_amount_download == 0); + return connection->statistics().total_payload_download(); + } + else + { + return size_type(prev_amount_download) << 10; + } + } + + size_type policy::peer::total_upload() const + { + if (connection != 0) + { + TORRENT_ASSERT(prev_amount_upload == 0); + return connection->statistics().total_payload_upload(); + } + else + { + return size_type(prev_amount_upload) << 10; + } + } + + // this returns true if lhs is a better erase candidate than rhs + bool policy::compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const + { + TORRENT_ASSERT(lhs.connection == 0); + TORRENT_ASSERT(rhs.connection == 0); + + // primarily, prefer getting rid of peers we've already tried and failed + if (lhs.failcount != rhs.failcount) + return lhs.failcount > rhs.failcount; + + bool lhs_resume_data_source = lhs.source == peer_info::resume_data; + bool rhs_resume_data_source = rhs.source == peer_info::resume_data; + + // prefer to drop peers whose only source is resume data + if (lhs_resume_data_source != rhs_resume_data_source) + return lhs_resume_data_source > rhs_resume_data_source; + + if (lhs.connectable != rhs.connectable) + return lhs.connectable < rhs.connectable; + + return lhs.trust_points < rhs.trust_points; + } + + // this returns true if lhs is a better connect candidate than rhs + bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs + , external_ip const& external, int external_port) const + { + // prefer peers with lower failcount + if (lhs.failcount != rhs.failcount) + return lhs.failcount < rhs.failcount; + + // Local peers should always be tried first + bool lhs_local = is_local(lhs.address()); + bool rhs_local = is_local(rhs.address()); + if (lhs_local != rhs_local) return lhs_local > rhs_local; + + if (lhs.last_connected != rhs.last_connected) + return lhs.last_connected < rhs.last_connected; + + int lhs_rank = source_rank(lhs.source); + int rhs_rank = source_rank(rhs.source); + if (lhs_rank != rhs_rank) return lhs_rank > rhs_rank; + +#ifndef TORRENT_DISABLE_GEO_IP + // don't bias fast peers when seeding + if (!m_finished && m_torrent->session().has_asnum_db()) + { + int lhs_as = lhs.inet_as ? lhs.inet_as->second : 0; + int rhs_as = rhs.inet_as ? rhs.inet_as->second : 0; + if (lhs_as != rhs_as) return lhs_as > rhs_as; + } +#endif + boost::uint32_t lhs_peer_rank = lhs.rank(external, external_port); + boost::uint32_t rhs_peer_rank = rhs.rank(external, external_port); + if (lhs_peer_rank > rhs_peer_rank) return true; + return false; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/puff.cpp b/apps/Launcher/ext/libtorrent/src/puff.cpp new file mode 100644 index 0000000000..82347e7791 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/puff.cpp @@ -0,0 +1,842 @@ +/* + * puff.c + * Copyright (C) 2002, 2003 Mark Adler + * For conditions of distribution and use, see copyright notice in puff.h + * version 1.7, 3 Mar 2003 + * + * puff.c is a simple inflate written to be an unambiguous way to specify the + * deflate format. It is not written for speed but rather simplicity. As a + * side benefit, this code might actually be useful when small code is more + * important than speed, such as bootstrap applications. For typical deflate + * data, zlib's inflate() is about four times as fast as puff(). zlib's + * inflate compiles to around 20K on my machine, whereas puff.c compiles to + * around 4K on my machine (a PowerPC using GNU cc). If the faster decode() + * function here is used, then puff() is only twice as slow as zlib's + * inflate(). + * + * All dynamically allocated memory comes from the stack. The stack required + * is less than 2K bytes. This code is compatible with 16-bit int's and + * assumes that long's are at least 32 bits. puff.c uses the short data type, + * assumed to be 16 bits, for arrays in order to to conserve memory. The code + * works whether integers are stored big endian or little endian. + * + * In the comments below are "Format notes" that describe the inflate process + * and document some of the less obvious aspects of the format. This source + * code is meant to supplement RFC 1951, which formally describes the deflate + * format: + * + * http://www.zlib.org/rfc-deflate.html + */ + +/* + * Change history: + * + * 1.0 10 Feb 2002 - First version + * 1.1 17 Feb 2002 - Clarifications of some comments and notes + * - Update puff() dest and source pointers on negative + * errors to facilitate debugging deflators + * - Remove longest from struct huffman -- not needed + * - Simplify offs[] index in construct() + * - Add input size and checking, using longjmp() to + * maintain easy readability + * - Use short data type for large arrays + * - Use pointers instead of long to specify source and + * destination sizes to avoid arbitrary 4 GB limits + * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!), + * but leave simple version for readabilty + * - Make sure invalid distances detected if pointers + * are 16 bits + * - Fix fixed codes table error + * - Provide a scanning mode for determining size of + * uncompressed data + * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Jean-loup] + * - Add a puff.h file for the interface + * - Add braces in puff() for else do [Jean-loup] + * - Use indexes instead of pointers for readability + * 1.4 31 Mar 2002 - Simplify construct() code set check + * - Fix some comments + * - Add FIXLCODES #define + * 1.5 6 Apr 2002 - Minor comment fixes + * 1.6 7 Aug 2002 - Minor format changes + * 1.7 3 Mar 2003 - Added test code for distribution + * - Added zlib-like license + */ + +/* +note by Arvid Norberg. +This file was turned into a .cpp file in order to +be able to take advantage of boost's cstdint.hpp file +All "short" has been replaced with boost::int16_t +and all "long" with boost::int32_t according to the +type width assuptions in the comment above. +*/ +#include /* for setjmp(), longjmp(), and jmp_buf */ +#include /* for types with size guarantees */ +#include "libtorrent/puff.hpp" /* prototype for puff() */ + +#define local static /* for local function definitions */ +#define NIL ((unsigned char *)0) /* for no output option */ + +/* + * Maximums for allocations and loops. It is not useful to change these -- + * they are fixed by the deflate format. + */ +#define MAXBITS 15 /* maximum bits in a code */ +#define MAXLCODES 286 /* maximum number of literal/length codes */ +#define MAXDCODES 30 /* maximum number of distance codes */ +#define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */ +#define FIXLCODES 288 /* number of fixed literal/length codes */ + +/* input and output state */ +struct state { + /* output state */ + unsigned char *out; /* output buffer */ + boost::uint32_t outlen; /* available space at out */ + boost::uint32_t outcnt; /* bytes written to out so far */ + + /* input state */ + unsigned char *in; /* input buffer */ + boost::uint32_t inlen; /* available input at in */ + boost::uint32_t incnt; /* bytes read so far */ + int bitbuf; /* bit buffer */ + int bitcnt; /* number of bits in bit buffer */ + + /* input limit error return state for bits() and decode() */ + jmp_buf env; +}; + +/* + * Return need bits from the input stream. This always leaves less than + * eight bits in the buffer. bits() works properly for need == 0. + * + * Format notes: + * + * - Bits are stored in bytes from the least significant bit to the most + * significant bit. Therefore bits are dropped from the bottom of the bit + * buffer, using shift right, and new bytes are appended to the top of the + * bit buffer, using shift left. + */ +local int bits(struct state *s, int need) +{ + boost::int32_t val; /* bit accumulator (can use up to 20 bits) */ + + /* load at least need bits into val */ + val = s->bitbuf; + while (s->bitcnt < need) { + if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ + val |= (boost::int32_t)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */ + s->bitcnt += 8; + } + + /* drop need bits and update buffer, always zero to seven bits left */ + s->bitbuf = (int)(val >> need); + s->bitcnt -= need; + + /* return need bits, zeroing the bits above that */ + return (int)(val & ((1L << need) - 1)); +} + +/* + * Process a stored block. + * + * Format notes: + * + * - After the two-bit stored block type (00), the stored block length and + * stored bytes are byte-aligned for fast copying. Therefore any leftover + * bits in the byte that has the last bit of the type, as many as seven, are + * discarded. The value of the discarded bits are not defined and should not + * be checked against any expectation. + * + * - The second inverted copy of the stored block length does not have to be + * checked, but it's probably a good idea to do so anyway. + * + * - A stored block can have zero length. This is sometimes used to byte-align + * subsets of the compressed data for random access or partial recovery. + */ +local int stored(struct state *s) +{ + unsigned len; /* length of stored block */ + + /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ + s->bitbuf = 0; + s->bitcnt = 0; + + /* get length and check against its one's complement */ + if (s->incnt + 4 > s->inlen) return 2; /* not enough input */ + len = s->in[s->incnt++]; + len |= s->in[s->incnt++] << 8; + if (s->in[s->incnt++] != (~len & 0xff) || + s->in[s->incnt++] != ((~len >> 8) & 0xff)) + return -2; /* didn't match complement! */ + + /* copy len bytes from in to out */ + if (s->incnt + len > s->inlen) return 2; /* not enough input */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) + return 1; /* not enough output space */ + while (len--) + s->out[s->outcnt++] = s->in[s->incnt++]; + } + else { /* just scanning */ + s->outcnt += len; + s->incnt += len; + } + + /* done with a valid stored block */ + return 0; +} + +/* + * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of + * each length, which for a canonical code are stepped through in order. + * symbol[] are the symbol values in canonical order, where the number of + * entries is the sum of the counts in count[]. The decoding process can be + * seen in the function decode() below. + */ +struct huffman { + boost::int16_t *count; /* number of symbols of each length */ + boost::int16_t *symbol; /* canonically ordered symbols */ +}; + +/* + * Decode a code from the stream s using huffman table h. Return the symbol or + * a negative value if there is an error. If all of the lengths are zero, i.e. + * an empty code, or if the code is incomplete and an invalid code is received, + * then -9 is returned after reading MAXBITS bits. + * + * Format notes: + * + * - The codes as stored in the compressed data are bit-reversed relative to + * a simple integer ordering of codes of the same lengths. Hence below the + * bits are pulled from the compressed data one at a time and used to + * build the code value reversed from what is in the stream in order to + * permit simple integer comparisons for decoding. A table-based decoding + * scheme (as used in zlib) does not need to do this reversal. + * + * - The first code for the shortest length is all zeros. Subsequent codes of + * the same length are simply integer increments of the previous code. When + * moving up a length, a zero bit is appended to the code. For a complete + * code, the last code of the longest length will be all ones. + * + * - Incomplete codes are handled by this decoder, since they are permitted + * in the deflate format. See the format notes for fixed() and dynamic(). + */ +#ifdef SLOW +local int decode(struct state *s, struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + + code = first = index = 0; + for (len = 1; len <= MAXBITS; len++) { + code |= bits(s, 1); /* get next bit */ + count = h->count[len]; + if (code < first + count) /* if length len, return symbol */ + return h->symbol[index + (code - first)]; + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + } + return -9; /* ran out of codes */ +} + +/* + * A faster version of decode() for real applications of this code. It's not + * as readable, but it makes puff() twice as fast. And it only makes the code + * a few percent larger. + */ +#else /* !SLOW */ +local int decode(struct state *s, struct huffman *h) +{ + int len; /* current number of bits in code */ + int code; /* len bits being decoded */ + int first; /* first code of length len */ + int count; /* number of codes of length len */ + int index; /* index of first code of length len in symbol table */ + int bitbuf; /* bits from stream */ + int left; /* bits left in next or left to process */ + boost::int16_t *next; /* next number of codes */ + + bitbuf = s->bitbuf; + left = s->bitcnt; + code = first = index = 0; + len = 1; + next = h->count + 1; + while (1) { + while (left--) { + code |= bitbuf & 1; + bitbuf >>= 1; + count = *next++; + if (code < first + count) { /* if length len, return symbol */ + s->bitbuf = bitbuf; + s->bitcnt = (s->bitcnt - len) & 7; + return h->symbol[index + (code - first)]; + } + index += count; /* else update for next length */ + first += count; + first <<= 1; + code <<= 1; + len++; + } + left = (MAXBITS+1) - len; + if (left == 0) break; + if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ + bitbuf = s->in[s->incnt++]; + if (left > 8) left = 8; + } + return -9; /* ran out of codes */ +} +#endif /* SLOW */ + +/* + * Given the list of code lengths length[0..n-1] representing a canonical + * Huffman code for n symbols, construct the tables required to decode those + * codes. Those tables are the number of codes of each length, and the symbols + * sorted by length, retaining their original order within each length. The + * return value is zero for a complete code set, negative for an over- + * subscribed code set, and positive for an incomplete code set. The tables + * can be used if the return value is zero or positive, but they cannot be used + * if the return value is negative. If the return value is zero, it is not + * possible for decode() using that table to return an error--any stream of + * enough bits will resolve to a symbol. If the return value is positive, then + * it is possible for decode() using that table to return an error for received + * codes past the end of the incomplete lengths. + * + * Not used by decode(), but used for error checking, h->count[0] is the number + * of the n symbols not in the code. So n - h->count[0] is the number of + * codes. This is useful for checking for incomplete codes that have more than + * one symbol, which is an error in a dynamic block. + * + * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS + * This is assured by the construction of the length arrays in dynamic() and + * fixed() and is not verified by construct(). + * + * Format notes: + * + * - Permitted and expected examples of incomplete codes are one of the fixed + * codes and any code with a single symbol which in deflate is coded as one + * bit instead of zero bits. See the format notes for fixed() and dynamic(). + * + * - Within a given code length, the symbols are kept in ascending order for + * the code bits definition. + */ +local int construct(struct huffman *h, boost::int16_t *length, int n) +{ + int symbol; /* current symbol when stepping through length[] */ + int len; /* current length when stepping through h->count[] */ + int left; /* number of possible codes left of current length */ + boost::int16_t offs[MAXBITS+1]; /* offsets in symbol table for each length */ + + /* count number of codes of each length */ + for (len = 0; len <= MAXBITS; len++) + h->count[len] = 0; + for (symbol = 0; symbol < n; symbol++) + (h->count[length[symbol]])++; /* assumes lengths are within bounds */ + if (h->count[0] == n) /* no codes! */ + return 0; /* complete, but decode() will fail */ + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; /* one possible code of zero length */ + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; /* one more bit, double codes left */ + left -= h->count[len]; /* deduct count from possible codes */ + if (left < 0) return left; /* over-subscribed--return negative */ + } /* left > 0 means incomplete */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + h->count[len]; + + /* + * put symbols in table sorted by length, by symbol order within each + * length + */ + for (symbol = 0; symbol < n; symbol++) + if (length[symbol] != 0) + h->symbol[offs[length[symbol]]++] = symbol; + + /* return zero for complete set, positive for incomplete set */ + return left; +} + +/* + * Decode literal/length and distance codes until an end-of-block code. + * + * Format notes: + * + * - Compressed data that is after the block type if fixed or after the code + * description if dynamic is a combination of literals and length/distance + * pairs terminated by and end-of-block code. Literals are simply Huffman + * coded bytes. A length/distance pair is a coded length followed by a + * coded distance to represent a string that occurs earlier in the + * uncompressed data that occurs again at the current location. + * + * - Literals, lengths, and the end-of-block code are combined into a single + * code of up to 286 symbols. They are 256 literals (0..255), 29 length + * symbols (257..285), and the end-of-block symbol (256). + * + * - There are 256 possible lengths (3..258), and so 29 symbols are not enough + * to represent all of those. Lengths 3..10 and 258 are in fact represented + * by just a length symbol. Lengths 11..257 are represented as a symbol and + * some number of extra bits that are added as an integer to the base length + * of the length symbol. The number of extra bits is determined by the base + * length symbol. These are in the static arrays below, lens[] for the base + * lengths and lext[] for the corresponding number of extra bits. + * + * - The reason that 258 gets its own symbol is that the longest length is used + * often in highly redundant files. Note that 258 can also be coded as the + * base value 227 plus the maximum extra value of 31. While a good deflate + * should never do this, it is not an error, and should be decoded properly. + * + * - If a length is decoded, including its extra bits if any, then it is + * followed a distance code. There are up to 30 distance symbols. Again + * there are many more possible distances (1..32768), so extra bits are added + * to a base value represented by the symbol. The distances 1..4 get their + * own symbol, but the rest require extra bits. The base distances and + * corresponding number of extra bits are below in the static arrays dist[] + * and dext[]. + * + * - Literal bytes are simply written to the output. A length/distance pair is + * an instruction to copy previously uncompressed bytes to the output. The + * copy is from distance bytes back in the output stream, copying for length + * bytes. + * + * - Distances pointing before the beginning of the output data are not + * permitted. + * + * - Overlapped copies, where the length is greater than the distance, are + * allowed and common. For example, a distance of one and a length of 258 + * simply copies the last byte 258 times. A distance of four and a length of + * twelve copies the last four bytes three times. A simple forward copy + * ignoring whether the length is greater than the distance or not implements + * this correctly. You should not use memcpy() since its behavior is not + * defined for overlapped arrays. You should not use memmove() or bcopy() + * since though their behavior -is- defined for overlapping arrays, it is + * defined to do the wrong thing in this case. + */ +local int codes(struct state *s, + struct huffman *lencode, + struct huffman *distcode) +{ + int symbol; /* decoded symbol */ + int len; /* length for copy */ + unsigned dist; /* distance for copy */ + static const boost::int16_t lens[29] = { /* Size base for length codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; + static const boost::int16_t lext[29] = { /* Extra bits for length codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; + static const boost::int16_t dists[30] = { /* Offset base for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; + static const boost::int16_t dext[30] = { /* Extra bits for distance codes 0..29 */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + /* decode literals and length/distance pairs */ + do { + symbol = decode(s, lencode); + if (symbol < 0) return symbol; /* invalid symbol */ + if (symbol < 256) { /* literal: symbol is the byte */ + /* write out the literal */ + if (s->out != NIL) { + if (s->outcnt == s->outlen) return 1; + s->out[s->outcnt] = symbol; + } + s->outcnt++; + } + else if (symbol > 256) { /* length */ + /* get and compute length */ + symbol -= 257; + if (symbol >= 29) return -9; /* invalid fixed code */ + len = lens[symbol] + bits(s, lext[symbol]); + + /* get and check distance */ + symbol = decode(s, distcode); + if (symbol < 0) return symbol; /* invalid symbol */ + dist = dists[symbol] + bits(s, dext[symbol]); + if (dist > s->outcnt) + return -10; /* distance too far back */ + + /* copy length bytes from distance bytes back */ + if (s->out != NIL) { + if (s->outcnt + len > s->outlen) return 1; + while (len--) { + s->out[s->outcnt] = s->out[s->outcnt - dist]; + s->outcnt++; + } + } + else + s->outcnt += len; + } + } while (symbol != 256); /* end of block symbol */ + + /* done with a valid fixed or dynamic block */ + return 0; +} + +/* + * Process a fixed codes block. + * + * Format notes: + * + * - This block type can be useful for compressing small amounts of data for + * which the size of the code descriptions in a dynamic block exceeds the + * benefit of custom codes for that block. For fixed codes, no bits are + * spent on code descriptions. Instead the code lengths for literal/length + * codes and distance codes are fixed. The specific lengths for each symbol + * can be seen in the "for" loops below. + * + * - The literal/length code is complete, but has two symbols that are invalid + * and should result in an error if received. This cannot be implemented + * simply as an incomplete code since those two symbols are in the "middle" + * of the code. They are eight bits long and the longest literal/length\ + * code is nine bits. Therefore the code must be constructed with those + * symbols, and the invalid symbols must be detected after decoding. + * + * - The fixed distance codes also have two invalid symbols that should result + * in an error if received. Since all of the distance codes are the same + * length, this can be implemented as an incomplete code. Then the invalid + * codes are detected while decoding. + */ +local int fixed(struct state *s) +{ + static int virgin = 1; + static boost::int16_t lencnt[MAXBITS+1], lensym[FIXLCODES]; + static boost::int16_t distcnt[MAXBITS+1], distsym[MAXDCODES]; + static struct huffman lencode = {lencnt, lensym}; + static struct huffman distcode = {distcnt, distsym}; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + int symbol; + boost::int16_t lengths[FIXLCODES]; + + /* literal/length table */ + for (symbol = 0; symbol < 144; symbol++) + lengths[symbol] = 8; + for (; symbol < 256; symbol++) + lengths[symbol] = 9; + for (; symbol < 280; symbol++) + lengths[symbol] = 7; + for (; symbol < FIXLCODES; symbol++) + lengths[symbol] = 8; + construct(&lencode, lengths, FIXLCODES); + + /* distance table */ + for (symbol = 0; symbol < MAXDCODES; symbol++) + lengths[symbol] = 5; + construct(&distcode, lengths, MAXDCODES); + + /* do this just once */ + virgin = 0; + } + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Process a dynamic codes block. + * + * Format notes: + * + * - A dynamic block starts with a description of the literal/length and + * distance codes for that block. New dynamic blocks allow the compressor to + * rapidly adapt to changing data with new codes optimized for that data. + * + * - The codes used by the deflate format are "canonical", which means that + * the actual bits of the codes are generated in an unambiguous way simply + * from the number of bits in each code. Therefore the code descriptions + * are simply a list of code lengths for each symbol. + * + * - The code lengths are stored in order for the symbols, so lengths are + * provided for each of the literal/length symbols, and for each of the + * distance symbols. + * + * - If a symbol is not used in the block, this is represented by a zero as + * as the code length. This does not mean a zero-length code, but rather + * that no code should be created for this symbol. There is no way in the + * deflate format to represent a zero-length code. + * + * - The maximum number of bits in a code is 15, so the possible lengths for + * any code are 1..15. + * + * - The fact that a length of zero is not permitted for a code has an + * interesting consequence. Normally if only one symbol is used for a given + * code, then in fact that code could be represented with zero bits. However + * in deflate, that code has to be at least one bit. So for example, if + * only a single distance base symbol appears in a block, then it will be + * represented by a single code of length one, in particular one 0 bit. This + * is an incomplete code, since if a 1 bit is received, it has no meaning, + * and should result in an error. So incomplete distance codes of one symbol + * should be permitted, and the receipt of invalid codes should be handled. + * + * - It is also possible to have a single literal/length code, but that code + * must be the end-of-block code, since every dynamic block has one. This + * is not the most efficient way to create an empty block (an empty fixed + * block is fewer bits), but it is allowed by the format. So incomplete + * literal/length codes of one symbol should also be permitted. + * + * - The list of up to 286 length/literal lengths and up to 30 distance lengths + * are themselves compressed using Huffman codes and run-length encoding. In + * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means + * that length, and the symbols 16, 17, and 18 are run-length instructions. + * Each of 16, 17, and 18 are follwed by extra bits to define the length of + * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10 + * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols + * are common, hence the special coding for zero lengths. + * + * - The symbols for 0..18 are Huffman coded, and so that code must be + * described first. This is simply a sequence of up to 19 three-bit values + * representing no code (0) or the code length for that symbol (1..7). + * + * - A dynamic block starts with three fixed-size counts from which is computed + * the number of literal/length code lengths, the number of distance code + * lengths, and the number of code length code lengths (ok, you come up with + * a better name!) in the code descriptions. For the literal/length and + * distance codes, lengths after those provided are considered zero, i.e. no + * code. The code length code lengths are received in a permuted order (see + * the order[] array below) to make a short code length code length list more + * likely. As it turns out, very short and very long codes are less likely + * to be seen in a dynamic code description, hence what may appear initially + * to be a peculiar ordering. + * + * - Given the number of literal/length code lengths (nlen) and distance code + * lengths (ndist), then they are treated as one long list of nlen + ndist + * code lengths. Therefore run-length coding can and often does cross the + * boundary between the two sets of lengths. + * + * - So to summarize, the code description at the start of a dynamic block is + * three counts for the number of code lengths for the literal/length codes, + * the distance codes, and the code length codes. This is followed by the + * code length code lengths, three bits each. This is used to construct the + * code length code which is used to read the remainder of the lengths. Then + * the literal/length code lengths and distance lengths are read as a single + * set of lengths using the code length codes. Codes are constructed from + * the resulting two sets of lengths, and then finally you can start + * decoding actual compressed data in the block. + * + * - For reference, a "typical" size for the code description in a dynamic + * block is around 80 bytes. + */ +local int dynamic(struct state *s) +{ + int nlen, ndist, ncode; /* number of lengths in descriptor */ + int index; /* index of lengths[] */ + int err; /* construct() return value */ + boost::int16_t lengths[MAXCODES]; /* descriptor code lengths */ + boost::int16_t lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */ + boost::int16_t distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */ + struct huffman lencode = {lencnt, lensym}; /* length code */ + struct huffman distcode = {distcnt, distsym}; /* distance code */ + static const boost::int16_t order[19] = /* permutation of code length codes */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* get number of lengths in each table, check lengths */ + nlen = bits(s, 5) + 257; + ndist = bits(s, 5) + 1; + ncode = bits(s, 4) + 4; + if (nlen > MAXLCODES || ndist > MAXDCODES) + return -3; /* bad counts */ + + /* read code length code lengths (really), missing lengths are zero */ + for (index = 0; index < ncode; index++) + lengths[order[index]] = bits(s, 3); + for (; index < 19; index++) + lengths[order[index]] = 0; + + /* build huffman table for code lengths codes (use lencode temporarily) */ + err = construct(&lencode, lengths, 19); + if (err != 0) return -4; /* require complete code set here */ + + /* read length/literal and distance code length tables */ + index = 0; + while (index < nlen + ndist) { + int symbol; /* decoded value */ + int len; /* last length to repeat */ + + symbol = decode(s, &lencode); + if (symbol < 16) /* length in 0..15 */ + lengths[index++] = symbol; + else { /* repeat instruction */ + len = 0; /* assume repeating zeros */ + if (symbol == 16) { /* repeat last length 3..6 times */ + if (index == 0) return -5; /* no last length! */ + len = lengths[index - 1]; /* last length */ + symbol = 3 + bits(s, 2); + } + else if (symbol == 17) /* repeat zero 3..10 times */ + symbol = 3 + bits(s, 3); + else /* == 18, repeat zero 11..138 times */ + symbol = 11 + bits(s, 7); + if (index + symbol > nlen + ndist) + return -6; /* too many lengths! */ + while (symbol--) /* repeat last or zero symbol times */ + lengths[index++] = len; + } + } + + /* build huffman table for literal/length codes */ + err = construct(&lencode, lengths, nlen); + if (err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) + return -7; /* only allow incomplete codes if just one code */ + + /* build huffman table for distance codes */ + err = construct(&distcode, lengths + nlen, ndist); + if (err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) + return -8; /* only allow incomplete codes if just one code */ + + /* decode data until end-of-block code */ + return codes(s, &lencode, &distcode); +} + +/* + * Inflate source to dest. On return, destlen and sourcelen are updated to the + * size of the uncompressed data and the size of the deflate data respectively. + * On success, the return value of puff() is zero. If there is an error in the + * source data, i.e. it is not in the deflate format, then a negative value is + * returned. If there is not enough input available or there is not enough + * output space, then a positive error is returned. In that case, destlen and + * sourcelen are not updated to facilitate retrying from the beginning with the + * provision of more input data or more output space. In the case of invalid + * inflate data (a negative error), the dest and source pointers are updated to + * facilitate the debugging of deflators. + * + * puff() also has a mode to determine the size of the uncompressed output with + * no output written. For this dest must be (unsigned char *)0. In this case, + * the input value of *destlen is ignored, and on return *destlen is set to the + * size of the uncompressed output. + * + * The return codes are: + * + * 2: available inflate data did not terminate + * 1: output space exhausted before completing inflate + * 0: successful inflate + * -1: invalid block type (type == 3) + * -2: stored block length did not match one's complement + * -3: dynamic block code description: too many length or distance codes + * -4: dynamic block code description: code lengths codes incomplete + * -5: dynamic block code description: repeat lengths with no first length + * -6: dynamic block code description: repeat more than specified lengths + * -7: dynamic block code description: invalid literal/length code lengths + * -8: dynamic block code description: invalid distance code lengths + * -9: invalid literal/length or distance code in fixed or dynamic block + * -10: distance is too far back in fixed or dynamic block + * + * Format notes: + * + * - Three bits are read for each block to determine the kind of block and + * whether or not it is the last block. Then the block is decoded and the + * process repeated if it was not the last block. + * + * - The leftover bits in the last byte of the deflate data after the last + * block (if it was a fixed or dynamic block) are undefined and have no + * expected values to check. + */ +int puff(unsigned char *dest, /* pointer to destination pointer */ + boost::uint32_t *destlen, /* amount of output space */ + unsigned char *source, /* pointer to source data pointer */ + boost::uint32_t *sourcelen) /* amount of input available */ +{ + struct state s; /* input/output state */ + int last, type; /* block information */ + int err; /* return value */ + + /* initialize output state */ + s.out = dest; + s.outlen = *destlen; /* ignored if dest is NIL */ + s.outcnt = 0; + + /* initialize input state */ + s.in = source; + s.inlen = *sourcelen; + s.incnt = 0; + s.bitbuf = 0; + s.bitcnt = 0; + + /* return if bits() or decode() tries to read past available input */ + if (setjmp(s.env) != 0) /* if came back here via longjmp() */ + err = 2; /* then skip do-loop, return error */ + else { + /* process blocks until last block or error */ + do { + last = bits(&s, 1); /* one if last block */ + type = bits(&s, 2); /* block type 0..3 */ + err = type == 0 ? stored(&s) : + (type == 1 ? fixed(&s) : + (type == 2 ? dynamic(&s) : + -1)); /* type == 3, invalid */ + if (err != 0) break; /* return with error */ + } while (!last); + } + + /* update the lengths and return */ + if (err <= 0) { + *destlen = s.outcnt; + *sourcelen = s.incnt; + } + return err; +} + +#ifdef TEST +/* Example of how to use puff() */ +#include +#include +#include +#include + +local unsigned char *yank(char *name, boost::uint32_t *len) +{ + boost::uint32_t size; + unsigned char *buf; + FILE *in; + struct stat s; + + *len = 0; + if (stat(name, &s)) return NULL; + if ((s.st_mode & S_IFMT) != S_IFREG) return NULL; + size = (boost::uint32_t)(s.st_size); + if (size == 0 || (off_t)size != s.st_size) return NULL; + in = fopen(name, "r"); + if (in == NULL) return NULL; + buf = malloc(size); + if (buf != NULL && fread(buf, 1, size, in) != size) { + free(buf); + buf = NULL; + } + fclose(in); + *len = size; + return buf; +} + +int main(int argc, char **argv) +{ + int ret; + unsigned char *source; + boost::uint32_t len, sourcelen, destlen; + + if (argc < 2) return 2; + source = yank(argv[1], &len); + if (source == NULL) return 2; + sourcelen = len; + ret = puff(NIL, &destlen, source, &sourcelen); + if (ret) + printf("puff() failed with return code %d\n", ret); + else { + printf("puff() succeeded uncompressing %lu bytes\n", destlen); + if (sourcelen < len) printf("%lu compressed bytes unused\n", + len - sourcelen); + } + free(source); + return ret; +} +#endif diff --git a/apps/Launcher/ext/libtorrent/src/random.cpp b/apps/Launcher/ext/libtorrent/src/random.cpp new file mode 100644 index 0000000000..f8698441cb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/random.cpp @@ -0,0 +1,72 @@ +/* + +Copyright (c) 2011-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + + namespace + { + boost::uint32_t x = 123456789; +#ifdef TORRENT_USE_ASSERTS + bool seeded = false; +#endif + } + + void random_seed(boost::uint32_t v) + { + x = v; +#ifdef TORRENT_USE_ASSERTS + seeded = true; +#endif + } + + // this is an xorshift random number generator + // see: http://en.wikipedia.org/wiki/Xorshift + boost::uint32_t random() + { + TORRENT_ASSERT(seeded); + + static boost::uint32_t y = 362436069; + static boost::uint32_t z = 521288629; + static boost::uint32_t w = 88675123; + boost::uint32_t t; + + t = x ^ (x << 11); + x = y; y = z; z = w; + return w = w ^ (w >> 19) ^ (t ^ (t >> 8)); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/rss.cpp b/apps/Launcher/ext/libtorrent/src/rss.cpp new file mode 100644 index 0000000000..700cacd6d6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/rss.cpp @@ -0,0 +1,683 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/rss.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/http_parser.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/settings.hpp" +#include "libtorrent/alert_types.hpp" // for rss_alert + +#include +#include +#include +#include + +namespace libtorrent { + +feed_item::feed_item(): size(-1) {} +feed_item::~feed_item() {} + +struct feed_state +{ + feed_state(feed& r) + : in_item(false) + , num_items(0) + , type(none) + , ret(r) + {} + + bool in_item; + int num_items; + std::string current_tag; + enum feed_type + { + none, atom, rss2 + } type; + feed_item current_item; + feed& ret; + + bool is_item(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "entry"); + case rss2: return string_equal_no_case(tag, "item"); + default: return false; + } + } + + bool is_title(char const* tag) const + { + switch (type) + { + case atom: + case rss2: return string_equal_no_case(tag, "title"); + default: return false; + } + } + + bool is_url(char const* tag) const + { + switch (type) + { + case atom: + case rss2: return string_equal_no_case(tag, "link"); + default: return false; + } + } + + bool is_desc(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "summary"); + case rss2: return string_equal_no_case(tag, "description") + || string_equal_no_case(tag, "media:text"); + default: return false; + } + } + + bool is_uuid(char const* tag) const + { + switch (type) + { + case atom: return string_equal_no_case(tag, "id"); + case rss2: return string_equal_no_case(tag, "guid"); + default: return false; + } + } + + bool is_comment(char const* tag) const + { + switch (type) + { + case atom: return false; + case rss2: return string_equal_no_case(tag, "comments"); + default: return false; + } + } + + bool is_category(char const* tag) const + { + switch (type) + { + case atom: return false; + case rss2: return string_equal_no_case(tag, "category"); + default: return false; + } + } + + bool is_size(char const* tag) const + { + return string_equal_no_case(tag, "size") + || string_equal_no_case(tag, "contentlength"); + } + + bool is_hash(char const* tag) const + { + return string_equal_no_case(tag, "hash") + || string_equal_no_case(tag, "media:hash"); + } + + bool is_ttl(char const* tag) const + { + return string_equal_no_case(tag, "ttl"); + } +}; + +void parse_feed(feed_state& f, int token, char const* name, char const* val) +{ + switch (token) + { + case xml_parse_error: + f.ret.m_error = errors::parse_failed; + return; + case xml_start_tag: + case xml_empty_tag: + { + f.current_tag = name; + if (f.type == feed_state::none) + { + if (string_equal_no_case(f.current_tag.c_str(), "feed")) + f.type = feed_state::atom; + else if (string_equal_no_case(f.current_tag.c_str(), "rss")) + f.type = feed_state::rss2; + } + if (f.is_item(name)) f.in_item = true; + return; + } + case xml_attribute: + { + if (!f.in_item) return; + if (f.is_url(f.current_tag.c_str()) + && f.type == feed_state::atom) + { + // atom feeds have items like this: + // + if (string_equal_no_case(name, "href")) + f.current_item.url = val; + else if (string_equal_no_case(name, "length")) + f.current_item.size = strtoll(val, 0, 10); + } + else if (f.type == feed_state::rss2 + && string_equal_no_case(f.current_tag.c_str(), "enclosure")) + { + // rss feeds have items like this: + // + if (string_equal_no_case(name, "url")) + f.current_item.url = val; + else if (string_equal_no_case(name, "length")) + f.current_item.size = strtoll(val, 0, 10); + } + else if (f.type == feed_state::rss2 + && string_equal_no_case(f.current_tag.c_str(), "media:content")) + { + // rss feeds sometimes have items like this: + // + if (string_equal_no_case(name, "url")) + f.current_item.url = val; + else if (string_equal_no_case(name, "filesize")) + f.current_item.size = strtoll(val, 0, 10); + } + return; + } + case xml_end_tag: + { + if (f.in_item && f.is_item(name)) + { + f.in_item = false; + if (!f.current_item.title.empty() + && !f.current_item.url.empty()) + { + f.ret.add_item(f.current_item); + ++f.num_items; + } + f.current_item = feed_item(); + } + f.current_tag = ""; + return; + } + case xml_string: + { + if (!f.in_item) + { + if (f.is_title(f.current_tag.c_str())) + f.ret.m_title = name; + else if (f.is_desc(f.current_tag.c_str())) + f.ret.m_description = name; + else if (f.is_ttl(f.current_tag.c_str())) + { + int tmp = atoi(name); + if (tmp > 0) f.ret.m_ttl = tmp; + } + return; + } + if (f.is_title(f.current_tag.c_str())) + f.current_item.title = name; + else if (f.is_desc(f.current_tag.c_str())) + f.current_item.description = name; + else if (f.is_uuid(f.current_tag.c_str())) + f.current_item.uuid = name; + else if (f.is_url(f.current_tag.c_str()) && f.type != feed_state::atom) + f.current_item.url = name; + else if (f.is_comment(f.current_tag.c_str())) + f.current_item.comment = name; + else if (f.is_category(f.current_tag.c_str())) + f.current_item.category = name; + else if (f.is_size(f.current_tag.c_str())) + f.current_item.size = strtoll(name, 0, 10); + else if (f.is_hash(f.current_tag.c_str()) && strlen(name) == 40) + { + if (!from_hex(name, 40, (char*)&f.current_item.info_hash[0])) + { + // hex parsing failed + f.current_item.info_hash.clear(); + } + } + return; + } + case xml_declaration_tag: return; + case xml_comment: return; + } +} + +torrent_handle add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& tp, error_code& ec) +{ + add_torrent_params p = tp; + p.url = fi.url; + p.uuid = fi.uuid; + // #error figure out how to get the feed url in here +// p.source_feed_url = ???; + p.ti.reset(); + p.info_hash.clear(); + p.name = fi.title.c_str(); + return s.add_torrent(p, ec); +} + +#ifndef BOOST_NO_EXCEPTIONS +torrent_handle add_feed_item(session& s, feed_item const& fi + , add_torrent_params const& tp) +{ + error_code ec; + torrent_handle ret = add_feed_item(s, fi, tp, ec); + if (ec) throw libtorrent_exception(ec); + return ret; +} +#endif + +boost::shared_ptr new_feed(aux::session_impl& ses, feed_settings const& sett) +{ + return boost::shared_ptr(new feed(ses, sett)); +} + +feed::feed(aux::session_impl& ses, feed_settings const& sett) + : m_last_attempt(0) + , m_last_update(0) + , m_ttl(-1) + , m_failures(0) + , m_updating(false) + , m_settings(sett) + , m_ses(ses) +{ +} + +void feed::set_settings(feed_settings const& s) +{ + m_settings = s; +} + +void feed::get_settings(feed_settings* s) const +{ + *s = m_settings; +} + +feed_handle feed::my_handle() +{ + return feed_handle(boost::weak_ptr(shared_from_this())); +} + +void feed::on_feed(error_code const& ec + , http_parser const& parser, char const* data, int size) +{ + // enabling this assert makes the unit test a lot more difficult +// TORRENT_ASSERT(m_updating); + m_updating = false; + + if (ec && ec != asio::error::eof) + { + ++m_failures; + m_error = ec; + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_error, m_error)); + } + return; + } + + if (parser.status_code() != 200) + { + ++m_failures; + m_error = error_code(parser.status_code(), get_http_category()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_error, m_error)); + } + return; + } + + m_failures = 0; + + char* buf = const_cast(data); + + feed_state s(*this); + xml_parse(buf, buf + size, boost::bind(&parse_feed, boost::ref(s), _1, _2, _3)); + + time_t now = time(NULL); + + // keep history of the typical feed size times 5 + int max_history = (std::max)(s.num_items * 5, 100); + + // this is not very efficient, but that's probably OK for now + while (int(m_added.size()) > max_history) + { + // loop over all elements and find the one with the lowest timestamp + // i.e. it was added the longest ago, then remove it + std::map::iterator i = std::min_element( + m_added.begin(), m_added.end() + , boost::bind(&std::pair::second, _1) + < boost::bind(&std::pair::second, _2)); + m_added.erase(i); + } + + m_last_update = now; + + // report that we successfully updated the feed + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_updated, error_code())); + } + + // update m_ses.m_next_rss_update timestamps + // now that we have updated our timestamp + m_ses.update_rss_feeds(); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed_settings,x), t}, + bencode_map_entry feed_settings_map[] = + { + TORRENT_SETTING(std_string, url) + TORRENT_SETTING(boolean, auto_download) + TORRENT_SETTING(boolean, auto_map_handles) + TORRENT_SETTING(integer, default_ttl) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed_item,x), t}, + bencode_map_entry feed_item_map[] = + { + TORRENT_SETTING(std_string, url) + TORRENT_SETTING(std_string, uuid) + TORRENT_SETTING(std_string, title) + TORRENT_SETTING(std_string, description) + TORRENT_SETTING(std_string, comment) + TORRENT_SETTING(std_string, category) + TORRENT_SETTING(size_integer, size) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(feed,x), t}, + bencode_map_entry feed_map[] = + { + TORRENT_SETTING(std_string, m_title) + TORRENT_SETTING(std_string, m_description) + TORRENT_SETTING(time_integer, m_last_attempt) + TORRENT_SETTING(time_integer, m_last_update) + }; +#undef TORRENT_SETTING + +#define TORRENT_SETTING(t, x) {#x, offsetof(add_torrent_params,x), t}, + bencode_map_entry add_torrent_map[] = + { + TORRENT_SETTING(std_string, save_path) + TORRENT_SETTING(size_integer, flags) + }; +#undef TORRENT_SETTING + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +void feed::load_state(lazy_entry const& rd) +{ + load_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0])); + lazy_entry const* e = rd.dict_find_list("items"); + if (e) + { + m_items.reserve(e->list_size()); + for (int i = 0; i < e->list_size(); ++i) + { + if (e->list_at(i)->type() != lazy_entry::dict_t) continue; + m_items.push_back(feed_item()); + load_struct(*e->list_at(i), &m_items.back(), feed_item_map + , sizeof(feed_item_map)/sizeof(feed_item_map[0])); + + // don't load duplicates + if (m_urls.find(m_items.back().url) != m_urls.end()) + { + m_items.pop_back(); + continue; + } + m_urls.insert(m_items.back().url); + } + } + load_struct(rd, &m_settings, feed_settings_map + , sizeof(feed_settings_map)/sizeof(feed_settings_map[0])); + e = rd.dict_find_dict("add_params"); + if (e) + { + load_struct(*e, &m_settings.add_args, add_torrent_map + , sizeof(add_torrent_map)/sizeof(add_torrent_map[0])); + } + + e = rd.dict_find_list("history"); + if (e) + { + for (int i = 0; i < e->list_size(); ++i) + { + if (e->list_at(i)->type() != lazy_entry::list_t) continue; + + lazy_entry const* item = e->list_at(i); + + if (item->list_size() != 2 + || item->list_at(0)->type() != lazy_entry::string_t + || item->list_at(1)->type() != lazy_entry::int_t) + continue; + + m_added.insert(std::pair( + item->list_at(0)->string_value() + , item->list_at(1)->int_value())); + } + } +} + +void feed::save_state(entry& rd) const +{ + // feed properties + save_struct(rd, this, feed_map, sizeof(feed_map)/sizeof(feed_map[0])); + + // items + entry::list_type& items = rd["items"].list(); + for (std::vector::const_iterator i = m_items.begin() + , end(m_items.end()); i != end; ++i) + { + items.push_back(entry()); + entry& item = items.back(); + save_struct(item, &*i, feed_item_map, sizeof(feed_item_map)/sizeof(feed_item_map[0])); + } + + // settings + feed_settings sett_def; + save_struct(rd, &m_settings, feed_settings_map + , sizeof(feed_settings_map)/sizeof(feed_settings_map[0]), &sett_def); + entry& add = rd["add_params"]; + add_torrent_params add_def; + save_struct(add, &m_settings.add_args, add_torrent_map + , sizeof(add_torrent_map)/sizeof(add_torrent_map[0]), &add_def); + + entry::list_type& history = rd["history"].list(); + for (std::map::const_iterator i = m_added.begin() + , end(m_added.end()); i != end; ++i) + { + history.push_back(entry()); + entry::list_type& item = history.back().list(); + item.push_back(entry(i->first)); + item.push_back(entry(i->second)); + } +} + +void feed::add_item(feed_item const& item) +{ + // don't add duplicates + if (m_urls.find(item.url) != m_urls.end()) + return; + + m_urls.insert(item.url); + m_items.push_back(item); + + feed_item& i = m_items.back(); + + if (m_settings.auto_map_handles) + i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(rss_item_alert(my_handle(), i)); + + if (m_settings.auto_download) + { + if (!m_settings.auto_map_handles) + i.handle = torrent_handle(m_ses.find_torrent(i.uuid.empty() ? i.url : i.uuid)); + + // if we're already downloading this torrent + // move along to the next one + if (i.handle.is_valid()) return; + + // has this already been added? + if (m_added.find(i.url) != m_added.end()) return; + + // this means we should add this torrent to the session + add_torrent_params p = m_settings.add_args; + p.url = i.url; + p.uuid = i.uuid; + p.source_feed_url = m_settings.url; + p.ti.reset(); + p.info_hash.clear(); + p.name = i.title.c_str(); + + error_code e; + m_ses.add_torrent(p, e); + time_t now = time(NULL); + m_added.insert(make_pair(i.url, now)); + } +} + +// returns the number of seconds until trying again +int feed::update_feed() +{ + if (m_updating) return 60; + + m_last_attempt = time(0); + m_last_update = 0; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(rss_alert(my_handle(), m_settings.url + , rss_alert::state_updating, error_code())); + } + + boost::shared_ptr feed( + new http_connection(m_ses.m_io_service, m_ses.m_half_open + , boost::bind(&feed::on_feed, shared_from_this() + , _1, _2, _3, _4))); + + m_updating = true; + feed->get(m_settings.url, seconds(30), 0, 0, 5, m_ses.m_settings.user_agent); + + return 60 + m_failures * m_failures * 60; +} + +void feed::get_feed_status(feed_status* ret) const +{ + ret->items = m_items; + ret->last_update = m_last_update; + ret->updating = m_updating; + ret->url = m_settings.url; + ret->title = m_title; + ret->description = m_description; + ret->error = m_error; + ret->ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; + ret->next_update = next_update(time(0)); +} + +int feed::next_update(time_t now) const +{ + if (m_last_update == 0) return int(m_last_attempt + 60 * 5 - now); + int ttl = m_ttl == -1 ? m_settings.default_ttl : m_ttl; + TORRENT_ASSERT((m_last_update + ttl * 60) - now < INT_MAX); + return int((m_last_update + ttl * 60) - now); +} + +// defined in session.cpp +void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f); + +#define TORRENT_ASYNC_CALL(x) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (!f) return; \ + aux::session_impl& ses = f->session(); \ + ses.m_io_service.post(boost::bind(&feed:: x, f)) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (!f) return; \ + aux::session_impl& ses = f->session(); \ + ses.m_io_service.post(boost::bind(&feed:: x, f, a1)) + +#define TORRENT_SYNC_CALL1(x, a1) \ + boost::shared_ptr f = m_feed_ptr.lock(); \ + if (f) { \ + bool done = false; \ + aux::session_impl& ses = f->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&feed:: x, f, a1)))); \ + f.reset(); \ + do { ses.cond.wait(l); } while(!done); } + +feed_handle::feed_handle(boost::weak_ptr const& p) + : m_feed_ptr(p) {} + +void feed_handle::update_feed() +{ + TORRENT_ASYNC_CALL(update_feed); +} + +feed_status feed_handle::get_feed_status() const +{ + feed_status ret; + TORRENT_SYNC_CALL1(get_feed_status, &ret); + return ret; +} + +void feed_handle::set_settings(feed_settings const& s) +{ + TORRENT_ASYNC_CALL1(set_settings, s); +} + +feed_settings feed_handle::settings() const +{ + feed_settings ret; + TORRENT_SYNC_CALL1(get_settings, &ret); + return ret; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/session.cpp b/apps/Launcher/ext/libtorrent/src/session.cpp new file mode 100644 index 0000000000..c666349417 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/session.cpp @@ -0,0 +1,1417 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/extensions/ut_pex.hpp" +#include "libtorrent/extensions/ut_metadata.hpp" +#include "libtorrent/extensions/smart_ban.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/magnet_uri.hpp" + +using boost::shared_ptr; +using boost::weak_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + TORRENT_EXPORT void TORRENT_LINK_TEST_NAME() {} + + // this function returns a session_settings object + // which will optimize libtorrent for minimum memory + // usage, with no consideration of performance. + TORRENT_EXPORT session_settings min_memory_usage() + { + session_settings set; + + set.alert_queue_size = 100; + + set.max_allowed_in_request_queue = 100; + + // setting this to a low limit, means more + // peers are more likely to request from the + // same piece. Which means fewer partial + // pieces and fewer entries in the partial + // piece list + set.whole_pieces_threshold = 2; + set.use_parole_mode = false; + set.prioritize_partial_pieces = true; + + // connect to 5 peers per second + set.connection_speed = 5; + + // be extra nice on the hard drive when running + // on embedded devices. This might slow down + // torrent checking + set.file_checks_delay_per_block = 5; + + // only have 4 files open at a time + set.file_pool_size = 4; + + // we want to keep the peer list as small as possible + set.allow_multiple_connections_per_ip = false; + set.max_failcount = 2; + set.inactivity_timeout = 120; + + // whenever a peer has downloaded one block, write + // it to disk, and don't read anything from the + // socket until the disk write is complete + set.max_queued_disk_bytes = 1; + + // don't keep track of all upnp devices, keep + // the device list small + set.upnp_ignore_nonrouters = true; + + // never keep more than one 16kB block in + // the send buffer + set.send_buffer_watermark = 9; + + // don't use any disk cache + set.cache_size = 0; + set.cache_buffer_chunk_size = 1; + set.use_read_cache = false; + set.use_disk_read_ahead = false; + + set.close_redundant_connections = true; + + set.max_peerlist_size = 500; + set.max_paused_peerlist_size = 50; + + // udp trackers are cheaper to talk to + set.prefer_udp_trackers = true; + + set.max_rejects = 10; + + set.recv_socket_buffer_size = 16 * 1024; + set.send_socket_buffer_size = 16 * 1024; + + // use less memory when checking pieces + set.optimize_hashing_for_speed = false; + + // use less memory when reading and writing + // whole pieces + set.coalesce_reads = false; + set.coalesce_writes = false; + + // disallow the buffer size to grow for the uTP socket + set.utp_dynamic_sock_buf = false; + + // max 'bottled' http receive buffer/url torrent size + set.max_http_recv_buffer_size = 1024 * 1024; + + return set; + } + + TORRENT_EXPORT session_settings high_performance_seed() + { + session_settings set; + + // allow peers to request a lot of blocks at a time, + // to be more likely to saturate the bandwidth-delay- + // product. + set.max_out_request_queue = 1500; + set.max_allowed_in_request_queue = 2000; + + // don't throttle TCP, assume there is + // plenty of bandwidth + set.mixed_mode_algorithm = session_settings::prefer_tcp; + + // we will probably see a high rate of alerts, make it less + // likely to loose alerts + set.alert_queue_size = 50000; + + // allow 500 files open at a time + set.file_pool_size = 500; + + // don't update access time for each read/write + set.no_atime_storage = true; + + // as a seed box, we must accept multiple peers behind + // the same NAT + set.allow_multiple_connections_per_ip = true; + + // connect to 50 peers per second + set.connection_speed = 50; + + // allow 8000 peer connections + set.connections_limit = 8000; + + // allow lots of peers to try to connect simultaneously + set.listen_queue_size = 200; + + // unchoke many peers + set.unchoke_slots_limit = 500; + + // we need more DHT capacity to ping more peers + // candidates before trying to connect + set.dht_upload_rate_limit = 20000; + + // we're more interested in downloading than seeding + // only service a read job every 1000 write job (when + // disk is congested). Presumably on a big box, writes + // are extremely cheap and reads are relatively expensive + // so that's the main reason this ratio should be adjusted + set.read_job_every = 100; + + // use 1 GB of cache + set.cache_size = 32768 * 2; + set.use_read_cache = true; + set.cache_buffer_chunk_size = 128; + set.read_cache_line_size = 32; + set.write_cache_line_size = 32; + set.low_prio_disk = false; + // one hour expiration + set.cache_expiry = 60 * 60; + // this is expensive and could add significant + // delays when freeing a large number of buffers + set.lock_disk_cache = false; + + // the max number of bytes pending write before we throttle + // download rate + set.max_queued_disk_bytes = 10 * 1024 * 1024; + // flush write cache in a way to minimize the amount we need to + // read back once we want to hash-check the piece. i.e. try to + // flush all blocks in-order + set.disk_cache_algorithm = session_settings::avoid_readback; + + set.explicit_read_cache = false; + // prevent fast pieces to interfere with suggested pieces + // since we unchoke everyone, we don't need fast pieces anyway + set.allowed_fast_set_size = 0; + // suggest pieces in the read cache for higher cache hit rate + set.suggest_mode = session_settings::suggest_read_cache; + + set.close_redundant_connections = true; + + set.max_rejects = 10; + + set.recv_socket_buffer_size = 1024 * 1024; + set.send_socket_buffer_size = 1024 * 1024; + + set.optimize_hashing_for_speed = true; + + // don't let connections linger for too long + set.request_timeout = 10; + set.peer_timeout = 20; + set.inactivity_timeout = 20; + + set.active_limit = 2000; + set.active_tracker_limit = 2000; + set.active_dht_limit = 600; + set.active_seeds = 2000; + + set.choking_algorithm = session_settings::fixed_slots_choker; + + // in order to be able to deliver very high + // upload rates, this should be able to cover + // the bandwidth delay product. Assuming an RTT + // of 500 ms, and a send rate of 20 MB/s, the upper + // limit should be 10 MB + set.send_buffer_watermark = 3 * 1024 * 1024; + + // put 1.5 seconds worth of data in the send buffer + // this gives the disk I/O more heads-up on disk + // reads, and can maximize throughput + set.send_buffer_watermark_factor = 150; + + // always stuff at least 1 MiB down each peer + // pipe, to quickly ramp up send rates + set.send_buffer_low_watermark = 1 * 1024 * 1024; + + // don't retry peers if they fail once. Let them + // connect to us if they want to + set.max_failcount = 1; + + // allow the buffer size to grow for the uTP socket + set.utp_dynamic_sock_buf = true; + + // max 'bottled' http receive buffer/url torrent size + set.max_http_recv_buffer_size = 6 * 1024 * 1024; + + // the disk cache performs better with the pool allocator + set.use_disk_cache_pool = true; + + return set; + } + + // wrapper around a function that's executed in the network thread + // ans synchronized in the client thread + template + void fun_ret(R* ret, bool* done, condition_variable* e, mutex* m, boost::function f) + { + *ret = f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + + void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f) + { + f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + +#define TORRENT_ASYNC_CALL(x) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get())) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1)) + +#define TORRENT_ASYNC_CALL2(x, a1, a2) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)) + +#define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ + m_impl->m_io_service.dispatch(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)) + +#define TORRENT_WAIT \ + mutex::scoped_lock l(m_impl->mut); \ + while (!done) { m_impl->cond.wait(l); }; + +#define TORRENT_SYNC_CALL(x) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL1(x, a1) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL2(x, a1, a2) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL4(x, a1, a2, a3, a4) \ + bool done = false; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3, a4)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET(type, x) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET1(type, x, a1) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET2(type, x, a1, a2) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ + TORRENT_WAIT + +#define TORRENT_SYNC_CALL_RET3(type, x, a1, a2, a3) \ + bool done = false; \ + type r; \ + m_impl->m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ + TORRENT_WAIT + +#ifndef TORRENT_CFG +#error TORRENT_CFG is not defined! +#endif + + // this is a dummy function that's exported and named based + // on the configuration. The session.hpp file will reference + // it and if the library and the client are built with different + // configurations this will give a link error + void TORRENT_EXPORT TORRENT_CFG() {} + +#if defined _MSC_VER && defined TORRENT_DEBUG + static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) + { throw; } +#endif + + void session::init(std::pair listen_range, char const* listen_interface + , fingerprint const& id, boost::uint32_t alert_mask) + { +#if defined _MSC_VER && defined TORRENT_DEBUG + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff + ::_set_se_translator(straight_to_debugger); +#endif + + m_impl.reset(new session_impl(listen_range, id, listen_interface, alert_mask)); + } + + void session::set_log_path(std::string const& p) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING \ + || defined TORRENT_ERROR_LOGGING + m_impl->set_log_path(p); +#endif + } + + void session::start(int flags) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + if (flags & add_default_plugins) + { + add_extension(create_ut_pex_plugin); + add_extension(create_ut_metadata_plugin); + add_extension(create_smart_ban_plugin); + } +#endif + + m_impl->start_session(); + + if (flags & start_default_features) + { + start_upnp(); + start_natpmp(); +#ifndef TORRENT_DISABLE_DHT + start_dht(); +#endif + start_lsd(); + } + } + + session::~session() + { + TORRENT_ASSERT(m_impl); + // if there is at least one destruction-proxy + // abort the session and let the destructor + // of the proxy to syncronize + if (!m_impl.unique()) + { + TORRENT_ASYNC_CALL(abort); + } + } + + void session::save_state(entry& e, boost::uint32_t flags) const + { + TORRENT_SYNC_CALL2(save_state, &e, flags); + } + + void session::load_state(lazy_entry const& e) + { + // this needs to be synchronized since the lifespan + // of e is tied to the caller + TORRENT_SYNC_CALL1(load_state, &e); + } + + feed_handle session::add_feed(feed_settings const& feed) + { + // if you have auto-download enabled, you must specify a download directory! + TORRENT_ASSERT_PRECOND(!feed.auto_download || !feed.add_args.save_path.empty()); + TORRENT_SYNC_CALL_RET1(feed_handle, add_feed, feed); + return r; + } + + void session::remove_feed(feed_handle h) + { + TORRENT_ASYNC_CALL1(remove_feed, h); + } + + void session::get_feeds(std::vector& f) const + { + f.clear(); + TORRENT_SYNC_CALL1(get_feeds, &f); + } + + void session::add_extension(boost::function(torrent*, void*)> ext) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL1(add_extension, ext); +#endif + } + + void session::add_extension(boost::shared_ptr ext) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL1(add_ses_extension, ext); +#endif + } + + void session::load_asnum_db(char const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_asnum_db, std::string(file)); +#endif + } + + void session::load_country_db(char const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_country_db, std::string(file)); +#endif + } + + int session::as_for_ip(address const& addr) + { +#ifndef TORRENT_DISABLE_GEO_IP + return m_impl->as_for_ip(addr); +#else + return 0; +#endif + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void session::load_asnum_db(wchar_t const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_asnum_dbw, std::wstring(file)); +#endif + } + + void session::load_country_db(wchar_t const* file) + { +#ifndef TORRENT_DISABLE_GEO_IP + TORRENT_ASYNC_CALL1(load_country_dbw, std::wstring(file)); +#endif + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + +#ifndef TORRENT_NO_DEPRECATE + void session::load_state(entry const& ses_state) + { + if (ses_state.type() == entry::undefined_t) return; + std::vector buf; + bencode(std::back_inserter(buf), ses_state); + lazy_entry e; + error_code ec; +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS || !defined BOOST_NO_EXCEPTIONS + int ret = +#endif + lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec); + + TORRENT_ASSERT(ret == 0); +#ifndef BOOST_NO_EXCEPTIONS + if (ret != 0) throw libtorrent_exception(ec); +#endif + TORRENT_SYNC_CALL1(load_state, &e); + } + + entry session::state() const + { + entry ret; + TORRENT_SYNC_CALL2(save_state, &ret, 0xffffffff); + return ret; + } +#endif + + void session::set_ip_filter(ip_filter const& f) + { + TORRENT_ASYNC_CALL1(set_ip_filter, f); + } + + ip_filter session::get_ip_filter() const + { + TORRENT_SYNC_CALL_RET(ip_filter, get_ip_filter); + return r; + } + + void session::set_port_filter(port_filter const& f) + { + TORRENT_ASYNC_CALL1(set_port_filter, f); + } + + void session::set_peer_id(peer_id const& id) + { + TORRENT_ASYNC_CALL1(set_peer_id, id); + } + + peer_id session::id() const + { + TORRENT_SYNC_CALL_RET(peer_id, get_peer_id); + return r; + } + + io_service& session::get_io_service() + { + return m_impl->m_io_service; + } + + void session::set_key(int key) + { + TORRENT_ASYNC_CALL1(set_key, key); + } + + void session::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL3(get_torrent_status, ret, boost::ref(pred), flags); + } + + void session::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + TORRENT_SYNC_CALL2(refresh_torrent_status, ret, flags); + } + + void session::post_torrent_updates() + { + TORRENT_ASYNC_CALL(post_torrent_updates); + } + + std::vector session::get_torrents() const + { + TORRENT_SYNC_CALL_RET(std::vector, get_torrents); + return r; + } + + torrent_handle session::find_torrent(sha1_hash const& info_hash) const + { + TORRENT_SYNC_CALL_RET1(torrent_handle, find_torrent_handle, info_hash); + return r; + } + +#ifndef BOOST_NO_EXCEPTIONS + torrent_handle session::add_torrent(add_torrent_params const& params) + { + error_code ec; + TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); + if (ec) throw libtorrent_exception(ec); + return r; + } +#endif + + torrent_handle session::add_torrent(add_torrent_params const& params, error_code& ec) + { + ec.clear(); + TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); + return r; + } + + void session::async_add_torrent(add_torrent_params const& params) + { + add_torrent_params* p = new add_torrent_params(params); +#ifndef TORRENT_NO_DEPRECATE + if (params.tracker_url) + { + p->trackers.push_back(params.tracker_url); + p->tracker_url = NULL; + } +#endif + TORRENT_ASYNC_CALL1(async_add_torrent, p); + } + +#ifndef BOOST_NO_EXCEPTIONS +#ifndef TORRENT_NO_DEPRECATE + // if the torrent already exists, this will throw duplicate_torrent + torrent_handle session::add_torrent( + torrent_info const& ti + , std::string const& save_path + , entry const& resume_data + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc) + { + boost::intrusive_ptr tip(new torrent_info(ti)); + add_torrent_params p(sc); + p.ti = tip; + p.save_path = save_path; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(p.resume_data), resume_data); + } + p.storage_mode = storage_mode; + p.paused = paused; + return add_torrent(p); + } + + torrent_handle session::add_torrent( + boost::intrusive_ptr ti + , std::string const& save_path + , entry const& resume_data + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params p(sc); + p.ti = ti; + p.save_path = save_path; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(p.resume_data), resume_data); + } + p.storage_mode = storage_mode; + p.paused = paused; + p.userdata = userdata; + return add_torrent(p); + } + + torrent_handle session::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , std::string const& save_path + , entry const& e + , storage_mode_t storage_mode + , bool paused + , storage_constructor_type sc + , void* userdata) + { + add_torrent_params p(sc); + p.tracker_url = tracker_url; + p.info_hash = info_hash; + p.save_path = save_path; + p.paused = paused; + p.userdata = userdata; + return add_torrent(p); + } +#endif // TORRENT_NO_DEPRECATE +#endif // BOOST_NO_EXCEPTIONS + + void session::remove_torrent(const torrent_handle& h, int options) + { + if (!h.is_valid()) +#ifdef BOOST_NO_EXCEPTIONS + return; +#else + throw_invalid_handle(); +#endif + TORRENT_ASYNC_CALL2(remove_torrent, h, options); + } + +#ifndef TORRENT_NO_DEPRECATE + bool session::listen_on( + std::pair const& port_range + , const char* net_interface, int flags) + { + error_code ec; + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); + return !!ec; + } +#endif + + void session::listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface, int flags) + { + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); + } + + unsigned short session::listen_port() const + { + TORRENT_SYNC_CALL_RET(unsigned short, listen_port); + return r; + } + + unsigned short session::ssl_listen_port() const + { + TORRENT_SYNC_CALL_RET(unsigned short, ssl_listen_port); + return r; + } + + session_status session::status() const + { + TORRENT_SYNC_CALL_RET(session_status, status); + return r; + } + + void session::pause() + { + TORRENT_ASYNC_CALL(pause); + } + + void session::resume() + { + TORRENT_ASYNC_CALL(resume); + } + + bool session::is_paused() const + { + TORRENT_SYNC_CALL_RET(bool, is_paused); + return r; + } + + void session::get_cache_info(sha1_hash const& ih + , std::vector& ret) const + { + m_impl->m_disk_thread.get_cache_info(ih, ret); + } + + cache_status session::get_cache_status() const + { + return m_impl->m_disk_thread.status(); + } + + void session::start_dht() + { +#ifndef TORRENT_DISABLE_DHT + // the state is loaded in load_state() + TORRENT_ASYNC_CALL(start_dht); +#endif + } + + void session::stop_dht() + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL(stop_dht); +#endif + } + + void session::set_dht_settings(dht_settings const& settings) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(set_dht_settings, settings); +#endif + } + +#ifndef TORRENT_NO_DEPRECATE + void session::start_dht(entry const& startup_state) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(start_dht, startup_state); +#endif + } + + entry session::dht_state() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(entry, dht_state); + return r; +#else + return entry(); +#endif + } +#endif // TORRENT_NO_DEPRECATE + + void session::add_dht_node(std::pair const& node) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(add_dht_node_name, node); +#endif + } + + void session::add_dht_router(std::pair const& node) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(add_dht_router, node); +#endif + } + + bool session::is_dht_running() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(bool, is_dht_running); + return r; +#else + return false; +#endif + } + + void session::dht_get_item(sha1_hash const& target) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(dht_get_immutable_item, target); +#endif + } + + void session::dht_get_item(boost::array key + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL2(dht_get_mutable_item, key, salt); +#endif + } + + sha1_hash session::dht_put_item(entry data) + { + std::vector buf; + bencode(std::back_inserter(buf), data); + sha1_hash ret = hasher(&buf[0], buf.size()).final(); + +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL2(dht_put_item, data, ret); +#endif + return ret; + } + + void session::dht_put_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL3(dht_put_mutable_item, key, cb, salt); +#endif + } + + void session::set_pe_settings(pe_settings const& settings) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASYNC_CALL1(set_pe_settings, settings); +#endif + } + + pe_settings session::get_pe_settings() const + { +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_SYNC_CALL_RET(pe_settings, get_pe_settings); +#else + pe_settings r; +#endif + return r; + } + + bool session::is_listening() const + { + TORRENT_SYNC_CALL_RET(bool, is_listening); + return r; + } + + void session::set_settings(session_settings const& s) + { + TORRENT_ASYNC_CALL1(set_settings, s); + } + + session_settings session::settings() const + { + TORRENT_SYNC_CALL_RET(session_settings, settings); + return r; + } + + void session::set_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_proxy, s); + } + + proxy_settings session::proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, proxy); + return r; + } + +#ifndef TORRENT_NO_DEPRECATE + void session::set_peer_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_peer_proxy, s); + } + + void session::set_web_seed_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_web_seed_proxy, s); + } + + void session::set_tracker_proxy(proxy_settings const& s) + { + TORRENT_ASYNC_CALL1(set_tracker_proxy, s); + } + + proxy_settings session::peer_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, peer_proxy); + return r; + } + + proxy_settings session::web_seed_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, web_seed_proxy); + return r; + } + + proxy_settings session::tracker_proxy() const + { + TORRENT_SYNC_CALL_RET(proxy_settings, tracker_proxy); + return r; + } + + + void session::set_dht_proxy(proxy_settings const& s) + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL1(set_dht_proxy, s); +#endif + } + + proxy_settings session::dht_proxy() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_SYNC_CALL_RET(proxy_settings, dht_proxy); + return r; +#else + return proxy_settings(); +#endif + } +#endif // TORRENT_NO_DEPRECATE + + void session::set_i2p_proxy(proxy_settings const& s) + { +#if TORRENT_USE_I2P + TORRENT_ASYNC_CALL1(set_i2p_proxy, s); +#endif + } + + proxy_settings session::i2p_proxy() const + { +#if TORRENT_USE_I2P + TORRENT_SYNC_CALL_RET(proxy_settings, i2p_proxy); +#else + proxy_settings r; +#endif + return r; + } + +#ifdef TORRENT_STATS + void session::enable_stats_logging(bool s) + { + TORRENT_ASYNC_CALL1(enable_stats_logging, s); + } +#endif + +#ifndef TORRENT_NO_DEPRECATE + int session::max_uploads() const + { + TORRENT_SYNC_CALL_RET(int, max_uploads); + return r; + } + + void session::set_max_uploads(int limit) + { + TORRENT_ASYNC_CALL1(set_max_uploads, limit); + } + + int session::max_connections() const + { + TORRENT_SYNC_CALL_RET(int, max_connections); + return r; + } + + void session::set_max_connections(int limit) + { + TORRENT_ASYNC_CALL1(set_max_connections, limit); + } + + int session::max_half_open_connections() const + { + TORRENT_SYNC_CALL_RET(int, max_half_open_connections); + return r; + } + + void session::set_max_half_open_connections(int limit) + { + TORRENT_ASYNC_CALL1(set_max_half_open_connections, limit); + } + + int session::local_upload_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, local_upload_rate_limit); + return r; + } + + int session::local_download_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, local_download_rate_limit); + return r; + } + + int session::upload_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, upload_rate_limit); + return r; + } + + int session::download_rate_limit() const + { + TORRENT_SYNC_CALL_RET(int, download_rate_limit); + return r; + } + + void session::set_local_upload_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_local_upload_rate_limit, bytes_per_second); + } + + void session::set_local_download_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_local_download_rate_limit, bytes_per_second); + } + + void session::set_upload_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_upload_rate_limit, bytes_per_second); + } + + void session::set_download_rate_limit(int bytes_per_second) + { + TORRENT_ASYNC_CALL1(set_download_rate_limit, bytes_per_second); + } + + int session::num_uploads() const + { + TORRENT_SYNC_CALL_RET(int, num_uploads); + return r; + } + + int session::num_connections() const + { + TORRENT_SYNC_CALL_RET(int, num_connections); + return r; + } +#endif // TORRENT_NO_DEPRECATE + + void session::set_alert_dispatch(boost::function)> const& fun) + { + TORRENT_ASYNC_CALL1(set_alert_dispatch, fun); + } + + std::auto_ptr session::pop_alert() + { + return m_impl->pop_alert(); + } + + void session::pop_alerts(std::deque* alerts) + { + for (std::deque::iterator i = alerts->begin() + , end(alerts->end()); i != end; ++i) + delete *i; + alerts->clear(); + m_impl->pop_alerts(alerts); + } + + alert const* session::wait_for_alert(time_duration max_wait) + { + return m_impl->wait_for_alert(max_wait); + } + + void session::set_alert_mask(boost::uint32_t m) + { + TORRENT_ASYNC_CALL1(set_alert_mask, m); + } + +#ifndef TORRENT_NO_DEPRECATE + size_t session::set_alert_queue_size_limit(size_t queue_size_limit_) + { + TORRENT_SYNC_CALL_RET1(size_t, set_alert_queue_size_limit, queue_size_limit_); + return r; + } + + void session::set_severity_level(alert::severity_t s) + { + int m = 0; + switch (s) + { + case alert::debug: m = alert::all_categories; break; + case alert::info: m = alert::all_categories & ~(alert::debug_notification + | alert::progress_notification | alert::dht_notification); break; + case alert::warning: m = alert::all_categories & ~(alert::debug_notification + | alert::status_notification | alert::progress_notification + | alert::dht_notification); break; + case alert::critical: m = alert::error_notification | alert::storage_notification; break; + case alert::fatal: m = alert::error_notification; break; + default: break; + } + + TORRENT_ASYNC_CALL1(set_alert_mask, m); + } +#endif + + void session::start_lsd() + { + TORRENT_ASYNC_CALL(start_lsd); + } + + void session::start_natpmp() + { + TORRENT_ASYNC_CALL(start_natpmp); + } + + void session::start_upnp() + { + TORRENT_ASYNC_CALL(start_upnp); + } + + int session::add_port_mapping(protocol_type t, int external_port, int local_port) + { + TORRENT_SYNC_CALL_RET3(int, add_port_mapping, int(t), external_port, local_port); + return r; + } + + void session::delete_port_mapping(int handle) + { + TORRENT_ASYNC_CALL1(delete_port_mapping, handle); + } + + void session::stop_lsd() + { + TORRENT_ASYNC_CALL(stop_lsd); + } + + void session::stop_natpmp() + { + TORRENT_ASYNC_CALL(stop_natpmp); + } + + void session::stop_upnp() + { + TORRENT_ASYNC_CALL(stop_upnp); + } + + connection_queue& session::get_connection_queue() + { + return m_impl->m_half_open; + } + + session_settings::session_settings(std::string const& user_agent_) + : version(LIBTORRENT_VERSION_NUM) + , user_agent(user_agent_) + , tracker_completion_timeout(30) + , tracker_receive_timeout(10) + , stop_tracker_timeout(5) + , tracker_maximum_response_length(1024*1024) + , piece_timeout(20) + , request_timeout(50) + , request_queue_time(3) + , max_allowed_in_request_queue(500) + , max_out_request_queue(500) + , whole_pieces_threshold(20) + , peer_timeout(120) + , urlseed_timeout(20) + , urlseed_pipeline_size(5) + , urlseed_wait_retry(30) + , file_pool_size(40) + , allow_multiple_connections_per_ip(false) + , max_failcount(3) + , min_reconnect_time(60) + , peer_connect_timeout(15) + , ignore_limits_on_local_network(true) + , connection_speed(6) + , send_redundant_have(true) + , lazy_bitfields(false) + , inactivity_timeout(600) + , unchoke_interval(15) + , optimistic_unchoke_interval(30) + , num_want(200) + , initial_picker_threshold(4) + , allowed_fast_set_size(10) + , suggest_mode(no_piece_suggestions) + , max_queued_disk_bytes(1024 * 1024) + , max_queued_disk_bytes_low_watermark(0) + , handshake_timeout(10) + , use_dht_as_fallback(false) + , free_torrent_hashes(true) + , upnp_ignore_nonrouters(false) + , send_buffer_low_watermark(512) + , send_buffer_watermark(500 * 1024) + , send_buffer_watermark_factor(50) +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + , auto_upload_slots(true) + , auto_upload_slots_rate_based(true) +#endif + , choking_algorithm(fixed_slots_choker) + , seed_choking_algorithm(round_robin) + , use_parole_mode(true) + , cache_size(1024) + , cache_buffer_chunk_size(16) + , cache_expiry(300) + , use_read_cache(true) + , explicit_read_cache(0) + , explicit_cache_interval(30) + , disk_io_write_mode(0) + , disk_io_read_mode(0) + , coalesce_reads(false) + , coalesce_writes(false) + , outgoing_ports(0,0) + , peer_tos(0) + , active_downloads(3) + , active_seeds(5) + , active_dht_limit(88) // don't announce more than once every 40 seconds + , active_tracker_limit(1600) // don't announce to trackers more than once every 1.125 seconds + , active_lsd_limit(60) // don't announce to local network more than once every 5 seconds + , active_limit(15) + , auto_manage_prefer_seeds(false) + , dont_count_slow_torrents(true) + , auto_manage_interval(30) + , share_ratio_limit(2.f) + , seed_time_ratio_limit(7.f) + , seed_time_limit(24 * 60 * 60) // 24 hours + , peer_turnover_interval(300) + , peer_turnover(2 / 50.f) + , peer_turnover_cutoff(.9f) + , close_redundant_connections(true) + , auto_scrape_interval(1800) + , auto_scrape_min_interval(300) + , max_peerlist_size(4000) + , max_paused_peerlist_size(4000) + , min_announce_interval(5 * 60) + , prioritize_partial_pieces(false) + , auto_manage_startup(60) + , rate_limit_ip_overhead(true) + , announce_to_all_trackers(false) + , announce_to_all_tiers(false) + , prefer_udp_trackers(true) + , strict_super_seeding(false) + , seeding_piece_quota(20) +#ifdef TORRENT_WINDOWS + , max_sparse_regions(30000) +#else + , max_sparse_regions(0) +#endif + , lock_disk_cache(false) + , max_rejects(50) + , recv_socket_buffer_size(0) + , send_socket_buffer_size(0) + , optimize_hashing_for_speed(true) + , file_checks_delay_per_block(0) + , disk_cache_algorithm(avoid_readback) + , read_cache_line_size(32) + , write_cache_line_size(32) + , optimistic_disk_retry(10 * 60) + , disable_hash_checks(false) + , allow_reordered_disk_operations(true) + , allow_i2p_mixed(false) + , max_suggest_pieces(10) + , drop_skipped_requests(false) + , low_prio_disk(true) + , local_service_announce_interval(5 * 60) + , dht_announce_interval(15 * 60) + , udp_tracker_token_expiry(60) + , volatile_read_cache(false) + , guided_read_cache(false) + , default_cache_min_age(1) + , num_optimistic_unchoke_slots(0) + , no_atime_storage(true) + , default_est_reciprocation_rate(16000) + , increase_est_reciprocation_rate(20) + , decrease_est_reciprocation_rate(3) + , incoming_starts_queued_torrents(false) + , report_true_downloaded(false) + , strict_end_game_mode(true) + , broadcast_lsd(true) + , enable_outgoing_utp(true) + , enable_incoming_utp(true) + , enable_outgoing_tcp(true) + , enable_incoming_tcp(true) + , max_pex_peers(50) + , ignore_resume_timestamps(false) + , no_recheck_incomplete_resume(false) + , anonymous_mode(false) + , force_proxy(false) + , tick_interval(500) + , report_web_seed_downloads(true) + , share_mode_target(3) + , upload_rate_limit(0) + , download_rate_limit(0) + , local_upload_rate_limit(0) + , local_download_rate_limit(0) + , dht_upload_rate_limit(4000) + , unchoke_slots_limit(8) + , half_open_limit(0) + , connections_limit(200) + , connections_slack(10) + , utp_target_delay(100) // milliseconds + , utp_gain_factor(1500) // bytes per rtt + , utp_min_timeout(500) // milliseconds + , utp_syn_resends(2) + , utp_fin_resends(2) + , utp_num_resends(6) + , utp_connect_timeout(3000) // milliseconds +#ifndef TORRENT_NO_DEPRECATE + , utp_delayed_ack(0) // milliseconds +#endif + , utp_dynamic_sock_buf(false) // this doesn't seem quite reliable yet + , utp_loss_multiplier(50) // specified in percent + , mixed_mode_algorithm(peer_proportional) + , rate_limit_utp(true) + , listen_queue_size(5) + , announce_double_nat(false) + , torrent_connect_boost(10) + , seeding_outgoing_connections(true) + , no_connect_privileged_ports(true) + , alert_queue_size(6000) + , max_metadata_size(3*1024*1024) + , smooth_connects(true) + , always_send_user_agent(false) + , apply_ip_filter_to_trackers(true) + , read_job_every(10) + , use_disk_read_ahead(true) + , lock_files(false) + , ssl_listen(4433) + , tracker_backoff(250) + , ban_web_seeds(true) + , max_http_recv_buffer_size(4*1024*1024) + , support_share_mode(true) + , support_merkle_torrents(false) + , report_redundant_bytes(true) + , use_disk_cache_pool(false) + , inactive_down_rate(2048) + , inactive_up_rate(2048) + {} + + session_settings::~session_settings() {} +} + diff --git a/apps/Launcher/ext/libtorrent/src/session_impl.cpp b/apps/Launcher/ext/libtorrent/src/session_impl.cpp new file mode 100644 index 0000000000..cc22d6aefd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/session_impl.cpp @@ -0,0 +1,6658 @@ +/* + +Copyright (c) 2006-2014, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef TORRENT_USE_VALGRIND +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#ifndef TORRENT_DISABLE_DHT +#include "libtorrent/kademlia/dht_tracker.hpp" +#endif +#include "libtorrent/enum_net.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/lsd.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/settings.hpp" +#include "libtorrent/build_config.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/magnet_uri.hpp" + +#if defined TORRENT_STATS && defined __MACH__ +#include +#endif + +#ifndef TORRENT_WINDOWS +#include +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + +// for logging stat layout +#include "libtorrent/stat.hpp" + +// for logging the size of DHT structures +#ifndef TORRENT_DISABLE_DHT +#include +#include +#include +#include +#include +#endif // TORRENT_DISABLE_DHT + +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" + +#include "libtorrent/debug.hpp" + +#if TORRENT_USE_IOSTREAM +namespace libtorrent { +std::ofstream logger::log_file; +std::string logger::open_filename; +mutex logger::file_mutex; +} +#endif // TORRENT_USE_IOSTREAM + +#endif + +#ifdef TORRENT_USE_GCRYPT + +extern "C" { +GCRY_THREAD_OPTION_PTHREAD_IMPL; +} + +namespace +{ + // libgcrypt requires this to initialize the library + struct gcrypt_setup + { + gcrypt_setup() + { + gcry_check_version(0); + gcry_error_t e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + if (e != 0) fprintf(stderr, "libcrypt ERROR: %s\n", gcry_strerror(e)); + e = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + if (e != 0) fprintf(stderr, "initialization finished error: %s\n", gcry_strerror(e)); + } + } gcrypt_global_constructor; +} + +#endif // TORRENT_USE_GCRYPT + +#ifdef TORRENT_USE_OPENSSL + +#include +#include + +namespace +{ + // openssl requires this to clean up internal + // structures it allocates + struct openssl_cleanup + { + ~openssl_cleanup() { CRYPTO_cleanup_all_ex_data(); } + } openssl_global_destructor; +} + +#endif // TORRENT_USE_OPENSSL + +#ifdef TORRENT_WINDOWS +// for ERROR_SEM_TIMEOUT +#include +#endif + +using boost::shared_ptr; +using boost::weak_ptr; +using libtorrent::aux::session_impl; + +#ifdef BOOST_NO_EXCEPTIONS +namespace boost { + void throw_exception(std::exception const& e) { ::abort(); } +} +#endif + +namespace libtorrent { + +#if defined TORRENT_ASIO_DEBUGGING + std::map _async_ops; + int _async_ops_nthreads = 0; + mutex _async_ops_mutex; +#endif + +namespace detail +{ + + std::string generate_auth_string(std::string const& user + , std::string const& passwd) + { + if (user.empty()) return std::string(); + return user + ":" + passwd; + } +} + +namespace aux { + +#ifdef TORRENT_STATS + void get_vm_stats(vm_statistics_data_t* vm_stat) + { + memset(vm_stat, 0, sizeof(*vm_stat)); +#if defined __MACH__ + mach_port_t host_port = mach_host_self(); + mach_msg_type_number_t host_count = HOST_VM_INFO_COUNT; + kern_return_t error = host_statistics(host_port, HOST_VM_INFO, + (host_info_t)vm_stat, &host_count); + TORRENT_ASSERT_VAL(error == KERN_SUCCESS, error); +#elif defined TORRENT_LINUX + char buffer[4096]; + char string[1024]; + boost::uint32_t value; + FILE* f = fopen("/proc/vmstat", "r"); + int ret = 0; + while ((ret = fscanf(f, "%s %u\n", string, &value)) != EOF) + { + if (ret != 2) continue; + if (strcmp(string, "nr_active_anon") == 0) vm_stat->active_count += value; + else if (strcmp(string, "nr_active_file") == 0) vm_stat->active_count += value; + else if (strcmp(string, "nr_inactive_anon") == 0) vm_stat->inactive_count += value; + else if (strcmp(string, "nr_inactive_file") == 0) vm_stat->inactive_count += value; + else if (strcmp(string, "nr_free_pages") == 0) vm_stat->free_count = value; + else if (strcmp(string, "nr_unevictable") == 0) vm_stat->wire_count = value; + else if (strcmp(string, "pswpin") == 0) vm_stat->pageins = value; + else if (strcmp(string, "pswpout") == 0) vm_stat->pageouts = value; + else if (strcmp(string, "pgfault") == 0) vm_stat->faults = value; + } + fclose(f); +#endif +// TOOD: windows? + } + + void get_thread_cpu_usage(thread_cpu_usage* tu) + { +#if defined __MACH__ + task_thread_times_info t_info; + mach_msg_type_number_t t_info_count = TASK_THREAD_TIMES_INFO_COUNT; + task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&t_info, &t_info_count); + + tu->user_time = min_time() + + seconds(t_info.user_time.seconds) + + microsec(t_info.user_time.microseconds); + tu->system_time = min_time() + + seconds(t_info.system_time.seconds) + + microsec(t_info.system_time.microseconds); +#elif defined TORRENT_LINUX + struct rusage ru; + getrusage(RUSAGE_THREAD, &ru); + tu->user_time = min_time() + + seconds(ru.ru_utime.tv_sec) + + microsec(ru.ru_utime.tv_usec); + tu->system_time = min_time() + + seconds(ru.ru_stime.tv_sec) + + microsec(ru.ru_stime.tv_usec); +#elif defined TORRENT_WINDOWS + FILETIME system_time; + FILETIME user_time; + FILETIME creation_time; + FILETIME exit_time; + GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, &user_time, &system_time); + + boost::uint64_t utime = (boost::uint64_t(user_time.dwHighDateTime) << 32) + + user_time.dwLowDateTime; + boost::uint64_t stime = (boost::uint64_t(system_time.dwHighDateTime) << 32) + + system_time.dwLowDateTime; + + tu->user_time = min_time() + microsec(utime / 10); + tu->system_time = min_time() + microsec(stime / 10); +#endif + } +#endif //TORRENT_STATS + + struct seed_random_generator + { + seed_random_generator() + { + random_seed((unsigned int)((total_microseconds( + time_now_hires() - min_time())) & 0xffffffff)); + } + }; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#define TORRENT_SETTING(t, x) {#x, offsetof(session_settings,x), t}, + + bencode_map_entry session_settings_map[] = + { + TORRENT_SETTING(std_string, user_agent) + TORRENT_SETTING(integer, tracker_completion_timeout) + TORRENT_SETTING(integer, tracker_receive_timeout) + TORRENT_SETTING(integer, stop_tracker_timeout) + TORRENT_SETTING(integer, tracker_maximum_response_length) + TORRENT_SETTING(integer, piece_timeout) + TORRENT_SETTING(integer, request_timeout) + TORRENT_SETTING(integer, request_queue_time) + TORRENT_SETTING(integer, max_allowed_in_request_queue) + TORRENT_SETTING(integer, max_out_request_queue) + TORRENT_SETTING(integer, whole_pieces_threshold) + TORRENT_SETTING(integer, peer_timeout) + TORRENT_SETTING(integer, urlseed_timeout) + TORRENT_SETTING(integer, urlseed_pipeline_size) + TORRENT_SETTING(integer, urlseed_wait_retry) + TORRENT_SETTING(integer, file_pool_size) + TORRENT_SETTING(boolean, allow_multiple_connections_per_ip) + TORRENT_SETTING(integer, max_failcount) + TORRENT_SETTING(integer, min_reconnect_time) + TORRENT_SETTING(integer, peer_connect_timeout) + TORRENT_SETTING(boolean, ignore_limits_on_local_network) + TORRENT_SETTING(integer, connection_speed) + TORRENT_SETTING(boolean, send_redundant_have) + TORRENT_SETTING(boolean, lazy_bitfields) + TORRENT_SETTING(integer, inactivity_timeout) + TORRENT_SETTING(integer, unchoke_interval) + TORRENT_SETTING(integer, optimistic_unchoke_interval) + TORRENT_SETTING(std_string, announce_ip) + TORRENT_SETTING(integer, num_want) + TORRENT_SETTING(integer, initial_picker_threshold) + TORRENT_SETTING(integer, allowed_fast_set_size) + TORRENT_SETTING(integer, suggest_mode) + TORRENT_SETTING(integer, max_queued_disk_bytes) + TORRENT_SETTING(integer, max_queued_disk_bytes_low_watermark) + TORRENT_SETTING(integer, handshake_timeout) +#ifndef TORRENT_DISABLE_DHT + TORRENT_SETTING(boolean, use_dht_as_fallback) +#endif + TORRENT_SETTING(boolean, free_torrent_hashes) + TORRENT_SETTING(boolean, upnp_ignore_nonrouters) + TORRENT_SETTING(integer, send_buffer_low_watermark) + TORRENT_SETTING(integer, send_buffer_watermark) + TORRENT_SETTING(integer, send_buffer_watermark_factor) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(boolean, auto_upload_slots) + TORRENT_SETTING(boolean, auto_upload_slots_rate_based) +#endif + TORRENT_SETTING(integer, choking_algorithm) + TORRENT_SETTING(integer, seed_choking_algorithm) + TORRENT_SETTING(boolean, use_parole_mode) + TORRENT_SETTING(integer, cache_size) + TORRENT_SETTING(integer, cache_buffer_chunk_size) + TORRENT_SETTING(integer, cache_expiry) + TORRENT_SETTING(boolean, use_read_cache) + TORRENT_SETTING(boolean, explicit_read_cache) + TORRENT_SETTING(integer, disk_io_write_mode) + TORRENT_SETTING(integer, disk_io_read_mode) + TORRENT_SETTING(boolean, coalesce_reads) + TORRENT_SETTING(boolean, coalesce_writes) + TORRENT_SETTING(character, peer_tos) + TORRENT_SETTING(integer, active_downloads) + TORRENT_SETTING(integer, active_seeds) + TORRENT_SETTING(integer, active_dht_limit) + TORRENT_SETTING(integer, active_tracker_limit) + TORRENT_SETTING(integer, active_lsd_limit) + TORRENT_SETTING(integer, active_limit) + TORRENT_SETTING(boolean, auto_manage_prefer_seeds) + TORRENT_SETTING(boolean, dont_count_slow_torrents) + TORRENT_SETTING(integer, auto_manage_interval) + TORRENT_SETTING(floating_point, share_ratio_limit) + TORRENT_SETTING(floating_point, seed_time_ratio_limit) + TORRENT_SETTING(integer, seed_time_limit) + TORRENT_SETTING(floating_point, peer_turnover) + TORRENT_SETTING(floating_point, peer_turnover_cutoff) + TORRENT_SETTING(boolean, close_redundant_connections) + TORRENT_SETTING(integer, auto_scrape_interval) + TORRENT_SETTING(integer, auto_scrape_min_interval) + TORRENT_SETTING(integer, max_peerlist_size) + TORRENT_SETTING(integer, max_paused_peerlist_size) + TORRENT_SETTING(integer, min_announce_interval) + TORRENT_SETTING(boolean, prioritize_partial_pieces) + TORRENT_SETTING(integer, auto_manage_startup) + TORRENT_SETTING(boolean, rate_limit_ip_overhead) + TORRENT_SETTING(boolean, announce_to_all_trackers) + TORRENT_SETTING(boolean, announce_to_all_tiers) + TORRENT_SETTING(boolean, prefer_udp_trackers) + TORRENT_SETTING(boolean, strict_super_seeding) + TORRENT_SETTING(integer, seeding_piece_quota) + TORRENT_SETTING(integer, max_sparse_regions) +#ifndef TORRENT_DISABLE_MLOCK + TORRENT_SETTING(boolean, lock_disk_cache) +#endif + TORRENT_SETTING(integer, max_rejects) + TORRENT_SETTING(integer, recv_socket_buffer_size) + TORRENT_SETTING(integer, send_socket_buffer_size) + TORRENT_SETTING(boolean, optimize_hashing_for_speed) + TORRENT_SETTING(integer, file_checks_delay_per_block) + TORRENT_SETTING(integer, disk_cache_algorithm) + TORRENT_SETTING(integer, read_cache_line_size) + TORRENT_SETTING(integer, write_cache_line_size) + TORRENT_SETTING(integer, optimistic_disk_retry) + TORRENT_SETTING(boolean, disable_hash_checks) + TORRENT_SETTING(boolean, allow_reordered_disk_operations) + TORRENT_SETTING(boolean, allow_i2p_mixed) + TORRENT_SETTING(integer, max_suggest_pieces) + TORRENT_SETTING(boolean, drop_skipped_requests) + TORRENT_SETTING(boolean, low_prio_disk) + TORRENT_SETTING(integer, local_service_announce_interval) + TORRENT_SETTING(integer, dht_announce_interval) + TORRENT_SETTING(integer, udp_tracker_token_expiry) + TORRENT_SETTING(boolean, volatile_read_cache) + TORRENT_SETTING(boolean, guided_read_cache) + TORRENT_SETTING(integer, default_cache_min_age) + TORRENT_SETTING(integer, num_optimistic_unchoke_slots) + TORRENT_SETTING(boolean, no_atime_storage) + TORRENT_SETTING(integer, default_est_reciprocation_rate) + TORRENT_SETTING(integer, increase_est_reciprocation_rate) + TORRENT_SETTING(integer, decrease_est_reciprocation_rate) + TORRENT_SETTING(boolean, incoming_starts_queued_torrents) + TORRENT_SETTING(boolean, report_true_downloaded) + TORRENT_SETTING(boolean, strict_end_game_mode) + TORRENT_SETTING(boolean, broadcast_lsd) + TORRENT_SETTING(boolean, enable_outgoing_utp) + TORRENT_SETTING(boolean, enable_incoming_utp) + TORRENT_SETTING(boolean, enable_outgoing_tcp) + TORRENT_SETTING(boolean, enable_incoming_tcp) + TORRENT_SETTING(integer, max_pex_peers) + TORRENT_SETTING(boolean, ignore_resume_timestamps) + TORRENT_SETTING(boolean, no_recheck_incomplete_resume) + TORRENT_SETTING(boolean, anonymous_mode) + TORRENT_SETTING(boolean, force_proxy) + TORRENT_SETTING(integer, tick_interval) + TORRENT_SETTING(boolean, report_web_seed_downloads) + TORRENT_SETTING(integer, share_mode_target) + TORRENT_SETTING(integer, upload_rate_limit) + TORRENT_SETTING(integer, download_rate_limit) + TORRENT_SETTING(integer, local_upload_rate_limit) + TORRENT_SETTING(integer, local_download_rate_limit) + TORRENT_SETTING(integer, dht_upload_rate_limit) + TORRENT_SETTING(integer, unchoke_slots_limit) + TORRENT_SETTING(integer, half_open_limit) + TORRENT_SETTING(integer, connections_limit) + TORRENT_SETTING(integer, utp_target_delay) + TORRENT_SETTING(integer, utp_gain_factor) + TORRENT_SETTING(integer, utp_syn_resends) + TORRENT_SETTING(integer, utp_fin_resends) + TORRENT_SETTING(integer, utp_num_resends) + TORRENT_SETTING(integer, utp_connect_timeout) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(integer, utp_delayed_ack) +#endif + TORRENT_SETTING(boolean, utp_dynamic_sock_buf) + TORRENT_SETTING(integer, mixed_mode_algorithm) + TORRENT_SETTING(boolean, rate_limit_utp) + TORRENT_SETTING(integer, listen_queue_size) + TORRENT_SETTING(boolean, announce_double_nat) + TORRENT_SETTING(integer, torrent_connect_boost) + TORRENT_SETTING(boolean, seeding_outgoing_connections) + TORRENT_SETTING(boolean, no_connect_privileged_ports) + TORRENT_SETTING(integer, alert_queue_size) + TORRENT_SETTING(integer, max_metadata_size) + TORRENT_SETTING(boolean, smooth_connects) + TORRENT_SETTING(boolean, always_send_user_agent) + TORRENT_SETTING(boolean, apply_ip_filter_to_trackers) + TORRENT_SETTING(integer, read_job_every) + TORRENT_SETTING(boolean, use_disk_read_ahead) + TORRENT_SETTING(boolean, lock_files) + TORRENT_SETTING(integer, ssl_listen) + TORRENT_SETTING(integer, tracker_backoff) + TORRENT_SETTING(boolean, ban_web_seeds) + TORRENT_SETTING(integer, max_http_recv_buffer_size) + }; + +#undef TORRENT_SETTING +#define TORRENT_SETTING(t, x) {#x, offsetof(proxy_settings,x), t}, + + bencode_map_entry proxy_settings_map[] = + { + TORRENT_SETTING(std_string, hostname) + TORRENT_SETTING(integer16, port) + TORRENT_SETTING(std_string, username) + TORRENT_SETTING(std_string, password) + TORRENT_SETTING(character, type) + TORRENT_SETTING(boolean, proxy_hostnames) + TORRENT_SETTING(boolean, proxy_peer_connections) + }; +#undef TORRENT_SETTING + +#ifndef TORRENT_DISABLE_DHT +#define TORRENT_SETTING(t, x) {#x, offsetof(dht_settings,x), t}, + bencode_map_entry dht_settings_map[] = + { + TORRENT_SETTING(integer, max_peers_reply) + TORRENT_SETTING(integer, search_branching) +#ifndef TORRENT_NO_DEPRECATE + TORRENT_SETTING(integer, service_port) +#endif + TORRENT_SETTING(integer, max_fail_count) + TORRENT_SETTING(integer, max_torrents) + TORRENT_SETTING(integer, max_dht_items) + TORRENT_SETTING(integer, max_torrent_search_reply) + TORRENT_SETTING(boolean, restrict_routing_ips) + TORRENT_SETTING(boolean, restrict_search_ips) + TORRENT_SETTING(boolean, extended_routing_table) + }; +#undef TORRENT_SETTING +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION +#define TORRENT_SETTING(t, x) {#x, offsetof(pe_settings,x), t}, + bencode_map_entry pe_settings_map[] = + { + TORRENT_SETTING(character, out_enc_policy) + TORRENT_SETTING(character, in_enc_policy) + TORRENT_SETTING(character, allowed_enc_level) + TORRENT_SETTING(boolean, prefer_rc4) + }; +#undef TORRENT_SETTING +#endif + + struct session_category + { + char const* name; + bencode_map_entry const* map; + int num_entries; + int flag; + int offset; + int default_offset; + }; + + // the names in here need to match the names in session_impl + // to make the macro simpler + struct all_default_values + { + session_settings m_settings; + proxy_settings m_proxy; +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif +#ifndef TORRENT_DISABLE_DHT + dht_settings m_dht_settings; +#endif + }; + +#define lenof(x) sizeof(x)/sizeof(x[0]) +#define TORRENT_CATEGORY(name, flag, member, map) \ + { name, map, lenof(map), session:: flag , offsetof(session_impl, member), offsetof(all_default_values, member) }, + + session_category all_settings[] = + { + TORRENT_CATEGORY("settings", save_settings, m_settings, session_settings_map) +#ifndef TORRENT_DISABLE_DHT + TORRENT_CATEGORY("dht", save_dht_settings, m_dht_settings, dht_settings_map) +#endif + TORRENT_CATEGORY("proxy", save_proxy, m_proxy, proxy_settings_map) +#if TORRENT_USE_I2P +// TORRENT_CATEGORY("i2p", save_i2p_proxy, m_i2p_proxy, proxy_settings_map) +#endif +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_CATEGORY("encryption", save_encryption_settings, m_pe_settings, pe_settings_map) +#endif + }; + + std::pair settings_map() + { + return std::make_pair(session_settings_map, lenof(session_settings_map)); + } +#undef lenof + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef TORRENT_STATS + int session_impl::logging_allocator::allocations = 0; + int session_impl::logging_allocator::allocated_bytes = 0; +#endif + +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 && OPENSSL_VERSION_NUMBER >= 0x90812f + // when running bittorrent over SSL, the SNI (server name indication) + // extension is used to know which torrent the incoming connection is + // trying to connect to. The 40 first bytes in the name is expected to + // be the hex encoded info-hash + int servername_callback(SSL *s, int *ad, void *arg) + { + session_impl* ses = (session_impl*)arg; + const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + + if (!servername || strlen(servername) < 40) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + sha1_hash info_hash; + bool valid = from_hex(servername, 40, (char*)&info_hash[0]); + + // the server name is not a valid hex-encoded info-hash + if (!valid) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + // see if there is a torrent with this info-hash + boost::shared_ptr t = ses->find_torrent(info_hash).lock(); + + // if there isn't, fail + if (!t) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // if the torrent we found isn't an SSL torrent, also fail. + if (!t->is_ssl_torrent()) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // if the torrent doesn't have an SSL context and should not allow + // incoming SSL connections + if (!t->ssl_ctx()) return SSL_TLSEXT_ERR_ALERT_FATAL; + + // use this torrent's certificate + SSL_CTX *torrent_context = t->ssl_ctx()->native_handle(); + + SSL_set_SSL_CTX(s, torrent_context); + SSL_set_verify(s, SSL_CTX_get_verify_mode(torrent_context), SSL_CTX_get_verify_callback(torrent_context)); + + return SSL_TLSEXT_ERR_OK; + } +#endif + + session_impl::session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface + , boost::uint32_t alert_mask + ) + : m_ipv4_peer_pool(500) +#if TORRENT_USE_IPV6 + , m_ipv6_peer_pool(500) +#endif +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + , m_send_buffers(send_buffer_size) +#endif + , m_files(40) + , m_io_service() +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx(m_io_service, asio::ssl::context::sslv23) +#endif + , m_alerts(m_settings.alert_queue_size, alert_mask) + , m_disk_thread(m_io_service, boost::bind(&session_impl::on_disk_queue, this), m_files) + , m_half_open(m_io_service) + , m_download_rate(peer_connection::download_channel) +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , m_upload_rate(peer_connection::upload_channel, true) +#else + , m_upload_rate(peer_connection::upload_channel) +#endif + , m_tracker_manager(*this, m_proxy) + , m_num_active_downloading(0) + , m_num_active_finished(0) + , m_key(0) + , m_listen_port_retries(listen_port_range.second - listen_port_range.first) +#if TORRENT_USE_I2P + , m_i2p_conn(m_io_service) +#endif + , m_allowed_upload_slots(8) + , m_num_unchoked(0) + , m_unchoke_time_scaler(0) + , m_auto_manage_time_scaler(0) + , m_optimistic_unchoke_time_scaler(0) + , m_disconnect_time_scaler(90) + , m_auto_scrape_time_scaler(180) + , m_next_explicit_cache_torrent(0) + , m_cache_rotation_timer(0) + , m_peak_up_rate(0) + , m_peak_down_rate(0) + , m_created(time_now_hires()) + , m_last_tick(m_created) + , m_last_second_tick(m_created - milliseconds(900)) + , m_last_disk_performance_warning(min_time()) + , m_last_disk_queue_performance_warning(min_time()) + , m_last_choke(m_created) + , m_next_rss_update(min_time()) +#ifndef TORRENT_DISABLE_DHT + , m_dht_announce_timer(m_io_service) + , m_dht_interval_update_torrents(0) +#endif + , m_external_udp_port(0) + , m_udp_socket(m_io_service, m_half_open) + // TODO: 4 in order to support SSL over uTP, the utp_socket manager either + // needs to be able to receive packets on multiple ports, or we need to + // peek into the first few bytes the payload stream of a socket to determine + // whether or not it's an SSL connection. (The former is simpler but won't + // do as well with NATs) + , m_utp_socket_manager(m_settings, m_udp_socket + , boost::bind(&session_impl::incoming_connection, this, _1)) + , m_boost_connections(0) + , m_timer(m_io_service) + , m_lsd_announce_timer(m_io_service) + , m_host_resolver(m_io_service) + , m_current_connect_attempts(0) + , m_tick_residual(0) + , m_non_filtered_torrents(0) +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + , m_logpath(".") +#endif +#ifndef TORRENT_DISABLE_GEO_IP + , m_asnum_db(0) + , m_country_db(0) +#endif + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_pending_auto_manage(false) + , m_need_auto_manage(false) + , m_abort(false) + , m_paused(false) + , m_incoming_connection(false) +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + , m_network_thread(0) +#endif + { +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = false; +#endif + + memset(m_redundant_bytes, 0, sizeof(m_redundant_bytes)); + m_udp_socket.set_rate_limit(m_settings.dht_upload_rate_limit); + + m_udp_socket.subscribe(&m_utp_socket_manager); + m_udp_socket.subscribe(this); + m_udp_socket.subscribe(&m_tracker_manager); + + m_disk_queues[0] = 0; + m_disk_queues[1] = 0; + +#ifdef TORRENT_REQUEST_LOGGING + char log_filename[200]; +#ifdef TORRENT_WINDOWS + const int pid = GetCurrentProcessId(); +#else + const int pid = getpid(); +#endif + snprintf(log_filename, sizeof(log_filename), "requests-%d.log", pid); + m_request_log = fopen(log_filename, "w+"); + if (m_request_log == 0) + { + fprintf(stderr, "failed to open request log file: (%d) %s\n", errno, strerror(errno)); + } +#endif + + error_code ec; + if (!listen_interface) listen_interface = "0.0.0.0"; + m_listen_interface = tcp::endpoint(address::from_string(listen_interface, ec), listen_port_range.first); + TORRENT_ASSERT_VAL(!ec, ec); + + // ---- generate a peer id ---- + static seed_random_generator seeder; + + std::string print = cl_fprint.to_string(); + TORRENT_ASSERT_VAL(print.length() <= 20, print.length()); + + // the client's fingerprint + std::copy( + print.begin() + , print.begin() + print.length() + , m_peer_id.begin()); + + url_random((char*)&m_peer_id[print.length()], (char*)&m_peer_id[0] + 20); + + update_rate_settings(); + update_connections_limit(); + update_unchoke_limit(); + } + + void session_impl::start_session() + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); + session_log("log created"); +#endif + + error_code ec; +#ifdef TORRENT_USE_OPENSSL + m_ssl_ctx.set_verify_mode(asio::ssl::context::verify_none, ec); +#if BOOST_VERSION >= 104700 +#if OPENSSL_VERSION_NUMBER >= 0x90812f + SSL_CTX_set_tlsext_servername_callback(m_ssl_ctx.native_handle(), servername_callback); + SSL_CTX_set_tlsext_servername_arg(m_ssl_ctx.native_handle(), this); +#endif // OPENSSL_VERSION_NUMBER +#endif // BOOST_VERSION +#endif + +#ifndef TORRENT_DISABLE_DHT + m_next_dht_torrent = m_torrents.begin(); +#endif + m_next_lsd_torrent = m_torrents.begin(); + m_next_connect_torrent = m_torrents.begin(); + m_next_disk_peer = m_connections.begin(); + + m_tcp_mapping[0] = -1; + m_tcp_mapping[1] = -1; + m_udp_mapping[0] = -1; + m_udp_mapping[1] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_mapping[0] = -1; + m_ssl_mapping[1] = -1; +#endif +#ifdef WIN32 + // windows XP has a limit on the number of + // simultaneous half-open TCP connections + // here's a table: + + // windows version half-open connections limit + // --------------------- --------------------------- + // XP sp1 and earlier infinite + // earlier than vista 8 + // vista sp1 and earlier 5 + // vista sp2 and later infinite + + // windows release version number + // ----------------------------------- -------------- + // Windows 7 6.1 + // Windows Server 2008 R2 6.1 + // Windows Server 2008 6.0 + // Windows Vista 6.0 + // Windows Server 2003 R2 5.2 + // Windows Home Server 5.2 + // Windows Server 2003 5.2 + // Windows XP Professional x64 Edition 5.2 + // Windows XP 5.1 + // Windows 2000 5.0 + + OSVERSIONINFOEX osv; + memset(&osv, 0, sizeof(osv)); + osv.dwOSVersionInfoSize = sizeof(osv); + GetVersionEx((OSVERSIONINFO*)&osv); + + // the low two bytes of windows_version is the actual + // version. + boost::uint32_t windows_version + = ((osv.dwMajorVersion & 0xff) << 16) + | ((osv.dwMinorVersion & 0xff) << 8) + | (osv.wServicePackMajor & 0xff); + + // this is the format of windows_version + // xx xx xx + // | | | + // | | + service pack version + // | + minor version + // + major version + + // the least significant byte is the major version + // and the most significant one is the minor version + if (windows_version >= 0x060100) + { + // windows 7 and up doesn't have a half-open limit + m_half_open.limit(0); + } + else if (windows_version >= 0x060002) + { + // on vista SP 2 and up, there's no limit + m_half_open.limit(0); + } + else if (windows_version >= 0x060000) + { + // on vista the limit is 5 (in home edition) + m_half_open.limit(4); + } + else if (windows_version >= 0x050102) + { + // on XP SP2 the limit is 10 + m_half_open.limit(9); + } + else + { + // before XP SP2, there was no limit + m_half_open.limit(0); + } + m_settings.half_open_limit = m_half_open.limit(); +#endif + + m_bandwidth_channel[peer_connection::download_channel] = &m_download_channel; + m_bandwidth_channel[peer_connection::upload_channel] = &m_upload_channel; + +#ifdef TORRENT_UPNP_LOGGING + m_upnp_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + + char tmp[300]; + snprintf(tmp, sizeof(tmp), "libtorrent configuration: %s\n" + "libtorrent version: %s\n" + "libtorrent revision: %s\n\n" + , TORRENT_CFG_STRING + , LIBTORRENT_VERSION + , LIBTORRENT_REVISION); + (*m_logger) << tmp; + +#endif // TORRENT_VERBOSE_LOGGING + +#ifdef TORRENT_STATS + + m_stats_logger = 0; + m_log_seq = 0; + m_stats_logging_enabled = true; + + memset(&m_last_cache_status, 0, sizeof(m_last_cache_status)); + get_vm_stats(&m_last_vm_stat); + + m_last_failed = 0; + m_last_redundant = 0; + m_last_uploaded = 0; + m_last_downloaded = 0; + get_thread_cpu_usage(&m_network_thread_cpu_usage); + + reset_stat_counters(); + rotate_stats_log(); +#endif +#ifdef TORRENT_DISK_STATS + m_buffer_usage_logger.open("buffer_stats.log", std::ios::trunc); + m_buffer_allocations = 0; +#endif + +#if defined TORRENT_BSD || defined TORRENT_LINUX + // ---- auto-cap open files ---- + + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) == 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" max number of open files: %d", rl.rlim_cur); +#endif + + // deduct some margin for epoll/kqueue, log files, + // futexes, shared objects etc. + rl.rlim_cur -= 20; + + // 80% of the available file descriptors should go + m_settings.connections_limit = (std::min)(m_settings.connections_limit + , int(rl.rlim_cur * 8 / 10)); + // 20% goes towards regular files + m_files.resize((std::min)(m_files.size_limit(), int(rl.rlim_cur * 2 / 10))); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " max connections: " << m_settings.connections_limit << "\n"; + (*m_logger) << time_now_string() << " max files: " << m_files.size_limit() << "\n"; +#endif + } +#endif // TORRENT_BSD || TORRENT_LINUX + + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" generated peer ID: %s", m_peer_id.to_string().c_str()); +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" spawning network thread"); +#endif + m_thread.reset(new thread(boost::bind(&session_impl::main_thread, this))); + } + +#ifdef TORRENT_STATS + void session_impl::rotate_stats_log() + { + if (m_stats_logger) + { + ++m_log_seq; + fclose(m_stats_logger); + } + + // make these cumulative for easier reading of graphs + // reset them every time the log is rotated though, + // to make them cumulative per one-hour graph + m_error_peers = 0; + m_disconnected_peers = 0; + m_eof_peers = 0; + m_connreset_peers = 0; + m_connrefused_peers = 0; + m_connaborted_peers = 0; + m_perm_peers = 0; + m_buffer_peers = 0; + m_unreachable_peers = 0; + m_broken_pipe_peers = 0; + m_addrinuse_peers = 0; + m_no_access_peers = 0; + m_invalid_arg_peers = 0; + m_aborted_peers = 0; + m_error_incoming_peers = 0; + m_error_outgoing_peers = 0; + m_error_rc4_peers = 0; + m_error_encrypted_peers = 0; + m_error_tcp_peers = 0; + m_error_utp_peers = 0; + m_connect_timeouts = 0; + m_uninteresting_peers = 0; + m_transport_timeout_peers = 0; + m_timeout_peers = 0; + m_no_memory_peers = 0; + m_too_many_peers = 0; + + error_code ec; + char filename[100]; + create_directory("session_stats", ec); +#ifdef TORRENT_WINDOWS + const int pid = GetCurrentProcessId(); +#else + const int pid = getpid(); +#endif + snprintf(filename, sizeof(filename), "session_stats/%d.%04d.log", pid, m_log_seq); + m_stats_logger = fopen(filename, "w+"); + if (m_stats_logger == 0) + { + fprintf(stderr, "Failed to create session stats log file \"%s\": (%d) %s\n" + , filename, errno, strerror(errno)); + return; + } + m_last_log_rotation = time_now(); + + fputs("second:uploaded bytes:downloaded bytes:downloading torrents:seeding torrents" + ":peers:connecting peers:disk block buffers:num list peers" + ":peer allocations:peer storage bytes" + ":checking torrents" + ":stopped torrents" + ":upload-only torrents" + ":queued seed torrents" + ":queued download torrents" + ":peers bw-up:peers bw-down:peers disk-up:peers disk-down" + ":upload rate:download rate:disk write queued bytes" + ":peers down 0:peers down 0-2:peers down 2-5:peers down 5-10:peers down 10-50" + ":peers down 50-100:peers down 100-" + ":peers up 0:peers up 0-2:peers up 2-5:peers up 5-10:peers up 10-50:peers up 50-100" + ":peers up 100-:error peers" + ":peers down interesting:peers down unchoked:peers down requests" + ":peers up interested:peers up unchoked:peers up requests" + ":peer disconnects:peers eof:peers connection reset" + ":outstanding requests:outstanding end-game requests" + ":outstanding writing blocks" + ":end game piece picker blocks" + ":piece picker blocks" + ":piece picks" + ":reject piece picks" + ":unchoke piece picks" + ":incoming redundant piece picks" + ":incoming piece picks" + ":end game piece picks" + ":snubbed piece picks" + ":connect timeouts" + ":uninteresting peers disconnect" + ":timeout peers" + ":% failed payload bytes" + ":% wasted payload bytes" + ":% protocol bytes" + ":disk read time" + ":disk write time" + ":disk queue time" + ":disk queue size" + ":disk queued bytes" + ":read cache hits" + ":disk block read" + ":disk block written" + ":failed bytes" + ":redundant bytes" + ":error torrents" + ":read disk cache size" + ":disk cache size" + ":disk buffer allocations" + ":disk hash time" + ":disk job time" + ":disk sort time" + ":connection attempts" + ":banned peers" + ":banned for hash failure" + ":cache size" + ":max connections" + ":connect candidates" + ":disk queue limit" + ":disk queue low watermark" + ":% read time" + ":% write time" + ":% hash time" + ":% sort time" + ":disk read back" + ":% read back" + ":disk read queue size" + ":tick interval" + ":tick residual" + ":max unchoked" + ":read job queue size limit" + ":smooth upload rate" + ":smooth download rate" + ":num end-game peers" + ":TCP up rate" + ":TCP down rate" + ":TCP up limit" + ":TCP down limit" + ":uTP up rate" + ":uTP down rate" + ":uTP peak send delay" + ":uTP avg send delay" + ":uTP peak recv delay" + ":uTP avg recv delay" + ":read ops/s" + ":write ops/s" + ":active resident pages" + ":inactive resident pages" + ":pinned resident pages" + ":free pages" + ":pageins" + ":pageouts" + ":page faults" + ":smooth read ops/s" + ":smooth write ops/s" + ":pending reading bytes" + ":read_counter" + ":write_counter" + ":tick_counter" + ":lsd_counter" + ":lsd_peer_counter" + ":udp_counter" + ":accept_counter" + ":disk_queue_counter" + ":disk_read_counter" + ":disk_write_counter" + ":up 8:up 16:up 32:up 64:up 128:up 256:up 512:up 1024:up 2048:up 4096:up 8192:up 16384:up 32768:up 65536:up 131072:up 262144:up 524288:up 1048576" + ":down 8:down 16:down 32:down 64:down 128:down 256:down 512:down 1024:down 2048:down 4096:down 8192:down 16384:down 32768:down 65536:down 131072:down 262144:down 524288:down 1048576" + ":network thread system time" + ":network thread user+system time" + + ":redundant timed-out" + ":redundant cancelled" + ":redundant unknown" + ":redundant seed" + ":redundant end-game" + ":redundant closing" + ":no memory peer errors" + ":too many peers" + ":transport timeout peers" + ":uTP idle" + ":uTP syn-sent" + ":uTP connected" + ":uTP fin-sent" + ":uTP close-wait" + + ":tcp peers" + ":utp peers" + + ":connection refused peers" + ":connection aborted peers" + ":permission denied peers" + ":no buffer peers" + ":host unreachable peers" + ":broken pipe peers" + ":address in use peers" + ":access denied peers" + ":invalid argument peers" + ":operation aborted peers" + + ":error incoming peers" + ":error outgoing peers" + ":error rc4 peers" + ":error encrypted peers" + ":error tcp peers" + ":error utp peers" + + ":total peers" + ":pending incoming block requests" + ":average pending incoming block requests" + + ":torrents want more peers" + ":average peers per limit" + + ":piece requests" + ":max piece requests" + ":invalid piece requests" + ":choked piece requests" + ":cancelled piece requests" + ":piece rejects" + + ":peers up send buffer" + + ":packet_loss" + ":timeout" + ":packets_in" + ":packets_out" + ":fast_retransmit" + ":packet_resend" + ":samples_above_target" + ":samples_below_target" + ":payload_pkts_in" + ":payload_pkts_out" + ":invalid_pkts_in" + ":redundant_pkts_in" + + "\n\n", m_stats_logger); + } +#endif + + void session_impl::trigger_auto_manage() + { + if (m_pending_auto_manage || m_abort) return; + + m_pending_auto_manage = true; + m_need_auto_manage = true; + m_io_service.post(boost::bind(&session_impl::on_trigger_auto_manage, this)); + } + + void session_impl::on_trigger_auto_manage() + { + INVARIANT_CHECK; + + assert(m_pending_auto_manage); + m_pending_auto_manage = false; + if (!m_need_auto_manage) return; + recalculate_auto_managed_torrents(); + } + + void session_impl::update_dht_announce_interval() + { +#ifndef TORRENT_DISABLE_DHT + if (!m_dht) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + m_dht_interval_update_torrents = m_torrents.size(); + error_code ec; + int delay = (std::max)(m_settings.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + boost::bind(&session_impl::on_dht_announce, this, _1)); + TORRENT_ASSERT(!ec); +#endif + } + + void session_impl::init() + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" *** session thread init"); +#endif + + // this is where we should set up all async operations. This + // is called from within the network thread as opposed to the + // constructor which is called from the main thread + +#if defined TORRENT_ASIO_DEBUGGING + async_inc_threads(); + add_outstanding_async("session_impl::on_tick"); +#endif + error_code ec; + m_io_service.post(boost::bind(&session_impl::on_tick, this, ec)); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + int delay = (std::max)(m_settings.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + boost::bind(&session_impl::on_lsd_announce, this, _1)); + TORRENT_ASSERT(!ec); + +#ifndef TORRENT_DISABLE_DHT + update_dht_announce_interval(); +#endif + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" open listen port"); +#endif + // no reuse_address and allow system defined port + open_listen_port(0, ec); +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log(" done starting session"); +#endif + } + + void session_impl::save_state(entry* eptr, boost::uint32_t flags) const + { + TORRENT_ASSERT(is_network_thread()); + + entry& e = *eptr; + + all_default_values def; + + for (int i = 0; i < int(sizeof(all_settings)/sizeof(all_settings[0])); ++i) + { + session_category const& c = all_settings[i]; + if ((flags & c.flag) == 0) continue; + save_struct(e[c.name], reinterpret_cast(this) + c.offset + , c.map, c.num_entries, reinterpret_cast(&def) + c.default_offset); + } +#ifndef TORRENT_DISABLE_DHT + if (m_dht && (flags & session::save_dht_state)) + { + e["dht state"] = m_dht->state(); + } +#endif + +#if TORRENT_USE_I2P + if (flags & session::save_i2p_proxy) + { + save_struct(e["i2p"], &i2p_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0]) + , &def.m_proxy); + } +#endif +#ifndef TORRENT_DISABLE_GEO_IP + if (flags & session::save_as_map) + { + entry::dictionary_type& as_map = e["AS map"].dict(); + char buf[10]; + for (std::map::const_iterator i = m_as_peak.begin() + , end(m_as_peak.end()); i != end; ++i) + { + if (i->second == 0) continue; + sprintf(buf, "%05d", i->first); + as_map[buf] = i->second; + } + } +#endif + + if (flags & session::save_feeds) + { + entry::list_type& feeds = e["feeds"].list(); + for (std::vector >::const_iterator i = + m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + feeds.push_back(entry()); + (*i)->save_state(feeds.back()); + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->save_state(*eptr); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + + void session_impl::set_proxy(proxy_settings const& s) + { + TORRENT_ASSERT(is_network_thread()); + + m_proxy = s; + // in case we just set a socks proxy, we might have to + // open the socks incoming connection + if (!m_socks_listen_socket) open_new_incoming_socks_connection(); + m_udp_socket.set_proxy_settings(m_proxy); + } + + void session_impl::load_state(lazy_entry const* e) + { + TORRENT_ASSERT(is_network_thread()); + + lazy_entry const* settings; + + if (e->type() != lazy_entry::dict_t) return; + + for (int i = 0; i < int(sizeof(all_settings)/sizeof(all_settings[0])); ++i) + { + session_category const& c = all_settings[i]; + settings = e->dict_find_dict(c.name); + if (!settings) continue; + load_struct(*settings, reinterpret_cast(this) + c.offset, c.map, c.num_entries); + } + + update_rate_settings(); + update_connections_limit(); + update_unchoke_limit(); + m_alerts.set_alert_queue_size_limit(m_settings.alert_queue_size); + + // in case we just set a socks proxy, we might have to + // open the socks incoming connection + if (!m_socks_listen_socket) open_new_incoming_socks_connection(); + m_udp_socket.set_proxy_settings(m_proxy); + +#ifndef TORRENT_DISABLE_DHT + settings = e->dict_find_dict("dht state"); + if (settings) + { + m_dht_state = *settings; + } +#endif + +#if TORRENT_USE_I2P + settings = e->dict_find_dict("i2p"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_i2p_proxy(s); + } +#endif +#ifndef TORRENT_DISABLE_GEO_IP + settings = e->dict_find_dict("AS map"); + if (settings) + { + for (int i = 0; i < settings->dict_size(); ++i) + { + std::pair item = settings->dict_at(i); + int as_num = atoi(item.first.c_str()); + if (item.second->type() != lazy_entry::int_t || item.second->int_value() == 0) continue; + int& peak = m_as_peak[as_num]; + if (peak < item.second->int_value()) peak = item.second->int_value(); + } + } +#endif + + if (m_settings.connection_speed < 0) m_settings.connection_speed = 200; + + update_disk_thread_settings(); + + settings = e->dict_find_list("feeds"); + if (settings) + { + m_feeds.reserve(settings->list_size()); + for (int i = 0; i < settings->list_size(); ++i) + { + if (settings->list_at(i)->type() != lazy_entry::dict_t) continue; + boost::shared_ptr f(new_feed(*this, feed_settings())); + f->load_state(*settings->list_at(i)); + f->update_feed(); + m_feeds.push_back(f); + } + update_rss_feeds(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->load_state(*e); + } TORRENT_CATCH(std::exception&) {} + } +#endif + } + +#ifndef TORRENT_DISABLE_GEO_IP + namespace + { + struct free_ptr + { + void* ptr_; + free_ptr(void* p): ptr_(p) {} + ~free_ptr() { free(ptr_); } + }; + } + + char const* session_impl::country_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_country_db == 0) return 0; + return GeoIP_country_code_by_ipnum(m_country_db, a.to_v4().to_ulong()); + } + + int session_impl::as_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_asnum_db == 0) return 0; + char* name = GeoIP_name_by_ipnum(m_asnum_db, a.to_v4().to_ulong()); + if (name == 0) return 0; + free_ptr p(name); + // GeoIP returns the name as AS??? where ? is the AS-number + return atoi(name + 2); + } + + std::string session_impl::as_name_for_ip(address const& a) + { + TORRENT_ASSERT(is_network_thread()); + + if (!a.is_v4() || m_asnum_db == 0) return std::string(); + char* name = GeoIP_name_by_ipnum(m_asnum_db, a.to_v4().to_ulong()); + if (name == 0) return std::string(); + free_ptr p(name); + char* tmp = std::strchr(name, ' '); + if (tmp == 0) return std::string(); + return tmp + 1; + } + + std::pair* session_impl::lookup_as(int as) + { + TORRENT_ASSERT(is_network_thread()); + + std::map::iterator i = m_as_peak.lower_bound(as); + + if (i == m_as_peak.end() || i->first != as) + { + // we don't have any data for this AS, insert a new entry + i = m_as_peak.insert(i, std::pair(as, 0)); + } + return &(*i); + } + + void session_impl::load_asnum_db(std::string file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_asnum_db) GeoIP_delete(m_asnum_db); + m_asnum_db = GeoIP_open(file.c_str(), GEOIP_STANDARD); +// return m_asnum_db; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void session_impl::load_asnum_dbw(std::wstring file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_asnum_db) GeoIP_delete(m_asnum_db); + std::string utf8; + wchar_utf8(file, utf8); + m_asnum_db = GeoIP_open(utf8.c_str(), GEOIP_STANDARD); +// return m_asnum_db; + } + + void session_impl::load_country_dbw(std::wstring file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_country_db) GeoIP_delete(m_country_db); + std::string utf8; + wchar_utf8(file, utf8); + m_country_db = GeoIP_open(utf8.c_str(), GEOIP_STANDARD); +// return m_country_db; + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void session_impl::load_country_db(std::string file) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_country_db) GeoIP_delete(m_country_db); + m_country_db = GeoIP_open(file.c_str(), GEOIP_STANDARD); +// return m_country_db; + } + +#endif // TORRENT_DISABLE_GEO_IP + +#ifndef TORRENT_DISABLE_EXTENSIONS + + typedef boost::function(torrent*, void*)> ext_function_t; + + struct session_plugin_wrapper : plugin + { + session_plugin_wrapper(ext_function_t const& f) : m_f(f) {} + + virtual boost::shared_ptr new_torrent(torrent* t, void* user) + { return m_f(t, user); } + ext_function_t m_f; + }; + + void session_impl::add_extension(ext_function_t ext) + { + TORRENT_ASSERT(is_network_thread()); + TORRENT_ASSERT_VAL(ext, ext); + + boost::shared_ptr p(new session_plugin_wrapper(ext)); + + m_ses_extensions.push_back(p); + } + + void session_impl::add_ses_extension(boost::shared_ptr ext) + { + TORRENT_ASSERT(is_network_thread()); + TORRENT_ASSERT_VAL(ext, ext); + + m_ses_extensions.push_back(ext); + m_alerts.add_extension(ext); + ext->added(this); + } +#endif + + feed_handle session_impl::add_feed(feed_settings const& sett) + { + TORRENT_ASSERT(is_network_thread()); + + // look for duplicates. If we already have a feed with this + // URL, return a handle to the existing one + for (std::vector >::const_iterator i + = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + if (sett.url != (*i)->m_settings.url) continue; + return feed_handle(*i); + } + + boost::shared_ptr f(new_feed(*this, sett)); + m_feeds.push_back(f); + update_rss_feeds(); + return feed_handle(f); + } + + void session_impl::remove_feed(feed_handle h) + { + TORRENT_ASSERT(is_network_thread()); + + boost::shared_ptr f = h.m_feed_ptr.lock(); + if (!f) return; + + std::vector >::iterator i + = std::find(m_feeds.begin(), m_feeds.end(), f); + + if (i == m_feeds.end()) return; + + m_feeds.erase(i); + } + + void session_impl::get_feeds(std::vector* ret) const + { + TORRENT_ASSERT(is_network_thread()); + + ret->clear(); + ret->reserve(m_feeds.size()); + for (std::vector >::const_iterator i = m_feeds.begin() + , end(m_feeds.end()); i != end; ++i) + ret->push_back(feed_handle(*i)); + } + + void session_impl::pause() + { + TORRENT_ASSERT(is_network_thread()); + + if (m_paused) return; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log(" *** session paused ***"); +#endif + m_paused = true; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + t.do_pause(); + } + } + + void session_impl::resume() + { + TORRENT_ASSERT(is_network_thread()); + + if (!m_paused) return; + m_paused = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + t.do_resume(); + if (t.should_check_files()) t.queue_torrent_check(); + } + } + + void session_impl::abort() + { + TORRENT_ASSERT(is_network_thread()); + + if (m_abort) return; +#if defined TORRENT_LOGGING + session_log(" *** ABORT CALLED ***"); +#endif + // abort the main thread + m_abort = true; + error_code ec; +#if TORRENT_USE_I2P + m_i2p_conn.close(ec); +#endif + m_queued_for_checking.clear(); + stop_lsd(); + stop_upnp(); + stop_natpmp(); +#ifndef TORRENT_DISABLE_DHT + stop_dht(); + m_dht_announce_timer.cancel(ec); +#endif + m_timer.cancel(ec); + m_lsd_announce_timer.cancel(ec); + + for (std::set >::iterator i = m_incoming_sockets.begin() + , end(m_incoming_sockets.end()); i != end; ++i) + { + (*i)->close(ec); + TORRENT_ASSERT(!ec); + } + m_incoming_sockets.clear(); + + // close the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + i->sock->close(ec); + TORRENT_ASSERT(!ec); + } + m_listen_sockets.clear(); + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + { + m_socks_listen_socket->close(ec); + TORRENT_ASSERT(!ec); + } + m_socks_listen_socket.reset(); + +#if TORRENT_USE_I2P + if (m_i2p_listen_socket && m_i2p_listen_socket->is_open()) + { + m_i2p_listen_socket->close(ec); + TORRENT_ASSERT(!ec); + } + m_i2p_listen_socket.reset(); +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all torrents (%d)", m_torrents.size()); +#endif + // abort all torrents + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + i->second->abort(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all tracker requests"); +#endif + m_tracker_manager.abort_all_requests(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" sending event=stopped to trackers"); +#endif + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + torrent& t = *i->second; + t.abort(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" aborting all connections (%d)", m_connections.size()); +#endif + m_half_open.close(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" connection queue: %d", m_half_open.size()); +#endif + + // abort all connections + while (!m_connections.empty()) + { +#if TORRENT_USE_ASSERTS + int conn = m_connections.size(); +#endif + (*m_connections.begin())->disconnect(errors::stopping_torrent); + TORRENT_ASSERT_VAL(conn == int(m_connections.size()) + 1, conn); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" connection queue: %d", m_half_open.size()); +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" shutting down connection queue"); +#endif + + m_download_rate.close(); + m_upload_rate.close(); + + // #error closing the udp socket here means that + // the uTP connections cannot be closed gracefully + m_udp_socket.close(); + m_external_udp_port = 0; + + m_undead_peers.clear(); + +#ifndef TORRENT_DISABLE_GEO_IP + if (m_asnum_db) GeoIP_delete(m_asnum_db); + if (m_country_db) GeoIP_delete(m_country_db); + m_asnum_db = 0; + m_country_db = 0; +#endif + + m_disk_thread.abort(); + } + + void session_impl::set_port_filter(port_filter const& f) + { + m_port_filter = f; + // TODO: recalculate all connect candidates for all torrents + } + + void session_impl::set_ip_filter(ip_filter const& f) + { + INVARIANT_CHECK; + + m_ip_filter = f; + + // Close connections whose endpoint is filtered + // by the new ip-filter + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + i->second->ip_filter_updated(); + } + + ip_filter const& session_impl::get_ip_filter() const + { + return m_ip_filter; + } + + void session_impl::update_disk_thread_settings() + { + disk_io_job j; + j.buffer = (char*)new session_settings(m_settings); + j.action = disk_io_job::update_settings; + m_disk_thread.add_job(j); + } + + template + void static set_socket_buffer_size(Socket& s, session_settings const& sett, error_code& ec) + { + if (sett.send_socket_buffer_size) + { + stream_socket::send_buffer_size prev_option; + s.get_option(prev_option, ec); + if (!ec) + { + stream_socket::send_buffer_size option( + sett.send_socket_buffer_size); + s.set_option(option, ec); + if (ec) + { + // restore previous value + s.set_option(prev_option, ec); + return; + } + } + } + if (sett.recv_socket_buffer_size) + { + stream_socket::receive_buffer_size prev_option; + s.get_option(prev_option, ec); + if (!ec) + { + stream_socket::receive_buffer_size option( + sett.recv_socket_buffer_size); + s.set_option(option, ec); + if (ec) + { + // restore previous value + s.set_option(prev_option, ec); + return; + } + } + } + } + + void session_impl::set_settings(session_settings const& s) + { + INVARIANT_CHECK; + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT_VAL(s.file_pool_size > 0, s.file_pool_size); + + // less than 5 seconds unchoke interval is insane + TORRENT_ASSERT_VAL(s.unchoke_interval >= 5, s.unchoke_interval); + + // if disk io thread settings were changed + // post a notification to that thread + bool update_disk_io_thread = false; + if (m_settings.cache_size != s.cache_size + || m_settings.cache_expiry != s.cache_expiry + || m_settings.optimize_hashing_for_speed != s.optimize_hashing_for_speed + || m_settings.file_checks_delay_per_block != s.file_checks_delay_per_block + || m_settings.disk_cache_algorithm != s.disk_cache_algorithm + || m_settings.read_cache_line_size != s.read_cache_line_size + || m_settings.write_cache_line_size != s.write_cache_line_size + || m_settings.coalesce_writes != s.coalesce_writes + || m_settings.coalesce_reads != s.coalesce_reads + || m_settings.max_queued_disk_bytes != s.max_queued_disk_bytes + || m_settings.max_queued_disk_bytes_low_watermark != s.max_queued_disk_bytes_low_watermark + || m_settings.disable_hash_checks != s.disable_hash_checks + || m_settings.explicit_read_cache != s.explicit_read_cache +#ifndef TORRENT_DISABLE_MLOCK + || m_settings.lock_disk_cache != s.lock_disk_cache +#endif + || m_settings.use_read_cache != s.use_read_cache + || m_settings.disk_io_write_mode != s.disk_io_write_mode + || m_settings.disk_io_read_mode != s.disk_io_read_mode + || m_settings.allow_reordered_disk_operations != s.allow_reordered_disk_operations + || m_settings.file_pool_size != s.file_pool_size + || m_settings.volatile_read_cache != s.volatile_read_cache + || m_settings.no_atime_storage!= s.no_atime_storage + || m_settings.ignore_resume_timestamps != s.ignore_resume_timestamps + || m_settings.no_recheck_incomplete_resume != s.no_recheck_incomplete_resume + || m_settings.low_prio_disk != s.low_prio_disk + || m_settings.lock_files != s.lock_files + || m_settings.use_disk_cache_pool != s.use_disk_cache_pool) + update_disk_io_thread = true; + + bool connections_limit_changed = m_settings.connections_limit != s.connections_limit; + bool unchoke_limit_changed = m_settings.unchoke_slots_limit != s.unchoke_slots_limit; + +#ifndef TORRENT_NO_DEPRECATE + // support deprecated choker settings + if (s.choking_algorithm == session_settings::rate_based_choker) + { + if (s.auto_upload_slots && !s.auto_upload_slots_rate_based) + m_settings.choking_algorithm = session_settings::auto_expand_choker; + else if (!s.auto_upload_slots) + m_settings.choking_algorithm = session_settings::fixed_slots_choker; + } +#endif + + // safety check + if (m_settings.volatile_read_cache + && (m_settings.suggest_mode == session_settings::suggest_read_cache + || m_settings.explicit_read_cache)) + { + // If you hit this assert, you're trying to set your cache to be + // volatile and to suggest pieces out of it (or to make the cache + // explicit) at the same time this is a bad configuration, don't do it + TORRENT_ASSERT_PRECOND(false); + m_settings.volatile_read_cache = false; + } + + if (m_settings.choking_algorithm != s.choking_algorithm) + { + // trigger recalculation of the unchoked peers + m_unchoke_time_scaler = 0; + } + +#ifndef TORRENT_DISABLE_DHT + if (m_settings.dht_announce_interval != s.dht_announce_interval) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + error_code ec; + int delay = (std::max)(s.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + boost::bind(&session_impl::on_dht_announce, this, _1)); + } +#endif + + if (m_settings.local_service_announce_interval != s.local_service_announce_interval) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + error_code ec; + int delay = (std::max)(s.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + boost::bind(&session_impl::on_lsd_announce, this, _1)); + } + + // if queuing settings were changed, recalculate + // queued torrents sooner + if ((m_settings.active_downloads != s.active_downloads + || m_settings.active_seeds != s.active_seeds + || m_settings.active_limit != s.active_limit)) + m_auto_manage_time_scaler = 2; + + if (m_settings.report_web_seed_downloads != s.report_web_seed_downloads) + { + // if this flag changed, update all web seed connections + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + int type = (*i)->type(); + if (type == peer_connection::url_seed_connection + || type == peer_connection::http_seed_connection) + (*i)->ignore_stats(!s.report_web_seed_downloads); + } + } + + if (m_settings.alert_queue_size != s.alert_queue_size) + m_alerts.set_alert_queue_size_limit(s.alert_queue_size); + + if (m_settings.dht_upload_rate_limit != s.dht_upload_rate_limit) + m_udp_socket.set_rate_limit(s.dht_upload_rate_limit); + + if (m_settings.peer_tos != s.peer_tos && s.peer_tos != 0) + { + error_code ec; + +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + if (m_udp_socket.local_endpoint(ec).address().is_v6()) + m_udp_socket.set_option(traffic_class(s.peer_tos), ec); + else +#endif + m_udp_socket.set_option(type_of_service(s.peer_tos), ec); + +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << ">>> SET_TOS[ udp_socket tos: " << s.peer_tos << " e: " << ec.message() << " ]\n"; +#endif + } + + { + error_code ec; + set_socket_buffer_size(m_udp_socket, m_settings, ec); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(udp::endpoint(), ec)); + } + } + + bool reopen_listen_port = false; + if (m_settings.ssl_listen != s.ssl_listen) + reopen_listen_port = true; + + m_settings = s; + + if (m_settings.cache_buffer_chunk_size <= 0) + m_settings.cache_buffer_chunk_size = 1; + + update_rate_settings(); + + if (connections_limit_changed) update_connections_limit(); + if (unchoke_limit_changed) update_unchoke_limit(); + + bool anonymous_mode = (m_settings.anonymous_mode != s.anonymous_mode && s.anonymous_mode); + if (anonymous_mode) + { + m_settings.user_agent.clear(); + url_random((char*)&m_peer_id[0], (char*)&m_peer_id[0] + 20); + } + + bool force_proxy = (m_settings.force_proxy != s.force_proxy && s.force_proxy); + + m_udp_socket.set_force_proxy(s.force_proxy); + + // in force_proxy mode, we don't want to accept any incoming + // connections, except through a proxy. + if (force_proxy) + { + stop_lsd(); + stop_upnp(); + stop_natpmp(); +#ifndef TORRENT_DISABLE_DHT + stop_dht(); +#endif + // close the listen sockets + error_code ec; + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + i->sock->close(ec); + m_listen_sockets.clear(); + } + if (m_settings.connection_speed < 0) m_settings.connection_speed = 200; + + if (update_disk_io_thread) + update_disk_thread_settings(); + + if (m_settings.num_optimistic_unchoke_slots >= m_allowed_upload_slots / 2) + { + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::too_many_optimistic_unchoke_slots)); + } + + if (s.choking_algorithm == session_settings::fixed_slots_choker) + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + else if (s.choking_algorithm == session_settings::auto_expand_choker + && m_allowed_upload_slots < m_settings.unchoke_slots_limit) + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + if (m_allowed_upload_slots < 0) + m_allowed_upload_slots = (std::numeric_limits::max)(); + + // replace all occurances of '\n' with ' '. + std::string::iterator i = m_settings.user_agent.begin(); + while ((i = std::find(i, m_settings.user_agent.end(), '\n')) + != m_settings.user_agent.end()) + *i = ' '; + + if (reopen_listen_port) + { + error_code ec; + open_listen_port(0, ec); + } + } + + tcp::endpoint session_impl::get_ipv6_interface() const + { + return m_ipv6_interface; + } + + tcp::endpoint session_impl::get_ipv4_interface() const + { + return m_ipv4_interface; + } + + void session_impl::setup_listener(listen_socket_t* s, tcp::endpoint ep + , int& retries, bool v6_only, int flags, error_code& ec) + { + int last_op = 0; + listen_failed_alert::socket_type_t sock_type = s->ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp; + s->sock.reset(new socket_acceptor(m_io_service)); + s->sock->open(ep.protocol(), ec); + last_op = listen_failed_alert::open; + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to open socket: %s: %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + + // SO_REUSEADDR on windows is a bit special. It actually allows + // two active sockets to bind to the same port. That means we + // may end up binding to the same socket as some other random + // application. Don't do it! +#ifndef TORRENT_WINDOWS + error_code err; // ignore errors here + s->sock->set_option(socket_acceptor::reuse_address(true), err); +#endif + +#if TORRENT_USE_IPV6 + if (ep.protocol() == tcp::v6()) + { + error_code err; // ignore errors here +#ifdef IPV6_V6ONLY + s->sock->set_option(v6only(v6_only), err); +#endif +#ifdef TORRENT_WINDOWS + +#ifndef PROTECTION_LEVEL_UNRESTRICTED +#define PROTECTION_LEVEL_UNRESTRICTED 10 +#endif + // enable Teredo on windows + s->sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); +#endif + } +#endif + s->sock->bind(ep, ec); + while (ec && retries > 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to bind to interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + ec.clear(); + TORRENT_ASSERT_VAL(!ec, ec); + --retries; + ep.port(ep.port() + 1); + s->sock->bind(ep, ec); + last_op = listen_failed_alert::bind; + } + if (ec && !(flags & session::listen_no_system_port)) + { + // instead of giving up, trying + // let the OS pick a port + ep.port(0); + ec = error_code(); + s->sock->bind(ep, ec); + last_op = listen_failed_alert::bind; + } + if (ec) + { + // not even that worked, give up + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + s->external_port = s->sock->local_endpoint(ec).port(); + TORRENT_ASSERT(s->external_port == ep.port() || ep.port() == 0); + last_op = listen_failed_alert::get_peer_name; + if (!ec) + { + s->sock->listen(m_settings.listen_queue_size, ec); + last_op = listen_failed_alert::listen; + } + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot listen on interface \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); +#endif + return; + } + + // if we asked the system to listen on port 0, which + // socket did it end up choosing? + if (ep.port() == 0) + { + ep.port(s->sock->local_endpoint(ec).port()); + last_op = listen_failed_alert::get_peer_name; + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, last_op, ec, sock_type)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, 200, "failed to get peer name \"%s\": %s" + , print_endpoint(ep).c_str(), ec.message().c_str()); + (*m_logger) << time_now_string() << msg << "\n"; +#endif + } + } + + if (m_alerts.should_post()) + m_alerts.post_alert(listen_succeeded_alert(ep, s->ssl ? listen_succeeded_alert::tcp_ssl : listen_succeeded_alert::tcp)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log(" listening on: %s external port: %d" + , print_endpoint(ep).c_str(), s->external_port); +#endif + } + + void session_impl::open_listen_port(int flags, error_code& ec) + { + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT(!m_abort); +retry: + + // close the open listen sockets + // close the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + i->sock->close(ec); + m_listen_sockets.clear(); + m_incoming_connection = false; + ec.clear(); + + if (m_abort) return; + + m_ipv6_interface = tcp::endpoint(); + m_ipv4_interface = tcp::endpoint(); + +#ifdef TORRENT_USE_OPENSSL + tcp::endpoint ssl_interface = m_listen_interface; + ssl_interface.port(m_settings.ssl_listen); +#endif + + if (is_any(m_listen_interface.address())) + { + // this means we should open two listen sockets + // one for IPv4 and one for IPv6 + + listen_socket_t s; + setup_listener(&s, tcp::endpoint(address_v4::any(), m_listen_interface.port()) + , m_listen_port_retries, false, flags, ec); + + if (s.sock) + { + // update the listen_interface member with the + // actual port we ended up listening on, so that the other + // sockets can be bound to the same one + m_listen_interface.port(s.external_port); + + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, ssl_interface, retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif + +#if TORRENT_USE_IPV6 + // only try to open the IPv6 port if IPv6 is installed + if (supports_ipv6()) + { + setup_listener(&s, tcp::endpoint(address_v6::any(), m_listen_interface.port()) + , m_listen_port_retries, true, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, tcp::endpoint(address_v6::any(), ssl_interface.port()) + , retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif // TORRENT_USE_OPENSSL + } +#endif // TORRENT_USE_IPV6 + + // set our main IPv4 and IPv6 interfaces + // used to send to the tracker + std::vector ifs = enum_net_interfaces(m_io_service, ec); + for (std::vector::const_iterator i = ifs.begin() + , end(ifs.end()); i != end; ++i) + { + address const& addr = i->interface_address; + if (addr.is_v6() && !is_local(addr) && !is_loopback(addr)) + m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); + else if (addr.is_v4() && !is_local(addr) && !is_loopback(addr)) + m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); + } + } + else + { + // we should only open a single listen socket, that + // binds to the given interface + + listen_socket_t s; + setup_listener(&s, m_listen_interface, m_listen_port_retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + + if (m_listen_interface.address().is_v6()) + m_ipv6_interface = m_listen_interface; + else + m_ipv4_interface = m_listen_interface; + } + +#ifdef TORRENT_USE_OPENSSL + if (m_settings.ssl_listen) + { + listen_socket_t s; + s.ssl = true; + int retries = 10; + setup_listener(&s, ssl_interface, retries, false, flags, ec); + + if (s.sock) + { + TORRENT_ASSERT(!m_abort); + m_listen_sockets.push_back(s); + } + } +#endif + } + + if (m_listen_sockets.empty() && ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "cannot bind TCP listen socket to interface \"%s\": %s" + , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); + (*m_logger) << msg << "\n"; +#endif + if (m_listen_port_retries > 0) + { + m_listen_interface.port(m_listen_interface.port() + 1); + --m_listen_port_retries; + goto retry; + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(m_listen_interface + , listen_failed_alert::bind, ec, listen_failed_alert::udp)); + return; + } + + m_udp_socket.bind(udp::endpoint(m_listen_interface.address(), m_listen_interface.port()), ec); + if (ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to UDP interface \"%s\": %s" + , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); +#endif + if (m_listen_port_retries > 0) + { + m_listen_interface.port(m_listen_interface.port() + 1); + --m_listen_port_retries; + goto retry; + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(m_listen_interface + , listen_failed_alert::bind, ec, listen_failed_alert::udp)); + return; + } + else + { + m_external_udp_port = m_udp_socket.local_port(); + maybe_update_udp_mapping(0, m_listen_interface.port(), m_listen_interface.port()); + maybe_update_udp_mapping(1, m_listen_interface.port(), m_listen_interface.port()); + if (m_alerts.should_post()) + m_alerts.post_alert(listen_succeeded_alert(m_listen_interface, listen_succeeded_alert::udp)); + } + + if (m_settings.peer_tos != 0) { + +#if TORRENT_USE_IPV6 && defined IPV6_TCLASS + if (m_udp_socket.local_endpoint(ec).address().is_v6()) + m_udp_socket.set_option(traffic_class(m_settings.peer_tos), ec); + else +#endif + m_udp_socket.set_option(type_of_service(m_settings.peer_tos), ec); + +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << ">>> SET_TOS[ udp_socket tos: " << m_settings.peer_tos << " e: " << ec.message() << " ]\n"; +#endif + } + ec.clear(); + + set_socket_buffer_size(m_udp_socket, m_settings, ec); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(udp::endpoint(), ec)); + } + + // initiate accepting on the listen sockets + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + async_accept(i->sock, i->ssl); + + open_new_incoming_socks_connection(); +#if TORRENT_USE_I2P + open_new_incoming_i2p_connection(); +#endif + + if (!m_listen_sockets.empty()) + { + tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); + if (!ec) remap_tcp_ports(3, local.port(), ssl_listen_port()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); +#endif + } + + void session_impl::remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port) + { + if ((mask & 1) && m_natpmp.get()) + { + if (m_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_tcp_mapping[0]); + m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp, tcp_port, tcp_port); +#ifdef TORRENT_USE_OPENSSL + if (m_ssl_mapping[0] != -1) m_natpmp->delete_mapping(m_ssl_mapping[0]); + if (ssl_port > 0) m_ssl_mapping[0] = m_natpmp->add_mapping(natpmp::tcp + , ssl_port, ssl_port); +#endif + } + if ((mask & 2) && m_upnp.get()) + { + if (m_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_tcp_mapping[1]); + m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp, tcp_port, tcp_port); +#ifdef TORRENT_USE_OPENSSL + if (m_ssl_mapping[1] != -1) m_upnp->delete_mapping(m_ssl_mapping[1]); + if (ssl_port > 0) m_ssl_mapping[1] = m_upnp->add_mapping(upnp::tcp + , ssl_port, ssl_port); +#endif + } + } + + void session_impl::open_new_incoming_socks_connection() + { + if (m_proxy.type != proxy_settings::socks5 + && m_proxy.type != proxy_settings::socks5_pw + && m_proxy.type != proxy_settings::socks4) + return; + + if (m_socks_listen_socket) return; + + m_socks_listen_socket = boost::shared_ptr(new socket_type(m_io_service)); + bool ret = instantiate_connection(m_io_service, m_proxy + , *m_socks_listen_socket); + TORRENT_ASSERT_VAL(ret, ret); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_socks_accept"); +#endif + socks5_stream& s = *m_socks_listen_socket->get(); + s.set_command(2); // 2 means BIND (as opposed to CONNECT) + m_socks_listen_port = m_listen_interface.port(); + if (m_socks_listen_port == 0) m_socks_listen_port = 2000 + random() % 60000; + s.async_connect(tcp::endpoint(address_v4::any(), m_socks_listen_port) + , boost::bind(&session_impl::on_socks_accept, this, m_socks_listen_socket, _1)); + } + +#if TORRENT_USE_I2P + void session_impl::set_i2p_proxy(proxy_settings const& s) + { + // we need this socket to be open before we + // can make name lookups for trackers for instance. + // pause the session now and resume it once we've + // established the i2p SAM connection + if (s.hostname.empty()) + { + error_code ec; + m_i2p_conn.close(ec); + return; + } + m_i2p_conn.open(s, boost::bind(&session_impl::on_i2p_open, this, _1)); + } + + void session_impl::on_i2p_open(error_code const& ec) + { + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(i2p_alert(ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "i2p open failed (%d) %s", ec.value(), ec.message().c_str()); + (*m_logger) << msg << "\n"; +#endif + } + // now that we have our i2p connection established + // it's OK to start torrents and use this socket to + // do i2p name lookups + + open_new_incoming_i2p_connection(); + } + + void session_impl::open_new_incoming_i2p_connection() + { + if (!m_i2p_conn.is_open()) return; + + if (m_i2p_listen_socket) return; + + m_i2p_listen_socket = boost::shared_ptr(new socket_type(m_io_service)); + bool ret = instantiate_connection(m_io_service, m_i2p_conn.proxy() + , *m_i2p_listen_socket); + TORRENT_ASSERT_VAL(ret, ret); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_i2p_accept"); +#endif + i2p_stream& s = *m_i2p_listen_socket->get(); + s.set_command(i2p_stream::cmd_accept); + s.set_session_id(m_i2p_conn.session_id()); + s.async_connect(tcp::endpoint(address_v4::any(), m_listen_interface.port()) + , boost::bind(&session_impl::on_i2p_accept, this, m_i2p_listen_socket, _1)); + } + + void session_impl::on_i2p_accept(boost::shared_ptr const& s + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_i2p_accept"); +#endif + m_i2p_listen_socket.reset(); + if (e == asio::error::operation_aborted) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(tcp::endpoint( + address_v4::any(), m_listen_interface.port()), listen_failed_alert::accept + , e, listen_failed_alert::i2p)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("cannot bind to port %d: %s" + , m_listen_interface.port(), e.message().c_str()); +#endif + return; + } + open_new_incoming_i2p_connection(); + incoming_connection(s); + } +#endif + + bool session_impl::incoming_packet(error_code const& ec + , udp::endpoint const& ep, char const* buf, int size) + { +#ifdef TORRENT_STATS + ++m_num_messages[on_udp_counter]; +#endif + + if (ec) + { + // don't bubble up operation aborted errors to the user + if (ec != asio::error::operation_aborted + && m_alerts.should_post()) + m_alerts.post_alert(udp_error_alert(ep, ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("UDP socket error: (%d) %s", ec.value(), ec.message().c_str()); +#endif + } + return false; + } + + void session_impl::async_accept(boost::shared_ptr const& listener, bool ssl) + { + TORRENT_ASSERT(!m_abort); + shared_ptr c(new socket_type(m_io_service)); + stream_socket* str = 0; + +#ifdef TORRENT_USE_OPENSSL + if (ssl) + { + // accept connections initializing the SSL connection to + // use the generic m_ssl_ctx context. However, since it has + // the servername callback set on it, we will switch away from + // this context into a specific torrent once we start handshaking + c->instantiate >(m_io_service, &m_ssl_ctx); + str = &c->get >()->next_layer(); + } + else +#endif + { + c->instantiate(m_io_service); + str = c->get(); + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_accept_connection"); +#endif + listener->async_accept(*str + , boost::bind(&session_impl::on_accept_connection, this, c + , boost::weak_ptr(listener), _1, ssl)); + } + + void session_impl::on_accept_connection(shared_ptr const& s + , weak_ptr listen_socket, error_code const& e, bool ssl) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_accept_connection"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_accept_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + boost::shared_ptr listener = listen_socket.lock(); + if (!listener) return; + + if (e == asio::error::operation_aborted) return; + + if (m_abort) return; + + error_code ec; + if (e) + { + tcp::endpoint ep = listener->local_endpoint(ec); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("error accepting connection on '%s': %s" + , print_endpoint(ep).c_str(), e.message().c_str()); +#endif +#ifdef TORRENT_WINDOWS + // Windows sometimes generates this error. It seems to be + // non-fatal and we have to do another async_accept. + if (e.value() == ERROR_SEM_TIMEOUT) + { + async_accept(listener, ssl); + return; + } +#endif +#ifdef TORRENT_BSD + // Leopard sometimes generates an "invalid argument" error. It seems to be + // non-fatal and we have to do another async_accept. + if (e.value() == EINVAL) + { + async_accept(listener, ssl); + return; + } +#endif + if (e == boost::system::errc::too_many_files_open) + { + // if we failed to accept an incoming connection + // because we have too many files open, try again + // and lower the number of file descriptors used + // elsewere. + if (m_settings.connections_limit > 10) + { + // now, disconnect a random peer + torrent_map::iterator i = std::max_element(m_torrents.begin() + , m_torrents.end(), boost::bind(&torrent::num_peers + , boost::bind(&torrent_map::value_type::second, _1))); + + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert( + torrent_handle(), performance_alert::too_few_file_descriptors)); + + if (i != m_torrents.end()) + { + i->second->disconnect_peers(1, e); + } + + m_settings.connections_limit = m_connections.size(); + } + // try again, but still alert the user of the problem + async_accept(listener, ssl); + } + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(ep, listen_failed_alert::accept, e + , ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp)); + return; + } + async_accept(listener, ssl); + +#ifdef TORRENT_USE_OPENSSL + if (ssl) + { + // for SSL connections, incoming_connection() is called + // after the handshake is done +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::ssl_handshake"); +#endif + s->get >()->async_accept_handshake( + boost::bind(&session_impl::ssl_handshake, this, _1, s)); + m_incoming_sockets.insert(s); + } + else +#endif + { + incoming_connection(s); + } + } + +#ifdef TORRENT_USE_OPENSSL + + // to test SSL connections, one can use this openssl command template: + // + // openssl s_client -cert .pem -key .pem + // -CAfile .pem -debug -connect 127.0.0.1:4433 -tls1 + // -servername + + void session_impl::ssl_handshake(error_code const& ec, boost::shared_ptr s) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::ssl_handshake"); +#endif + m_incoming_sockets.erase(s); + + error_code e; + tcp::endpoint endp = s->remote_endpoint(e); + if (e) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" *** peer SSL handshake done [ ip: %s ec: %s socket: %s ]" + , print_endpoint(endp).c_str(), ec.message().c_str(), s->type_name()); +#endif + + if (ec) + { + if (m_alerts.should_post()) + { + m_alerts.post_alert(peer_error_alert(torrent_handle(), endp + , peer_id(), ec)); + } + return; + } + + incoming_connection(s); + } + +#endif // TORRENT_USE_OPENSSL + + void session_impl::incoming_connection(boost::shared_ptr const& s) + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_USE_OPENSSL + // add the current time to the PRNG, to add more unpredictability + boost::uint64_t now = total_microseconds(time_now_hires() - min_time()); + // assume 12 bits of entropy (i.e. about 8 milliseconds) + RAND_add(&now, 8, 1.5); +#endif + + if (m_paused) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION [ ignored, paused ]"); +#endif + return; + } + + error_code ec; + // we got a connection request! + tcp::endpoint endp = s->remote_endpoint(ec); + + if (ec) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION FAILED, could " + "not retrieve remote endpoint " + , print_endpoint(endp).c_str(), ec.message().c_str()); +#endif + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" <== INCOMING CONNECTION %s type: %s" + , print_endpoint(endp).c_str(), s->type_name()); +#endif + + if (m_alerts.should_post()) + { + m_alerts.post_alert(incoming_connection_alert(s->type(), endp)); + } + + if (!m_settings.enable_incoming_utp + && s->get()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" rejected uTP connection"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::utp_disabled)); + return; + } + + if (!m_settings.enable_incoming_tcp + && s->get()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" rejected TCP connection"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::tcp_disabled)); + return; + } + + // local addresses do not count, since it's likely + // coming from our own client through local service discovery + // and it does not reflect whether or not a router is open + // for incoming connections or not. + if (!is_local(endp.address())) + m_incoming_connection = true; + + // this filter is ignored if a single torrent + // is set to ignore the filter, since this peer might be + // for that torrent + if (m_non_filtered_torrents == 0 + && (m_ip_filter.access(endp.address()) & ip_filter::blocked)) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("filtered blocked ip"); +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle() + , endp.address(), peer_blocked_alert::ip_filter)); + return; + } + + // check if we have any active torrents + // if we don't reject the connection + if (m_torrents.empty()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" There are no torrents, disconnect"); +#endif + return; + } + + // don't allow more connections than the max setting + bool reject = false; + if (m_settings.ignore_limits_on_local_network && is_local(endp.address())) + reject = m_settings.connections_limit < INT_MAX / 12 + && num_connections() >= m_settings.connections_limit * 12 / 10; + else + reject = num_connections() >= m_settings.connections_limit + m_settings.connections_slack; + + if (reject) + { + if (m_alerts.should_post()) + { + m_alerts.post_alert( + peer_disconnected_alert(torrent_handle(), endp, peer_id() + , error_code(errors::too_many_connections, get_libtorrent_category()))); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("number of connections limit exceeded (conns: %d" + ", limit: %d slack: %d), connection rejected\n" + , num_connections(), m_settings.connections_limit, m_settings.connections_slack); +#endif + return; + } + + // if we don't have any active torrents, there's no + // point in accepting this connection. If, however, + // the setting to start up queued torrents when they + // get an incoming connection is enabled, we cannot + // perform this check. + if (!m_settings.incoming_starts_queued_torrents) + { + bool has_active_torrent = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + if (i->second->allows_peers()) + { + has_active_torrent = true; + break; + } + } + if (!has_active_torrent) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" There are no _active_ torrents, disconnect"); +#endif + return; + } + } + + setup_socket_buffers(*s); + + boost::intrusive_ptr c( + new bt_peer_connection(*this, s, endp, 0, get_peer_id())); +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + + if (!c->is_disconnecting()) + { + // in case we've exceeded the limit, let this peer know that + // as soon as it's received the handshake, it needs to either + // disconnect or pick another peer to disconnect + if (num_connections() >= m_settings.connections_limit) + c->peer_exceeds_limit(); + + TORRENT_ASSERT(!c->m_in_constructor); + m_connections.insert(c); + c->start(); + // update the next disk peer round-robin cursor + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + } + } + + void session_impl::setup_socket_buffers(socket_type& s) + { + error_code ec; + set_socket_buffer_size(s, m_settings, ec); + } + + void session_impl::on_socks_accept(boost::shared_ptr const& s + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_socks_accept"); +#endif + m_socks_listen_socket.reset(); + if (e == asio::error::operation_aborted) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(tcp::endpoint( + address_v4::any(), m_listen_interface.port()), listen_failed_alert::accept, e + , listen_failed_alert::socks5)); + return; + } + open_new_incoming_socks_connection(); + incoming_connection(s); + } + + void session_impl::close_connection(peer_connection const* p + , error_code const& ec) + { + TORRENT_ASSERT(is_network_thread()); + + // someone else is holding a reference, it's important that + // it's destructed from the network thread. Make sure the + // last reference is held by the network thread. + if (p->refcount() != 1) + m_undead_peers.push_back((peer_connection*)p); + +// too expensive +// INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG +// for (aux::session_impl::torrent_map::const_iterator i = m_torrents.begin() +// , end(m_torrents.end()); i != end; ++i) +// TORRENT_ASSERT(!i->second->has_peer((peer_connection*)p)); +#endif + +#if defined(TORRENT_LOGGING) + session_log(" CLOSING CONNECTION %s : %s" + , print_endpoint(p->remote()).c_str(), ec.message().c_str()); +#endif + + TORRENT_ASSERT(p->is_disconnecting()); + + if (!p->is_choked() && !p->ignore_unchoke_slots()) --m_num_unchoked; + TORRENT_ASSERT(p->refcount() > 0); + + boost::intrusive_ptr sp((peer_connection*)p); + connection_map::iterator i = m_connections.find(sp); + // make sure the next disk peer round-robin cursor stays valid + if (m_next_disk_peer == i) ++m_next_disk_peer; + if (i != m_connections.end()) m_connections.erase(i); + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + } + + // implements alert_dispatcher + bool session_impl::post_alert(alert* a) + { + if (!m_alerts.should_post(a)) return false; + m_alerts.post_alert_ptr(a); + return true; + } + + void session_impl::set_peer_id(peer_id const& id) + { + m_peer_id = id; + } + + void session_impl::set_key(int key) + { + m_key = key; + } + + void session_impl::unchoke_peer(peer_connection& c) + { + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + torrent* t = c.associated_torrent().lock().get(); + TORRENT_ASSERT(t); + if (t->unchoke_peer(c)) + ++m_num_unchoked; + } + + void session_impl::choke_peer(peer_connection& c) + { + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + torrent* t = c.associated_torrent().lock().get(); + TORRENT_ASSERT(t); + if (t->choke_peer(c)) + --m_num_unchoked; + } + + int session_impl::next_port() + { + std::pair const& out_ports = m_settings.outgoing_ports; + if (m_next_port < out_ports.first || m_next_port > out_ports.second) + m_next_port = out_ports.first; + + int port = m_next_port; + ++m_next_port; + if (m_next_port > out_ports.second) m_next_port = out_ports.first; +#if defined TORRENT_LOGGING + session_log(" *** BINDING OUTGOING CONNECTION [ port: %d ]", port); +#endif + return port; + } + + // this function is called from the disk-io thread + // when the disk queue is low enough to post new + // write jobs to it. It will go through all peer + // connections that are blocked on the disk and + // wake them up + void session_impl::on_disk_queue() + { +#ifdef TORRENT_STATS + ++m_num_messages[on_disk_queue_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + + // just to play it safe + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + + // never loop more times than there are connections + // keep in mind that connections may disconnect + // while we're looping, that's why this is a reliable + // way of limiting it + int limit = m_connections.size(); + + while (m_next_disk_peer != m_connections.end() && limit > 0 && can_write_to_disk()) + { + --limit; + peer_connection* p = m_next_disk_peer->get(); + ++m_next_disk_peer; + if (m_next_disk_peer == m_connections.end()) m_next_disk_peer = m_connections.begin(); + if ((p->m_channel_state[peer_connection::download_channel] + & peer_info::bw_disk) == 0) continue; + p->on_disk(); + } + + } + + // used to cache the current time + // every 100 ms. This is cheaper + // than a system call and can be + // used where more accurate time + // is not necessary + extern ptime g_current_time; + + initialize_timer::initialize_timer() + { + g_current_time = time_now_hires(); + } + + void session_impl::on_tick(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_tick"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_tick_counter]; +#endif + + TORRENT_ASSERT(is_network_thread()); + + ptime now = time_now_hires(); + aux::g_current_time = now; +// too expensive +// INVARIANT_CHECK; + + // we have to keep ticking the utp socket manager + // until they're all closed + if (m_abort && m_utp_socket_manager.num_sockets() == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + fprintf(stderr, "uTP sockets left: %d\n", m_utp_socket_manager.num_sockets()); +#endif + return; + } + + if (e == asio::error::operation_aborted) return; + + if (e) + { +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + session_log("*** TICK TIMER FAILED %s", e.message().c_str()); +#endif + ::abort(); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_tick"); +#endif + error_code ec; + m_timer.expires_at(now + milliseconds(m_settings.tick_interval), ec); + m_timer.async_wait(bind(&session_impl::on_tick, this, _1)); + + m_download_rate.update_quotas(now - m_last_tick); + m_upload_rate.update_quotas(now - m_last_tick); + + m_last_tick = now; + + m_utp_socket_manager.tick(now); + + // only tick the following once per second + if (now - m_last_second_tick < seconds(1)) return; + +#ifndef TORRENT_DISABLE_DHT + if (m_dht_interval_update_torrents < 40 + && m_dht_interval_update_torrents != int(m_torrents.size())) + update_dht_announce_interval(); +#endif + + // remove undead peers that only have this list as their reference keeping them alive + std::vector >::iterator i = std::remove_if( + m_undead_peers.begin(), m_undead_peers.end() + , boost::bind(&peer_connection::refcount, _1) == 1); + m_undead_peers.erase(i, m_undead_peers.end()); + + int tick_interval_ms = int(total_milliseconds(now - m_last_second_tick)); + m_last_second_tick = now; + m_tick_residual += tick_interval_ms - 1000; + + boost::int64_t session_time = total_seconds(now - m_created); + if (session_time > 65000) + { + // we're getting close to the point where our timestamps + // in policy::peer are wrapping. We need to step all counters back + // four hours. This means that any timestamp that refers to a time + // more than 18.2 - 4 = 14.2 hours ago, will be incremented to refer to + // 14.2 hours ago. + + m_created += hours(4); + + const int four_hours = 60 * 60 * 4; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + policy& p = i->second->get_policy(); + for (policy::iterator j = p.begin_peer() + , end(p.end_peer()); j != end; ++j) + { + policy::peer* pe = *j; + + if (pe->last_optimistically_unchoked < four_hours) + pe->last_optimistically_unchoked = 0; + else + pe->last_optimistically_unchoked -= four_hours; + + if (pe->last_connected < four_hours) + pe->last_connected = 0; + else + pe->last_connected -= four_hours; + } + } + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_tick(); + } TORRENT_CATCH(std::exception&) {} + } +#endif + + // don't do any of the following while we're shutting down + if (m_abort) return; + + // -------------------------------------------------------------- + // RSS feeds + // -------------------------------------------------------------- + if (now > m_next_rss_update) + update_rss_feeds(); + + switch (m_settings.mixed_mode_algorithm) + { + case session_settings::prefer_tcp: + m_tcp_upload_channel.throttle(0); + m_tcp_download_channel.throttle(0); + break; + case session_settings::peer_proportional: + { + int num_peers[2][2] = {{0, 0}, {0, 0}}; + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end());i != end; ++i) + { + peer_connection& p = *(*i); + if (p.in_handshake()) continue; + int protocol = 0; + if (is_utp(*p.get_socket())) protocol = 1; + + if (p.download_queue().size() + p.request_queue().size() > 0) + ++num_peers[protocol][peer_connection::download_channel]; + if (p.upload_queue().size() > 0) + ++num_peers[protocol][peer_connection::upload_channel]; + } + + bandwidth_channel* tcp_channel[] = { &m_tcp_upload_channel, &m_tcp_download_channel }; + int stat_rate[] = {m_stat.upload_rate(), m_stat.download_rate() }; + // never throttle below this + int lower_limit[] = {5000, 30000}; + + for (int i = 0; i < 2; ++i) + { + // if there are no uploading uTP peers, don't throttle TCP up + if (num_peers[1][i] == 0) + { + tcp_channel[i]->throttle(0); + } + else + { + if (num_peers[0][i] == 0) num_peers[0][i] = 1; + int total_peers = num_peers[0][i] + num_peers[1][i]; + // this are 64 bits since it's multiplied by the number + // of peers, which otherwise might overflow an int + boost::uint64_t rate = stat_rate[i]; + tcp_channel[i]->throttle((std::max)(int(rate * num_peers[0][i] / total_peers), lower_limit[i])); + } + } + } + break; + } + + // -------------------------------------------------------------- + // auto managed torrent + // -------------------------------------------------------------- + if (!m_paused) m_auto_manage_time_scaler--; + if (m_auto_manage_time_scaler < 0) + { + m_auto_manage_time_scaler = settings().auto_manage_interval; + recalculate_auto_managed_torrents(); + } + + // -------------------------------------------------------------- + // check for incoming connections that might have timed out + // -------------------------------------------------------------- + + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = (*i).get(); + ++i; + // ignore connections that already have a torrent, since they + // are ticked through the torrents' second_tick + if (!p->associated_torrent().expired()) continue; + // TODO: have a separate list for these connections, instead of having to loop through all of them + if (m_last_tick - p->connected_time() > seconds(m_settings.handshake_timeout)) + p->disconnect(errors::timed_out); + } + + // -------------------------------------------------------------- + // second_tick every torrent + // -------------------------------------------------------------- + + int congested_torrents = 0; + int uncongested_torrents = 0; + + // count the number of seeding torrents vs. downloading + // torrents we are running + int num_seeds = 0; + int num_downloads = 0; + + // count the number of peers of downloading torrents + int num_downloads_peers = 0; + + torrent_map::iterator least_recently_scraped = m_torrents.end(); + int num_paused_auto_managed = 0; + + int num_checking = 0; + int num_queued = 0; + +#if TORRENT_DEBUG_STREAMING > 0 + printf("\033[2J\033[0;0H"); +#endif + + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end();) + { + torrent& t = *i->second; + TORRENT_ASSERT(!t.is_aborted()); + if (t.statistics().upload_rate() * 11 / 10 > t.upload_limit()) + ++congested_torrents; + else + ++uncongested_torrents; + + if (t.state() == torrent_status::checking_files) ++num_checking; + else if (t.state() == torrent_status::queued_for_checking && !t.is_paused()) ++num_queued; + + if (t.is_auto_managed() && t.is_paused() && !t.has_error()) + { + ++num_paused_auto_managed; + if (least_recently_scraped == m_torrents.end() + || least_recently_scraped->second->seconds_since_last_scrape() + < t.seconds_since_last_scrape()) + { + least_recently_scraped = i; + } + } + + if (t.is_finished()) + { + ++num_seeds; + } + else + { + ++num_downloads; + num_downloads_peers += t.num_peers(); + } + + ++i; + t.second_tick(m_stat, tick_interval_ms); + } + + // some people claim that there sometimes can be cases where + // there is no torrent being checked, but there are torrents + // waiting to be checked. I have never seen this, and I can't + // see a way for it to happen. But, if it does, start one of + // the queued torrents + if (num_checking == 0 && num_queued > 0 && !m_paused) + { + TORRENT_ASSERT(false); + check_queue_t::iterator i = std::min_element(m_queued_for_checking.begin() + , m_queued_for_checking.end(), boost::bind(&torrent::queue_position, _1) + < boost::bind(&torrent::queue_position, _2)); + if (i != m_queued_for_checking.end()) + { + (*i)->start_checking(); + } + } + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + int dht_down; + int dht_up; + m_dht->network_stats(dht_up, dht_down); + m_stat.sent_dht_bytes(dht_up); + m_stat.received_dht_bytes(dht_down); + } +#endif + + if (m_settings.rate_limit_ip_overhead) + { + m_download_channel.use_quota( +#ifndef TORRENT_DISABLE_DHT + m_stat.download_dht() + +#endif + m_stat.download_tracker()); + + m_upload_channel.use_quota( +#ifndef TORRENT_DISABLE_DHT + m_stat.upload_dht() + +#endif + m_stat.upload_tracker()); + + int up_limit = m_upload_channel.throttle(); + int down_limit = m_download_channel.throttle(); + + if (down_limit > 0 + && m_stat.download_ip_overhead() >= down_limit + && m_alerts.should_post()) + { + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && m_stat.upload_ip_overhead() >= up_limit + && m_alerts.should_post()) + { + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::upload_limit_too_low)); + } + } + + m_peak_up_rate = (std::max)(m_stat.upload_rate(), m_peak_up_rate); + m_peak_down_rate = (std::max)(m_stat.download_rate(), m_peak_down_rate); + + m_stat.second_tick(tick_interval_ms); + + TORRENT_ASSERT(least_recently_scraped == m_torrents.end() + || (least_recently_scraped->second->is_paused() + && least_recently_scraped->second->is_auto_managed())); + +#ifdef TORRENT_STATS + + if (m_stats_logging_enabled) + { + print_log_line(tick_interval_ms, now); + } +#endif + + // -------------------------------------------------------------- + // scrape paused torrents that are auto managed + // (unless the session is paused) + // -------------------------------------------------------------- + if (!is_paused()) + { + --m_auto_scrape_time_scaler; + if (m_auto_scrape_time_scaler <= 0) + { + m_auto_scrape_time_scaler = m_settings.auto_scrape_interval + / (std::max)(1, num_paused_auto_managed); + if (m_auto_scrape_time_scaler < m_settings.auto_scrape_min_interval) + m_auto_scrape_time_scaler = m_settings.auto_scrape_min_interval; + + if (least_recently_scraped != m_torrents.end()) + { + least_recently_scraped->second->scrape_tracker(); + } + } + } + + // -------------------------------------------------------------- + // refresh explicit disk read cache + // -------------------------------------------------------------- + --m_cache_rotation_timer; + if (m_settings.explicit_read_cache + && m_cache_rotation_timer <= 0) + { + m_cache_rotation_timer = m_settings.explicit_cache_interval; + + torrent_map::iterator least_recently_refreshed = m_torrents.begin(); + if (m_next_explicit_cache_torrent >= int(m_torrents.size())) + m_next_explicit_cache_torrent = 0; + + std::advance(least_recently_refreshed, m_next_explicit_cache_torrent); + + // how many blocks does this torrent get? + int cache_size = (std::max)(0, m_settings.cache_size * 9 / 10); + + if (m_connections.empty()) + { + // if we don't have any connections at all, split the + // cache evenly across all torrents + cache_size = cache_size / (std::max)(int(m_torrents.size()), 1); + } + else + { + cache_size = cache_size * least_recently_refreshed->second->num_peers() + / m_connections.size(); + } + + if (least_recently_refreshed != m_torrents.end()) + least_recently_refreshed->second->refresh_explicit_cache(cache_size); + ++m_next_explicit_cache_torrent; + } + + // -------------------------------------------------------------- + // connect new peers + // -------------------------------------------------------------- + + try_connect_more_peers(num_downloads, num_downloads_peers); + + // -------------------------------------------------------------- + // unchoke set calculations + // -------------------------------------------------------------- + m_unchoke_time_scaler--; + if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) + { + m_unchoke_time_scaler = settings().unchoke_interval; + recalculate_unchoke_slots(congested_torrents + , uncongested_torrents); + } + + // -------------------------------------------------------------- + // optimistic unchoke calculation + // -------------------------------------------------------------- + m_optimistic_unchoke_time_scaler--; + if (m_optimistic_unchoke_time_scaler <= 0) + { + m_optimistic_unchoke_time_scaler + = settings().optimistic_unchoke_interval; + recalculate_optimistic_unchoke_slots(); + } + + // -------------------------------------------------------------- + // disconnect peers when we have too many + // -------------------------------------------------------------- + --m_disconnect_time_scaler; + if (m_disconnect_time_scaler <= 0) + { + m_disconnect_time_scaler = m_settings.peer_turnover_interval; + + // if the connections_limit is too low, the disconnect + // logic is disabled, since it is too disruptive + if (m_settings.connections_limit > 5) + { + if (num_connections() >= m_settings.connections_limit * m_settings.peer_turnover_cutoff + && !m_torrents.empty()) + { + // every 90 seconds, disconnect the worst peers + // if we have reached the connection limit + torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end() + , boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _1)) + < boost::bind(&torrent::num_peers, boost::bind(&torrent_map::value_type::second, _2))); + + TORRENT_ASSERT(i != m_torrents.end()); + int peers_to_disconnect = (std::min)((std::max)( + int(i->second->num_peers() * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + i->second->disconnect_peers(peers_to_disconnect + , error_code(errors::optimistic_disconnect, get_libtorrent_category())); + } + else + { + // if we haven't reached the global max. see if any torrent + // has reached its local limit + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = i->second; + + // ths disconnect logic is disabled for torrents with + // too low connection limit + if (t->num_peers() < t->max_connections() * m_settings.peer_turnover_cutoff + || t->max_connections() < 6) + continue; + + int peers_to_disconnect = (std::min)((std::max)(int(i->second->num_peers() + * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + t->disconnect_peers(peers_to_disconnect + , error_code(errors::optimistic_disconnect, get_libtorrent_category())); + } + } + } + } + + while (m_tick_residual >= 1000) m_tick_residual -= 1000; +// m_peer_pool.release_memory(); + } + +#ifdef TORRENT_STATS + + void session_impl::enable_stats_logging(bool s) + { + if (m_stats_logging_enabled == s) return; + + m_stats_logging_enabled = s; + + reset_stat_counters(); + if (!s) + { + if (m_stats_logger) fclose(m_stats_logger); + m_stats_logger = 0; + } + else + { + rotate_stats_log(); + get_thread_cpu_usage(&m_network_thread_cpu_usage); + } + } + + void session_impl::reset_stat_counters() + { + m_end_game_piece_picker_blocks = 0; + m_piece_picker_blocks = 0; + m_piece_picks = 0; + m_reject_piece_picks = 0; + m_unchoke_piece_picks = 0; + m_incoming_redundant_piece_picks = 0; + m_incoming_piece_picks = 0; + m_end_game_piece_picks = 0; + m_snubbed_piece_picks = 0; + m_connection_attempts = 0; + m_num_banned_peers = 0; + m_banned_for_hash_failure = 0; + + m_piece_requests = 0; + m_max_piece_requests = 0; + m_invalid_piece_requests = 0; + m_choked_piece_requests = 0; + m_cancelled_piece_requests = 0; + m_piece_rejects = 0; + + memset(m_num_messages, 0, sizeof(m_num_messages)); + memset(m_send_buffer_sizes, 0, sizeof(m_send_buffer_sizes)); + memset(m_recv_buffer_sizes, 0, sizeof(m_recv_buffer_sizes)); + } + + void session_impl::print_log_line(int tick_interval_ms, ptime now) + { + int connect_candidates = 0; + + int checking_torrents = 0; + int stopped_torrents = 0; + int upload_only_torrents = 0; + int downloading_torrents = 0; + int seeding_torrents = 0; + int queued_seed_torrents = 0; + int queued_download_torrents = 0; + int error_torrents = 0; + + int num_peers = 0; + int peer_dl_rate_buckets[7]; + int peer_ul_rate_buckets[7]; + memset(peer_dl_rate_buckets, 0, sizeof(peer_dl_rate_buckets)); + memset(peer_ul_rate_buckets, 0, sizeof(peer_ul_rate_buckets)); + int outstanding_requests = 0; + int outstanding_end_game_requests = 0; + int outstanding_write_blocks = 0; + + int peers_up_interested = 0; + int peers_down_interesting = 0; + int peers_up_requests = 0; + int peers_down_requests = 0; + int peers_up_send_buffer = 0; + + // number of torrents that want more peers + int num_want_more_peers = 0; + + // number of peers among torrents with a peer limit + int num_limited_peers = 0; + // sum of limits of all torrents with a peer limit + boost::uint64_t total_peers_limit = 0; + + std::vector dq; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + int connection_slots = (std::max)(t->max_connections() - t->num_peers(), 0); + int candidates = t->get_policy().num_connect_candidates(); + connect_candidates += (std::min)(candidates, connection_slots); + num_peers += t->get_policy().num_peers(); + + if (t->want_more_peers()) ++num_want_more_peers; + if (t->max_connections() > 0) + { + num_limited_peers += t->num_peers(); + total_peers_limit += t->max_connections(); + } + + if (t->has_error()) + ++error_torrents; + else + { + if (t->is_paused()) + { + if (!t->is_auto_managed()) + ++stopped_torrents; + else + { + if (t->is_seed()) + ++queued_seed_torrents; + else + ++queued_download_torrents; + } + } + else + { + if (i->second->state() == torrent_status::checking_files + || i->second->state() == torrent_status::queued_for_checking) + ++checking_torrents; + else if (i->second->is_seed()) + ++seeding_torrents; + else if (i->second->is_upload_only()) + ++upload_only_torrents; + else + ++downloading_torrents; + } + } + + dq.clear(); + i->second->get_download_queue(&dq); + for (std::vector::iterator j = dq.begin() + , end(dq.end()); j != end; ++j) + { + for (int k = 0; k < j->blocks_in_piece; ++k) + { + block_info& bi = j->blocks[k]; + if (bi.state == block_info::requested) + { + ++outstanding_requests; + if (bi.num_peers > 1) ++outstanding_end_game_requests; + } + else if (bi.state == block_info::writing) + ++outstanding_write_blocks; + } + } + } + int tcp_up_rate = 0; + int tcp_down_rate = 0; + int utp_up_rate = 0; + int utp_down_rate = 0; + int utp_peak_send_delay = 0; + int utp_peak_recv_delay = 0; + boost::uint64_t utp_send_delay_sum = 0; + boost::uint64_t utp_recv_delay_sum = 0; + int num_utp_peers = 0; + int num_tcp_peers = 0; + int utp_num_delay_sockets = 0; + int utp_num_recv_delay_sockets = 0; + int num_complete_connections = 0; + int num_half_open = 0; + int peers_down_unchoked = 0; + int peers_up_unchoked = 0; + int num_end_game_peers = 0; + int reading_bytes = 0; + int pending_incoming_reqs = 0; + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + if (p->is_connecting()) + { + ++num_half_open; + continue; + } + + ++num_complete_connections; + if (!p->is_choked()) ++peers_up_unchoked; + if (!p->has_peer_choked()) ++peers_down_unchoked; + if (!p->download_queue().empty()) ++peers_down_requests; + if (p->is_peer_interested()) ++peers_up_interested; + if (p->is_interesting()) ++peers_down_interesting; + if (p->send_buffer_size() > 100 || !p->upload_queue().empty() || p->num_reading_bytes() > 0) + ++peers_up_requests; + if (p->endgame()) ++num_end_game_peers; + reading_bytes += p->num_reading_bytes(); + + pending_incoming_reqs += int(p->upload_queue().size()); + + int dl_bucket = 0; + int dl_rate = p->statistics().download_payload_rate(); + if (dl_rate == 0) dl_bucket = 0; + else if (dl_rate < 2000) dl_bucket = 1; + else if (dl_rate < 5000) dl_bucket = 2; + else if (dl_rate < 10000) dl_bucket = 3; + else if (dl_rate < 50000) dl_bucket = 4; + else if (dl_rate < 100000) dl_bucket = 5; + else dl_bucket = 6; + + int ul_rate = p->statistics().upload_payload_rate(); + int ul_bucket = 0; + if (ul_rate == 0) ul_bucket = 0; + else if (ul_rate < 2000) ul_bucket = 1; + else if (ul_rate < 5000) ul_bucket = 2; + else if (ul_rate < 10000) ul_bucket = 3; + else if (ul_rate < 50000) ul_bucket = 4; + else if (ul_rate < 100000) ul_bucket = 5; + else ul_bucket = 6; + + ++peer_dl_rate_buckets[dl_bucket]; + ++peer_ul_rate_buckets[ul_bucket]; + + boost::uint64_t upload_rate = int(p->statistics().upload_rate()); + int buffer_size_watermark = upload_rate + * m_settings.send_buffer_watermark_factor / 100; + if (buffer_size_watermark < m_settings.send_buffer_low_watermark) + buffer_size_watermark = m_settings.send_buffer_low_watermark; + else if (buffer_size_watermark > m_settings.send_buffer_watermark) + buffer_size_watermark = m_settings.send_buffer_watermark; + if (p->send_buffer_size() + p->num_reading_bytes() >= buffer_size_watermark) + ++peers_up_send_buffer; + + utp_stream* utp_socket = p->get_socket()->get(); +#ifdef TORRENT_USE_OPENSSL + if (!utp_socket) + { + ssl_stream* ssl_str = p->get_socket()->get >(); + if (ssl_str) utp_socket = &ssl_str->next_layer(); + } +#endif + if (utp_socket) + { + utp_up_rate += ul_rate; + utp_down_rate += dl_rate; + int send_delay = utp_socket->send_delay(); + int recv_delay = utp_socket->recv_delay(); + utp_peak_send_delay = (std::max)(utp_peak_send_delay, send_delay); + utp_peak_recv_delay = (std::max)(utp_peak_recv_delay, recv_delay); + if (send_delay > 0) + { + utp_send_delay_sum += send_delay; + ++utp_num_delay_sockets; + } + if (recv_delay > 0) + { + utp_recv_delay_sum += recv_delay; + ++utp_num_recv_delay_sockets; + } + ++num_utp_peers; + } + else + { + tcp_up_rate += ul_rate; + tcp_down_rate += dl_rate; + ++num_tcp_peers; + } + + } + + int low_watermark = m_settings.max_queued_disk_bytes_low_watermark == 0 + || m_settings.max_queued_disk_bytes_low_watermark >= m_settings.max_queued_disk_bytes + ? size_type(m_settings.max_queued_disk_bytes) * 7 / 8 + : m_settings.max_queued_disk_bytes_low_watermark; + + if (now - m_last_log_rotation > hours(1)) + rotate_stats_log(); + + // system memory stats + vm_statistics_data_t vm_stat; + get_vm_stats(&vm_stat); + thread_cpu_usage cur_cpu_usage; + get_thread_cpu_usage(&cur_cpu_usage); + + if (m_stats_logger) + { + cache_status cs = m_disk_thread.status(); + session_status sst = status(); + + m_read_ops.add_sample((cs.reads - m_last_cache_status.reads) * 1000.0 / float(tick_interval_ms)); + m_write_ops.add_sample((cs.writes - m_last_cache_status.writes) * 1000.0 / float(tick_interval_ms)); + + int total_job_time = cs.cumulative_job_time == 0 ? 1 : cs.cumulative_job_time; + +#ifdef TORRENT_USE_VALGRIND +#define STAT_LOGL(type, val) VALGRIND_CHECK_VALUE_IS_DEFINED(val); fprintf(m_stats_logger, "%" #type "\t", val) +#else +#define STAT_LOGL(type, val) fprintf(m_stats_logger, "%" #type "\t", val) +#endif +#define STAT_LOG(type, val) fprintf(m_stats_logger, "%" #type "\t", val) + + STAT_LOG(f, total_milliseconds(now - m_last_log_rotation) / 1000.f); + size_type uploaded = m_stat.total_upload() - m_last_uploaded; + STAT_LOG(d, int(uploaded)); + size_type downloaded = m_stat.total_download() - m_last_downloaded; + STAT_LOG(d, int(downloaded)); + STAT_LOGL(d, downloading_torrents); + STAT_LOGL(d, seeding_torrents); + STAT_LOGL(d, num_complete_connections); + STAT_LOGL(d, num_half_open); + STAT_LOG(d, m_disk_thread.disk_allocations()); + STAT_LOGL(d, num_peers); + STAT_LOGL(d, logging_allocator::allocations); + STAT_LOGL(d, logging_allocator::allocated_bytes); + STAT_LOGL(d, checking_torrents); + STAT_LOGL(d, stopped_torrents); + STAT_LOGL(d, upload_only_torrents); + STAT_LOGL(d, queued_seed_torrents); + STAT_LOGL(d, queued_download_torrents); + STAT_LOG(d, m_upload_rate.queue_size()); + STAT_LOG(d, m_download_rate.queue_size()); + STAT_LOGL(d, m_disk_queues[peer_connection::upload_channel]); + STAT_LOGL(d, m_disk_queues[peer_connection::download_channel]); + STAT_LOG(d, m_stat.upload_rate()); + STAT_LOG(d, m_stat.download_rate()); + STAT_LOG(d, int(m_disk_thread.queue_buffer_size())); + STAT_LOGL(d, peer_dl_rate_buckets[0]); + STAT_LOGL(d, peer_dl_rate_buckets[1]); + STAT_LOGL(d, peer_dl_rate_buckets[2]); + STAT_LOGL(d, peer_dl_rate_buckets[3]); + STAT_LOGL(d, peer_dl_rate_buckets[4]); + STAT_LOGL(d, peer_dl_rate_buckets[5]); + STAT_LOGL(d, peer_dl_rate_buckets[6]); + STAT_LOGL(d, peer_ul_rate_buckets[0]); + STAT_LOGL(d, peer_ul_rate_buckets[1]); + STAT_LOGL(d, peer_ul_rate_buckets[2]); + STAT_LOGL(d, peer_ul_rate_buckets[3]); + STAT_LOGL(d, peer_ul_rate_buckets[4]); + STAT_LOGL(d, peer_ul_rate_buckets[5]); + STAT_LOGL(d, peer_ul_rate_buckets[6]); + STAT_LOGL(d, m_error_peers); + STAT_LOGL(d, peers_down_interesting); + STAT_LOGL(d, peers_down_unchoked); + STAT_LOGL(d, peers_down_requests); + STAT_LOGL(d, peers_up_interested); + STAT_LOGL(d, peers_up_unchoked); + STAT_LOGL(d, peers_up_requests); + STAT_LOGL(d, m_disconnected_peers); + STAT_LOGL(d, m_eof_peers); + STAT_LOGL(d, m_connreset_peers); + STAT_LOGL(d, outstanding_requests); + STAT_LOGL(d, outstanding_end_game_requests); + STAT_LOGL(d, outstanding_write_blocks); + STAT_LOGL(d, m_end_game_piece_picker_blocks); + STAT_LOGL(d, m_piece_picker_blocks); + STAT_LOGL(d, m_piece_picks); + STAT_LOGL(d, m_reject_piece_picks); + STAT_LOGL(d, m_unchoke_piece_picks); + STAT_LOGL(d, m_incoming_redundant_piece_picks); + STAT_LOGL(d, m_incoming_piece_picks); + STAT_LOGL(d, m_end_game_piece_picks); + STAT_LOGL(d, m_snubbed_piece_picks); + STAT_LOGL(d, m_connect_timeouts); + STAT_LOGL(d, m_uninteresting_peers); + STAT_LOGL(d, m_timeout_peers); + STAT_LOG(f, (float(m_total_failed_bytes) * 100.f / (m_stat.total_payload_download() == 0 ? 1 : m_stat.total_payload_download()))); + STAT_LOG(f, (float(m_total_redundant_bytes) * 100.f / (m_stat.total_payload_download() == 0 ? 1 : m_stat.total_payload_download()))); + STAT_LOG(f, (float(m_stat.total_protocol_download()) * 100.f / (m_stat.total_download() == 0 ? 1 : m_stat.total_download()))); + STAT_LOG(f, float(cs.average_read_time) / 1000000.f); + STAT_LOG(f, float(cs.average_write_time) / 1000000.f); + STAT_LOG(f, float(cs.average_queue_time) / 1000000.f); + STAT_LOG(d, int(cs.job_queue_length)); + STAT_LOG(d, int(cs.queued_bytes)); + STAT_LOG(d, int(cs.blocks_read_hit - m_last_cache_status.blocks_read_hit)); + STAT_LOG(d, int(cs.blocks_read - m_last_cache_status.blocks_read)); + STAT_LOG(d, int(cs.blocks_written - m_last_cache_status.blocks_written)); + STAT_LOG(d, int(m_total_failed_bytes - m_last_failed)); + STAT_LOG(d, int(m_total_redundant_bytes - m_last_redundant)); + STAT_LOGL(d, error_torrents); + STAT_LOGL(d, cs.read_cache_size); + STAT_LOGL(d, cs.cache_size); + STAT_LOGL(d, cs.total_used_buffers); + STAT_LOG(f, float(cs.average_hash_time) / 1000000.f); + STAT_LOG(f, float(cs.average_job_time) / 1000000.f); + STAT_LOG(f, float(cs.average_sort_time) / 1000000.f); + STAT_LOGL(d, m_connection_attempts); + STAT_LOGL(d, m_num_banned_peers); + STAT_LOGL(d, m_banned_for_hash_failure); + STAT_LOGL(d, m_settings.cache_size); + STAT_LOGL(d, m_settings.connections_limit); + STAT_LOGL(d, connect_candidates); + STAT_LOG(d, int(m_settings.max_queued_disk_bytes)); + STAT_LOGL(d, low_watermark); + STAT_LOG(f, float(cs.cumulative_read_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_write_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_hash_time * 100.f / total_job_time)); + STAT_LOG(f, float(cs.cumulative_sort_time * 100.f / total_job_time)); + STAT_LOG(d, int(cs.total_read_back - m_last_cache_status.total_read_back)); + STAT_LOG(f, float(cs.total_read_back * 100.f / (cs.blocks_written == 0 ? 1: cs.blocks_written))); + STAT_LOGL(d, cs.read_queue_size); + STAT_LOG(f, float(tick_interval_ms) / 1000.f); + STAT_LOG(f, float(m_tick_residual) / 1000.f); + STAT_LOGL(d, m_allowed_upload_slots); + STAT_LOG(d, m_settings.unchoke_slots_limit * 2); + STAT_LOG(d, m_stat.low_pass_upload_rate()); + STAT_LOG(d, m_stat.low_pass_download_rate()); + STAT_LOGL(d, num_end_game_peers); + STAT_LOGL(d, tcp_up_rate); + STAT_LOGL(d, tcp_down_rate); + STAT_LOG(d, int(m_tcp_upload_channel.throttle())); + STAT_LOG(d, int(m_tcp_download_channel.throttle())); + STAT_LOGL(d, utp_up_rate); + STAT_LOGL(d, utp_down_rate); + STAT_LOG(f, float(utp_peak_send_delay) / 1000000.f); + STAT_LOG(f, float(utp_num_delay_sockets ? float(utp_send_delay_sum) / float(utp_num_delay_sockets) : 0) / 1000000.f); + STAT_LOG(f, float(utp_peak_recv_delay) / 1000000.f); + STAT_LOG(f, float(utp_num_recv_delay_sockets ? float(utp_recv_delay_sum) / float(utp_num_recv_delay_sockets) : 0) / 1000000.f); + STAT_LOG(f, float(cs.reads - m_last_cache_status.reads) * 1000.0 / float(tick_interval_ms)); + STAT_LOG(f, float(cs.writes - m_last_cache_status.writes) * 1000.0 / float(tick_interval_ms)); + + STAT_LOG(d, int(vm_stat.active_count)); + STAT_LOG(d, int(vm_stat.inactive_count)); + STAT_LOG(d, int(vm_stat.wire_count)); + STAT_LOG(d, int(vm_stat.free_count)); + STAT_LOG(d, int(vm_stat.pageins - m_last_vm_stat.pageins)); + STAT_LOG(d, int(vm_stat.pageouts - m_last_vm_stat.pageouts)); + STAT_LOG(d, int(vm_stat.faults - m_last_vm_stat.faults)); + + STAT_LOG(d, m_read_ops.mean()); + STAT_LOG(d, m_write_ops.mean()); + + STAT_LOGL(d, reading_bytes); + + for (int i = 0; i < max_messages; ++i) + { + STAT_LOGL(d, m_num_messages[i]); + } + int num_max = sizeof(m_send_buffer_sizes)/sizeof(m_send_buffer_sizes[0]); + for (int i = 0; i < num_max; ++i) + { + STAT_LOGL(d, m_send_buffer_sizes[i]); + } + num_max = sizeof(m_recv_buffer_sizes)/sizeof(m_recv_buffer_sizes[0]); + for (int i = 0; i < num_max; ++i) + { + STAT_LOGL(d, m_recv_buffer_sizes[i]); + } + + STAT_LOG(f, total_microseconds(cur_cpu_usage.user_time + - m_network_thread_cpu_usage.user_time) / double(tick_interval_ms * 10)); + STAT_LOG(f, (total_microseconds(cur_cpu_usage.system_time + - m_network_thread_cpu_usage.system_time) + + total_microseconds(cur_cpu_usage.user_time + - m_network_thread_cpu_usage.user_time)) + / double(tick_interval_ms * 10)); + + for (int i = 0; i < torrent::waste_reason_max; ++i) + { + STAT_LOG(f, (m_redundant_bytes[i] * 100.) / double(m_total_redundant_bytes == 0 ? 1 : m_total_redundant_bytes)); + } + + STAT_LOGL(d, m_no_memory_peers); + STAT_LOGL(d, m_too_many_peers); + STAT_LOGL(d, m_transport_timeout_peers); + + STAT_LOGL(d, sst.utp_stats.num_idle); + STAT_LOGL(d, sst.utp_stats.num_syn_sent); + STAT_LOGL(d, sst.utp_stats.num_connected); + STAT_LOGL(d, sst.utp_stats.num_fin_sent); + STAT_LOGL(d, sst.utp_stats.num_close_wait); + + STAT_LOGL(d, num_tcp_peers); + STAT_LOGL(d, num_utp_peers); + + STAT_LOGL(d, m_connrefused_peers); + STAT_LOGL(d, m_connaborted_peers); + STAT_LOGL(d, m_perm_peers); + STAT_LOGL(d, m_buffer_peers); + STAT_LOGL(d, m_unreachable_peers); + STAT_LOGL(d, m_broken_pipe_peers); + STAT_LOGL(d, m_addrinuse_peers); + STAT_LOGL(d, m_no_access_peers); + STAT_LOGL(d, m_invalid_arg_peers); + STAT_LOGL(d, m_aborted_peers); + + STAT_LOGL(d, m_error_incoming_peers); + STAT_LOGL(d, m_error_outgoing_peers); + STAT_LOGL(d, m_error_rc4_peers); + STAT_LOGL(d, m_error_encrypted_peers); + STAT_LOGL(d, m_error_tcp_peers); + STAT_LOGL(d, m_error_utp_peers); + + STAT_LOG(d, int(m_connections.size())); + STAT_LOGL(d, pending_incoming_reqs); + STAT_LOG(f, num_complete_connections == 0 ? 0.f : (float(pending_incoming_reqs) / num_complete_connections)); + + STAT_LOGL(d, num_want_more_peers); + STAT_LOG(f, total_peers_limit == 0 ? 0 : float(num_limited_peers) / total_peers_limit); + + STAT_LOGL(d, m_piece_requests); + STAT_LOGL(d, m_max_piece_requests); + STAT_LOGL(d, m_invalid_piece_requests); + STAT_LOGL(d, m_choked_piece_requests); + STAT_LOGL(d, m_cancelled_piece_requests); + STAT_LOGL(d, m_piece_rejects); + + STAT_LOGL(d, peers_up_send_buffer); + + STAT_LOG(d, int(sst.utp_stats.packet_loss)); + STAT_LOG(d, int(sst.utp_stats.timeout)); + STAT_LOG(d, int(sst.utp_stats.packets_in)); + STAT_LOG(d, int(sst.utp_stats.packets_out)); + STAT_LOG(d, int(sst.utp_stats.fast_retransmit)); + STAT_LOG(d, int(sst.utp_stats.packet_resend)); + STAT_LOG(d, int(sst.utp_stats.samples_above_target)); + STAT_LOG(d, int(sst.utp_stats.samples_below_target)); + STAT_LOG(d, int(sst.utp_stats.payload_pkts_in)); + STAT_LOG(d, int(sst.utp_stats.payload_pkts_out)); + STAT_LOG(d, int(sst.utp_stats.invalid_pkts_in)); + STAT_LOG(d, int(sst.utp_stats.redundant_pkts_in)); + + fprintf(m_stats_logger, "\n"); + +#undef STAT_LOG +#undef STAT_LOGL + + m_last_cache_status = cs; + m_last_vm_stat = vm_stat; + m_network_thread_cpu_usage = cur_cpu_usage; + m_last_failed = m_total_failed_bytes; + m_last_redundant = m_total_redundant_bytes; + m_last_uploaded = m_stat.total_upload(); + m_last_downloaded = m_stat.total_download(); + } + + reset_stat_counters(); + } +#endif // TORRENT_STATS + + void session_impl::update_rss_feeds() + { + time_t now_posix = time(0); + ptime min_update = max_time(); + ptime now = time_now(); + for (std::vector >::iterator i + = m_feeds.begin(), end(m_feeds.end()); i != end; ++i) + { + feed& f = **i; + int delta = f.next_update(now_posix); + if (delta <= 0) + delta = f.update_feed(); + TORRENT_ASSERT(delta >= 0); + ptime next_update = now + seconds(delta); + if (next_update < min_update) min_update = next_update; + } + m_next_rss_update = min_update; + } + + void session_impl::prioritize_connections(boost::weak_ptr t) + { + m_prio_torrents.push_back(std::make_pair(t, 10)); + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::add_dht_node(udp::endpoint n) + { + TORRENT_ASSERT(is_network_thread()); + + if (m_dht) m_dht->add_node(n); + } + + void session_impl::prioritize_dht(boost::weak_ptr t) + { + TORRENT_ASSERT(m_dht); + m_dht_torrents.push_back(t); + // trigger a DHT announce right away if we just added a new torrent and + // there's no back-log. in the timer handler, as long as there are more + // high priority torrents to be announced to the DHT, it will keep the + // timer interval short until all torrents have been announced. + if (m_dht_torrents.size() == 1) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + error_code ec; + m_dht_announce_timer.expires_from_now(seconds(0), ec); + m_dht_announce_timer.async_wait( + bind(&session_impl::on_dht_announce, this, _1)); + } + } + + void session_impl::on_dht_announce(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_dht_announce"); +#endif + TORRENT_ASSERT(is_network_thread()); + if (e) return; + + if (m_abort) return; + + TORRENT_ASSERT(m_dht); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_announce"); +#endif + // announce to DHT every 15 minutes + int delay = (std::max)(m_settings.dht_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + + if (!m_dht_torrents.empty()) + { + // we have prioritized torrents that need + // an initial DHT announce. Don't wait too long + // until we announce those. + delay = (std::min)(4, delay); + } + + error_code ec; + m_dht_announce_timer.expires_from_now(seconds(delay), ec); + m_dht_announce_timer.async_wait( + bind(&session_impl::on_dht_announce, this, _1)); + + if (!m_dht_torrents.empty()) + { + boost::shared_ptr t; + do + { + t = m_dht_torrents.front().lock(); + m_dht_torrents.pop_front(); + } + while (!t && !m_dht_torrents.empty()); + if (t) + { + t->dht_announce(); + return; + } + } + if (m_torrents.empty()) return; + + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); + m_next_dht_torrent->second->dht_announce(); + ++m_next_dht_torrent; + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); + } +#endif + + void session_impl::on_lsd_announce(error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_lsd_announce"); +#endif +#ifdef TORRENT_STATS + ++m_num_messages[on_lsd_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + if (e) return; + + if (m_abort) return; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_lsd_announce"); +#endif + // announce on local network every 5 minutes + int delay = (std::max)(m_settings.local_service_announce_interval + / (std::max)(int(m_torrents.size()), 1), 1); + error_code ec; + m_lsd_announce_timer.expires_from_now(seconds(delay), ec); + m_lsd_announce_timer.async_wait( + bind(&session_impl::on_lsd_announce, this, _1)); + + if (m_torrents.empty()) return; + + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + m_next_lsd_torrent->second->lsd_announce(); + ++m_next_lsd_torrent; + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + } + + void session_impl::auto_manage_torrents(std::vector& list + , int& dht_limit, int& tracker_limit, int& lsd_limit + , int& hard_limit, int type_limit) + { + for (std::vector::iterator i = list.begin() + , end(list.end()); i != end; ++i) + { + torrent* t = *i; + + if ((t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking)) + continue; + + --dht_limit; + --lsd_limit; + --tracker_limit; + t->set_announce_to_dht(dht_limit >= 0); + t->set_announce_to_trackers(tracker_limit >= 0); + t->set_announce_to_lsd(lsd_limit >= 0); + + if (!t->is_paused() && t->is_inactive() + && hard_limit > 0) + { + // the hard limit takes inactive torrents into account, but the + // download and seed limits don't. + continue; + } + + if (type_limit > 0 && hard_limit > 0) + { + --hard_limit; + --type_limit; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + t->log_to_all_peers("AUTO MANAGER STARTING TORRENT"); +#endif + t->set_allow_peers(true); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + t->log_to_all_peers("AUTO MANAGER PAUSING TORRENT"); +#endif + // use graceful pause for auto-managed torrents + t->set_allow_peers(false, true); + } + } + } + + void session_impl::recalculate_auto_managed_torrents() + { + INVARIANT_CHECK; + + m_need_auto_manage = false; + + // these vectors are filled with auto managed torrents + std::vector downloaders; + downloaders.reserve(m_torrents.size()); + std::vector seeds; + seeds.reserve(m_torrents.size()); + + // these counters are set to the number of torrents + // of each kind we're allowed to have active + int num_downloaders = settings().active_downloads; + int num_seeds = settings().active_seeds; + int dht_limit = settings().active_dht_limit; + int tracker_limit = settings().active_tracker_limit; + int lsd_limit = settings().active_lsd_limit; + int hard_limit = settings().active_limit; + + if (num_downloaders == -1) + num_downloaders = (std::numeric_limits::max)(); + if (num_seeds == -1) + num_seeds = (std::numeric_limits::max)(); + if (hard_limit == -1) + hard_limit = (std::numeric_limits::max)(); + if (dht_limit == -1) + dht_limit = (std::numeric_limits::max)(); + if (lsd_limit == -1) + lsd_limit = (std::numeric_limits::max)(); + if (tracker_limit == -1) + tracker_limit = (std::numeric_limits::max)(); + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + TORRENT_ASSERT(t); + + // checking torrents are not subject to auto-management + if (t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking) + { + if (t->is_auto_managed() && t->is_paused()) t->resume(); + continue; + } + if (t->is_auto_managed() && !t->has_error()) + { + TORRENT_ASSERT(t->m_resume_data_loaded || !t->valid_metadata()); + // this torrent is auto managed, add it to + // the list (depending on if it's a seed or not) + if (t->is_finished()) + seeds.push_back(t); + else + downloaders.push_back(t); + } + else if (!t->is_paused()) + { + TORRENT_ASSERT(t->m_resume_data_loaded || !t->valid_metadata()); + --hard_limit; + } + } + + bool handled_by_extension = false; + +#ifndef TORRENT_DISABLE_EXTENSIONS + // TODO: 0 allow extensions to sort torrents for queuing +#endif + + if (!handled_by_extension) + { + std::sort(downloaders.begin(), downloaders.end() + , boost::bind(&torrent::sequence_number, _1) < boost::bind(&torrent::sequence_number, _2)); + + std::sort(seeds.begin(), seeds.end() + , boost::bind(&torrent::seed_rank, _1, boost::ref(m_settings)) + > boost::bind(&torrent::seed_rank, _2, boost::ref(m_settings))); + } + + if (settings().auto_manage_prefer_seeds) + { + auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_seeds); + auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_downloaders); + } + else + { + auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_downloaders); + auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit + , hard_limit, num_seeds); + } + } + + void session_impl::recalculate_optimistic_unchoke_slots() + { + TORRENT_ASSERT(is_network_thread()); + if (m_allowed_upload_slots == 0) return; + + std::vector opt_unchoke; + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + TORRENT_ASSERT(p); + policy::peer* pi = p->peer_info_struct(); + if (!pi) continue; + if (pi->web_seed) continue; + torrent* t = p->associated_torrent().lock().get(); + if (!t) continue; + if (t->is_paused()) continue; + + if (pi->optimistically_unchoked) + { + TORRENT_ASSERT(!p->is_choked()); + opt_unchoke.push_back(pi); + } + + if (!p->is_connecting() + && !p->is_disconnecting() + && p->is_peer_interested() + && t->free_upload_slots() + && p->is_choked() + && !p->ignore_unchoke_slots() + && t->valid_metadata()) + { + opt_unchoke.push_back(pi); + } + } + + // find the peers that has been waiting the longest to be optimistically + // unchoked + + // avoid having a bias towards peers that happen to be sorted first + std::random_shuffle(opt_unchoke.begin(), opt_unchoke.end()); + + // sort all candidates based on when they were last optimistically + // unchoked. + std::sort(opt_unchoke.begin(), opt_unchoke.end() + , boost::bind(&policy::peer::last_optimistically_unchoked, _1) + < boost::bind(&policy::peer::last_optimistically_unchoked, _2)); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + if ((*i)->on_optimistic_unchoke(opt_unchoke)) + break; + } +#endif + + int num_opt_unchoke = m_settings.num_optimistic_unchoke_slots; + if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, m_allowed_upload_slots / 5); + + // unchoke the first num_opt_unchoke peers in the candidate set + // and make sure that the others are choked + for (std::vector::iterator i = opt_unchoke.begin() + , end(opt_unchoke.end()); i != end; ++i) + { + policy::peer* pi = *i; + if (num_opt_unchoke > 0) + { + --num_opt_unchoke; + if (!pi->optimistically_unchoked) + { + torrent* t = pi->connection->associated_torrent().lock().get(); + bool ret = t->unchoke_peer(*pi->connection, true); + if (ret) + { + pi->optimistically_unchoked = true; + ++m_num_unchoked; + pi->last_optimistically_unchoked = boost::uint16_t(session_time()); + } + else + { + // we failed to unchoke it, increment the count again + ++num_opt_unchoke; + } + } + } + else + { + if (pi->optimistically_unchoked) + { + torrent* t = pi->connection->associated_torrent().lock().get(); + pi->optimistically_unchoked = false; + t->choke_peer(*pi->connection); + --m_num_unchoked; + } + } + } + } + + void session_impl::try_connect_more_peers(int num_downloads, int num_downloads_peers) + { + // let torrents connect to peers if they want to + // if there are any torrents and any free slots + + // this loop will "hand out" max(connection_speed + // , half_open.free_slots()) to the torrents, in a + // round robin fashion, so that every torrent is + // equally likely to connect to a peer + + int free_slots = m_half_open.free_slots(); + int max_connections = m_settings.connection_speed; + // boost connections are connections made by torrent connection + // boost, which are done immediately on a tracker response. These + // connections needs to be deducted from this second + if (m_boost_connections > 0) + { + if (m_boost_connections > max_connections) + { + m_boost_connections -= max_connections; + max_connections = 0; + } + else + { + max_connections -= m_boost_connections; + m_boost_connections = 0; + } + } + + // this logic is here to smooth out the number of new connection + // attempts over time, to prevent connecting a large number of + // sockets, wait 10 seconds, and then try again + int limit = (std::min)(m_settings.connections_limit - num_connections(), free_slots); + if (m_settings.smooth_connects && max_connections > (limit+1) / 2) + max_connections = (limit+1) / 2; + + // TODO: use a lower limit than m_settings.connections_limit + // to allocate the to 10% or so of connection slots for incoming + // connections + if (!m_torrents.empty() + && free_slots > -m_half_open.limit() + && num_connections() < m_settings.connections_limit + && !m_abort + && m_settings.connection_speed > 0 + && max_connections > 0) + { + // this is the maximum number of connections we will + // attempt this tick +// int average_peers = 0; +// if (num_downloads > 0) +// average_peers = num_downloads_peers / num_downloads; + + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + int steps_since_last_connect = 0; + int num_torrents = int(m_torrents.size()); + for (;;) + { + torrent& t = *m_next_connect_torrent->second; + if (t.want_more_peers()) + { + TORRENT_ASSERT(t.allows_peers()); + // have a bias to give more connection attempts + // to downloading torrents than seed, and even + // more to downloading torrents with less than + // average number of connections + int num_attempts = 1; + if (!t.is_finished() && m_num_active_downloading > 0) + { + // TODO: make this bias configurable + // TODO: also take average_peers into account, to create a + // bias for downloading torrents with < average peers + num_attempts += m_num_active_finished / m_num_active_downloading; + } + while (m_current_connect_attempts < num_attempts) + { + TORRENT_TRY + { + ++m_current_connect_attempts; + if (t.try_connect_peer()) + { + --max_connections; + --free_slots; + steps_since_last_connect = 0; +#ifdef TORRENT_STATS + ++m_connection_attempts; +#endif + } + } + TORRENT_CATCH(std::bad_alloc&) + { + // we ran out of memory trying to connect to a peer + // lower the global limit to the number of peers + // we already have + m_settings.connections_limit = num_connections(); + if (m_settings.connections_limit < 2) m_settings.connections_limit = 2; + } + if (!t.want_more_peers()) break; + if (free_slots <= -m_half_open.limit()) return; + if (max_connections == 0) return; + if (num_connections() >= m_settings.connections_limit) return; + } + } + + ++m_next_connect_torrent; + m_current_connect_attempts = 0; + ++steps_since_last_connect; + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + // if we have gone a whole loop without + // handing out a single connection, break + if (steps_since_last_connect > num_torrents + 1) break; + // if there are no more free connection slots, abort + if (free_slots <= -m_half_open.limit()) break; + // if we should not make any more connections + // attempts this tick, abort + if (max_connections == 0) break; + // maintain the global limit on number of connections + if (num_connections() >= m_settings.connections_limit) break; + } + } + } + + void session_impl::recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents) + { + TORRENT_ASSERT(is_network_thread()); + INVARIANT_CHECK; + + ptime now = time_now(); + time_duration unchoke_interval = now - m_last_choke; + m_last_choke = now; + + // build list of all peers that are + // unchokable. + std::vector peers; + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + TORRENT_ASSERT(p); + ++i; + torrent* t = p->associated_torrent().lock().get(); + policy::peer* pi = p->peer_info_struct(); + + if (p->ignore_unchoke_slots() || t == 0 || pi == 0 || pi->web_seed || t->is_paused()) + continue; + + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + if (!p->is_choked() && p->is_interesting()) + { + if (!p->has_peer_choked()) + { + // we're unchoked, we may want to lower our estimated + // reciprocation rate + p->decrease_est_reciprocation_rate(); + } + else + { + // we've unchoked this peer, and it hasn't reciprocated + // we may want to increase our estimated reciprocation rate + p->increase_est_reciprocation_rate(); + } + } + } + + if (!p->is_peer_interested() + || p->is_disconnecting() + || p->is_connecting()) + { + // this peer is not unchokable. So, if it's unchoked + // already, make sure to choke it. + if (p->is_choked()) continue; + if (pi && pi->optimistically_unchoked) + { + pi->optimistically_unchoked = false; + // force a new optimistic unchoke + m_optimistic_unchoke_time_scaler = 0; + // TODO: post a message to have this happen + // immediately instead of waiting for the next tick + } + t->choke_peer(*p); + continue; + } + peers.push_back(p.get()); + } + + if (m_settings.choking_algorithm == session_settings::rate_based_choker) + { + m_allowed_upload_slots = 0; + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::upload_rate_compare, _1, _2)); + +#ifdef TORRENT_DEBUG + for (std::vector::const_iterator i = peers.begin() + , end(peers.end()), prev(peers.end()); i != end; ++i) + { + if (prev != end) + { + boost::shared_ptr t1 = (*prev)->associated_torrent().lock(); + TORRENT_ASSERT(t1); + boost::shared_ptr t2 = (*i)->associated_torrent().lock(); + TORRENT_ASSERT(t2); + TORRENT_ASSERT((*prev)->uploaded_in_last_round() * 1000 + * (1 + t1->priority()) / total_milliseconds(unchoke_interval) + >= (*i)->uploaded_in_last_round() * 1000 + * (1 + t2->priority()) / total_milliseconds(unchoke_interval)); + } + prev = i; + } +#endif + + // TODO: make configurable + int rate_threshold = 1024; + + for (std::vector::const_iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_connection const& p = **i; + int rate = int(p.uploaded_in_last_round() + * 1000 / total_milliseconds(unchoke_interval)); + + if (rate < rate_threshold) break; + + ++m_allowed_upload_slots; + + // TODO: make configurable + rate_threshold += 1024; + } + // allow one optimistic unchoke + ++m_allowed_upload_slots; + } + + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + // if we're using the bittyrant choker, sort peers by their return + // on investment. i.e. download rate / upload rate + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::bittyrant_unchoke_compare, _1, _2)); + } + else + { + // sorts the peers that are eligible for unchoke by download rate and secondary + // by total upload. The reason for this is, if all torrents are being seeded, + // the download rate will be 0, and the peers we have sent the least to should + // be unchoked + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::unchoke_compare, _1, _2)); + } + + // auto unchoke + int upload_limit = m_bandwidth_channel[peer_connection::upload_channel]->throttle(); + if (m_settings.choking_algorithm == session_settings::auto_expand_choker + && upload_limit > 0) + { + // if our current upload rate is less than 90% of our + // limit AND most torrents are not "congested", i.e. + // they are not holding back because of a per-torrent + // limit + if (m_stat.upload_rate() < upload_limit * 0.9f + && m_allowed_upload_slots <= m_num_unchoked + 1 + && congested_torrents < uncongested_torrents + && m_upload_rate.queue_size() < 2) + { + ++m_allowed_upload_slots; + } + else if (m_upload_rate.queue_size() > 1 + && m_allowed_upload_slots > m_settings.unchoke_slots_limit + && m_settings.unchoke_slots_limit >= 0) + { + --m_allowed_upload_slots; + } + } + + int num_opt_unchoke = m_settings.num_optimistic_unchoke_slots; + if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, m_allowed_upload_slots / 5); + + // reserve some upload slots for optimistic unchokes + int unchoke_set_size = m_allowed_upload_slots - num_opt_unchoke; + + int upload_capacity_left = 0; + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + upload_capacity_left = m_upload_channel.throttle(); + if (upload_capacity_left == 0) + { + // we don't know at what rate we can upload. If we have a + // measurement of the peak, use that + 10kB/s, otherwise + // assume 20 kB/s + upload_capacity_left = (std::max)(20000, m_peak_up_rate + 10000); + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::bittyrant_with_no_uplimit)); + } + } + + m_num_unchoked = 0; + // go through all the peers and unchoke the first ones and choke + // all the other ones. + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_connection* p = *i; + TORRENT_ASSERT(p); + TORRENT_ASSERT(!p->ignore_unchoke_slots()); + + // this will update the m_uploaded_at_last_unchoke + // #error this should be called for all peers! + p->reset_choke_counters(); + + torrent* t = p->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + + // if this peer should be unchoked depends on different things + // in different unchoked schemes + bool unchoke = false; + if (m_settings.choking_algorithm == session_settings::bittyrant_choker) + { + unchoke = p->est_reciprocation_rate() <= upload_capacity_left; + } + else + { + unchoke = unchoke_set_size > 0; + } + + if (unchoke) + { + upload_capacity_left -= p->est_reciprocation_rate(); + + // yes, this peer should be unchoked + if (p->is_choked()) + { + if (!t->unchoke_peer(*p)) + continue; + } + + --unchoke_set_size; + ++m_num_unchoked; + + TORRENT_ASSERT(p->peer_info_struct()); + if (p->peer_info_struct()->optimistically_unchoked) + { + // force a new optimistic unchoke + // since this one just got promoted into the + // proper unchoke set + m_optimistic_unchoke_time_scaler = 0; + p->peer_info_struct()->optimistically_unchoked = false; + } + } + else + { + // no, this peer should be shoked + TORRENT_ASSERT(p->peer_info_struct()); + if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) + t->choke_peer(*p); + if (!p->is_choked()) + ++m_num_unchoked; + } + } + } + +#if defined _MSC_VER && defined TORRENT_DEBUG + static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) + { throw; } +#endif + + void session_impl::main_thread() + { +#if defined _MSC_VER && defined TORRENT_DEBUG + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff + ::_set_se_translator(straight_to_debugger); +#endif +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + m_network_thread = pthread_self(); +#endif + TORRENT_ASSERT(is_network_thread()); + + // initialize async operations + init(); + + bool stop_loop = false; + while (!stop_loop) + { + error_code ec; + m_io_service.run(ec); + if (ec) + { +#ifdef TORRENT_DEBUG + fprintf(stderr, "%s\n", ec.message().c_str()); + std::string err = ec.message(); +#endif + TORRENT_ASSERT(false); + } + m_io_service.reset(); + + stop_loop = m_abort; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" locking mutex"); +#endif + +/* +#ifdef TORRENT_DEBUG + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + TORRENT_ASSERT(i->second->num_peers() == 0); + } +#endif +*/ +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log(" cleaning up torrents"); +#endif + m_torrents.clear(); + + TORRENT_ASSERT(m_torrents.empty()); + TORRENT_ASSERT(m_connections.empty()); + +#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS + m_network_thread = 0; +#endif + } + + + // the return value from this function is valid only as long as the + // session is locked! + boost::weak_ptr session_impl::find_torrent(sha1_hash const& info_hash) const + { + TORRENT_ASSERT(is_network_thread()); + + torrent_map::const_iterator i = m_torrents.find(info_hash); +#ifdef TORRENT_DEBUG + for (torrent_map::const_iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + torrent* p = boost::get_pointer(j->second); + TORRENT_ASSERT(p); + } +#endif + if (i != m_torrents.end()) return i->second; + return boost::weak_ptr(); + } + + boost::weak_ptr session_impl::find_torrent(std::string const& uuid) const + { + TORRENT_ASSERT(is_network_thread()); + + std::map >::const_iterator i + = m_uuids.find(uuid); + if (i != m_uuids.end()) return i->second; + return boost::weak_ptr(); + } + + // returns true if lhs is a better disconnect candidate than rhs + bool compare_disconnect_torrent(session_impl::torrent_map::value_type const& lhs + , session_impl::torrent_map::value_type const& rhs) + { + // a torrent with 0 peers is never a good disconnect candidate + // since there's nothing to disconnect + if ((lhs.second->num_peers() == 0) != (rhs.second->num_peers() == 0)) + return lhs.second->num_peers() != 0; + + // other than that, always prefer to disconnect peers from seeding torrents + // in order to not harm downloading ones + if (lhs.second->is_seed() != rhs.second->is_seed()) + return lhs.second->is_seed(); + + return lhs.second->num_peers() > rhs.second->num_peers(); + } + + boost::weak_ptr session_impl::find_disconnect_candidate_torrent() const + { + aux::session_impl::torrent_map::const_iterator i = std::min_element(m_torrents.begin(), m_torrents.end() + , boost::bind(&compare_disconnect_torrent, _1, _2)); + + TORRENT_ASSERT(i != m_torrents.end()); + if (i == m_torrents.end()) return boost::shared_ptr(); + + return i->second; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr session_impl::create_log(std::string const& name + , int instance, bool append) + { + error_code ec; + // current options are file_logger, cout_logger and null_logger + return boost::shared_ptr(new logger(m_logpath, name, instance, append)); + } + + void session_impl::session_log(char const* fmt, ...) const + { + if (!m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[400]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[450]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_logger) << buf; + } +#endif + + void session_impl::get_torrent_status(std::vector* ret + , boost::function const& pred + , boost::uint32_t flags) const + { + for (torrent_map::const_iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + torrent_status st; + i->second->status(&st, flags); + if (!pred(st)) continue; + ret->push_back(st); + } + } + + void session_impl::refresh_torrent_status(std::vector* ret + , boost::uint32_t flags) const + { + for (std::vector::iterator i + = ret->begin(), end(ret->end()); i != end; ++i) + { + boost::shared_ptr t = i->handle.m_torrent.lock(); + if (!t) continue; + t->status(&*i, flags); + } + } + + void session_impl::post_torrent_updates() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(is_network_thread()); + + std::auto_ptr alert(new state_update_alert()); + alert->status.reserve(m_state_updates.size()); + +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = true; +#endif + + for (std::vector >::iterator i = m_state_updates.begin() + , end(m_state_updates.end()); i != end; ++i) + { + boost::shared_ptr t = i->lock(); + if (!t) continue; + alert->status.push_back(torrent_status()); + t->status(&alert->status.back(), 0xffffffff); + t->clear_in_state_update(); + } + m_state_updates.clear(); + +#if TORRENT_USE_ASSERTS + m_posting_torrent_updates = false; +#endif + + m_alerts.post_alert_ptr(alert.release()); + } + + std::vector session_impl::get_torrents() const + { + std::vector ret; + + for (torrent_map::const_iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + ret.push_back(torrent_handle(i->second)); + } + return ret; + } + + torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash) + { + return torrent_handle(find_torrent(info_hash)); + } + + void session_impl::async_add_torrent(add_torrent_params* params) + { + error_code ec; + torrent_handle handle = add_torrent(*params, ec); + delete params; + } + + torrent_handle session_impl::add_torrent(add_torrent_params const& p + , error_code& ec) + { + torrent_handle h = add_torrent_impl(p, ec); + m_alerts.post_alert(add_torrent_alert(h, p, ec)); + return h; + } + + torrent_handle session_impl::add_torrent_impl(add_torrent_params const& p + , error_code& ec) + { + TORRENT_ASSERT(!p.save_path.empty()); + +#ifndef TORRENT_NO_DEPRECATE + p.update_flags(); +#endif + + add_torrent_params params = p; + if (string_begins_no_case("magnet:", params.url.c_str())) + { + parse_magnet_uri(params.url, params, ec); + if (ec) return torrent_handle(); + params.url.clear(); + } + + if (params.ti && params.ti->is_valid() && params.ti->num_files() == 0) + { + ec = errors::no_files_in_torrent; + return torrent_handle(); + } + +#ifndef TORRENT_DISABLE_DHT + // add p.dht_nodes to the DHT, if enabled + if (m_dht && !p.dht_nodes.empty()) + { + for (std::vector >::const_iterator i = p.dht_nodes.begin() + , end(p.dht_nodes.end()); i != end; ++i) + m_dht->add_node(*i); + } +#endif + +// INVARIANT_CHECK; + + if (is_aborted()) + { + ec = errors::session_is_closing; + return torrent_handle(); + } + + // figure out the info hash of the torrent + sha1_hash const* ih = 0; + sha1_hash tmp; + if (params.ti) ih = ¶ms.ti->info_hash(); + else if (!params.url.empty()) + { + // in order to avoid info-hash collisions, for + // torrents where we don't have an info-hash, but + // just a URL, set the temporary info-hash to the + // hash of the URL. This will be changed once we + // have the actual .torrent file + tmp = hasher(¶ms.url[0], params.url.size()).final(); + ih = &tmp; + } + else ih = ¶ms.info_hash; + + // we don't have a torrent file. If the user provided + // resume data, there may be some metadata in there + if ((!params.ti || !params.ti->is_valid()) + && !params.resume_data.empty()) + { + int pos; + error_code ec; + lazy_entry tmp; + lazy_entry const* info = 0; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("adding magnet link with resume data"); +#endif + if (lazy_bdecode(¶ms.resume_data[0], ¶ms.resume_data[0] + + params.resume_data.size(), tmp, ec, &pos) == 0 + && tmp.type() == lazy_entry::dict_t + && (info = tmp.dict_find_dict("info"))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("found metadata in resume data"); +#endif + // verify the info-hash of the metadata stored in the resume file matches + // the torrent we're loading + + std::pair buf = info->data_section(); + sha1_hash resume_ih = hasher(buf.first, buf.second).final(); + + // if url is set, the info_hash is not actually the info-hash of the + // torrent, but the hash of the URL, until we have the full torrent + // only require the info-hash to match if we actually passed in one + if (resume_ih == params.info_hash + || !params.url.empty() + || params.info_hash.is_all_zeros()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("info-hash matched"); +#endif + params.ti = new torrent_info(resume_ih); + + if (params.ti->parse_info_section(*info, ec, 0)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + session_log("successfully loaded metadata from resume file"); +#endif + // make the info-hash be the one in the resume file + params.info_hash = resume_ih; + ih = ¶ms.info_hash; + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("failed to load metadata from resume file: %s" + , ec.message().c_str()); +#endif + } + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + session_log("metadata info-hash failed"); + } +#endif + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + else + { + session_log("no metadata found"); + } +#endif + } + + // is the torrent already active? + boost::shared_ptr torrent_ptr = find_torrent(*ih).lock(); + if (!torrent_ptr && !params.uuid.empty()) torrent_ptr = find_torrent(params.uuid).lock(); + // if we still can't find the torrent, look for it by url + if (!torrent_ptr && !params.url.empty()) + { + std::map >::iterator i = std::find_if(m_torrents.begin() + , m_torrents.end(), boost::bind(&torrent::url, boost::bind(&std::pair >::second, _1)) == params.url); + if (i != m_torrents.end()) + torrent_ptr = i->second; + } + + if (torrent_ptr) + { + if ((params.flags & add_torrent_params::flag_duplicate_is_error) == 0) + { + if (!params.uuid.empty() && torrent_ptr->uuid().empty()) + torrent_ptr->set_uuid(params.uuid); + if (!params.url.empty() && torrent_ptr->url().empty()) + torrent_ptr->set_url(params.url); + if (!params.source_feed_url.empty() && torrent_ptr->source_feed_url().empty()) + torrent_ptr->set_source_feed_url(params.source_feed_url); + return torrent_handle(torrent_ptr); + } + + ec = errors::duplicate_torrent; + return torrent_handle(); + } + + int queue_pos = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int pos = i->second->queue_position(); + if (pos >= queue_pos) queue_pos = pos + 1; + } + + torrent_ptr.reset(new torrent(*this, m_listen_interface + , 16 * 1024, queue_pos, params, *ih)); + torrent_ptr->start(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::vector(torrent*, void*)> > + torrent_plugins_t; + + for (torrent_plugins_t::const_iterator i = params.extensions.begin() + , end(params.extensions.end()); i != end; ++i) + { + torrent_ptr->add_extension((*i)(torrent_ptr.get(), + params.userdata)); + } + + for (ses_extension_list_t::iterator i = m_ses_extensions.begin() + , end(m_ses_extensions.end()); i != end; ++i) + { + boost::shared_ptr tp((*i)->new_torrent(torrent_ptr.get(), params.userdata)); + if (tp) torrent_ptr->add_extension(tp); + } +#endif + +#ifndef TORRENT_DISABLE_DHT + if (m_dht && params.ti) + { + torrent_info::nodes_t const& nodes = params.ti->nodes(); + std::for_each(nodes.begin(), nodes.end(), boost::bind( + (void(dht::dht_tracker::*)(std::pair const&)) + &dht::dht_tracker::add_node + , boost::ref(m_dht), _1)); + } +#endif + + m_torrents.insert(std::make_pair(*ih, torrent_ptr)); + if (!params.uuid.empty() || !params.url.empty()) + m_uuids.insert(std::make_pair(params.uuid.empty() + ? params.url : params.uuid, torrent_ptr)); + + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_added_alert(torrent_ptr->get_handle())); + + // recalculate auto-managed torrents sooner (or put it off) + // if another torrent will be added within one second from now + // we want to put it off again anyway. So that while we're adding + // a boat load of torrents, we postpone the recalculation until + // we're done adding them all (since it's kind of an expensive operation) + if (params.flags & add_torrent_params::flag_auto_managed) + trigger_auto_manage(); + + return torrent_handle(torrent_ptr); + } + + void session_impl::queue_check_torrent(boost::shared_ptr const& t) + { + if (m_abort) return; + TORRENT_ASSERT(t->should_check_files()); + TORRENT_ASSERT(t->state() != torrent_status::checking_files); + if (m_queued_for_checking.empty()) t->start_checking(); + else t->set_state(torrent_status::queued_for_checking); + TORRENT_ASSERT(std::find(m_queued_for_checking.begin() + , m_queued_for_checking.end(), t) == m_queued_for_checking.end()); + m_queued_for_checking.push_back(t); + } + + void session_impl::dequeue_check_torrent(boost::shared_ptr const& t) + { + INVARIANT_CHECK; + TORRENT_ASSERT(t->state() == torrent_status::checking_files + || t->state() == torrent_status::queued_for_checking); + + if (m_queued_for_checking.empty()) return; + + boost::shared_ptr next_check = *m_queued_for_checking.begin(); + check_queue_t::iterator done = m_queued_for_checking.end(); + for (check_queue_t::iterator i = m_queued_for_checking.begin() + , end(m_queued_for_checking.end()); i != end; ++i) + { + // the reason m_paused is in there is because when the session + // is paused, all torrents that are queued ar all of a sudden + // not supposed to be queued anymore. The first torrent that gets + // removed from the queue will hence trigger this assert, without + // the m_paused exception + TORRENT_ASSERT(*i == t || (*i)->should_check_files() || m_paused); + if (*i == t) done = i; + else if (next_check == t || next_check->queue_position() > (*i)->queue_position()) + next_check = *i; + } + TORRENT_ASSERT(next_check != t || m_queued_for_checking.size() == 1); + // only start a new one if we removed the one that is checking + TORRENT_ASSERT(done != m_queued_for_checking.end()); + if (done == m_queued_for_checking.end()) return; + + if (next_check != t + && t->state() == torrent_status::checking_files + && !m_paused) + { + next_check->start_checking(); + } + + m_queued_for_checking.erase(done); + } + + void session_impl::remove_torrent(const torrent_handle& h, int options) + { + INVARIANT_CHECK; + + boost::shared_ptr tptr = h.m_torrent.lock(); + if (!tptr) return; + + remove_torrent_impl(tptr, options); + + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_removed_alert(tptr->get_handle(), tptr->info_hash())); + + tptr->abort(); + tptr->set_queue_position(-1); + } + + void session_impl::remove_torrent_impl(boost::shared_ptr tptr, int options) + { + // remove from uuid list + if (!tptr->uuid().empty()) + { + std::map >::iterator j + = m_uuids.find(tptr->uuid()); + if (j != m_uuids.end()) m_uuids.erase(j); + } + + torrent_map::iterator i = + m_torrents.find(tptr->torrent_file().info_hash()); + + // this torrent might be filed under the URL-hash + if (i == m_torrents.end() && !tptr->url().empty()) + { + std::string const& url = tptr->url(); + sha1_hash urlhash = hasher(&url[0], url.size()).final(); + i = m_torrents.find(urlhash); + } + + if (i == m_torrents.end()) return; + + torrent& t = *i->second; + if (options & session::delete_files) + { + if (!t.delete_files()) + { + if (m_alerts.should_post()) + m_alerts.post_alert(torrent_delete_failed_alert(t.get_handle() + , error_code(), t.torrent_file().info_hash())); + } + } + + tptr->update_guage(); + +#if TORRENT_USE_ASSERTS + sha1_hash i_hash = t.torrent_file().info_hash(); +#endif +#ifndef TORRENT_DISABLE_DHT + if (i == m_next_dht_torrent) + ++m_next_dht_torrent; +#endif + if (i == m_next_lsd_torrent) + ++m_next_lsd_torrent; + if (i == m_next_connect_torrent) + ++m_next_connect_torrent; + + m_torrents.erase(i); + +#ifndef TORRENT_DISABLE_DHT + if (m_next_dht_torrent == m_torrents.end()) + m_next_dht_torrent = m_torrents.begin(); +#endif + if (m_next_lsd_torrent == m_torrents.end()) + m_next_lsd_torrent = m_torrents.begin(); + if (m_next_connect_torrent == m_torrents.end()) + m_next_connect_torrent = m_torrents.begin(); + + std::list >::iterator k + = std::find(m_queued_for_checking.begin(), m_queued_for_checking.end(), tptr); + if (k != m_queued_for_checking.end()) m_queued_for_checking.erase(k); + TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); + } + + void session_impl::listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface, int flags) + { + INVARIANT_CHECK; + + tcp::endpoint new_interface; + if (net_interface && std::strlen(net_interface) > 0) + { + new_interface = tcp::endpoint(address::from_string(net_interface, ec), port_range.first); + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(listen_failed_alert(new_interface, listen_failed_alert::parse_addr, ec + , listen_failed_alert::tcp)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + session_log("listen_on: %s failed: %s" + , net_interface, ec.message().c_str()); +#endif + return; + } + } + else + { + new_interface = tcp::endpoint(address_v4::any(), port_range.first); + } + + m_listen_port_retries = port_range.second - port_range.first; + + // if the interface is the same and the socket is open + // don't do anything + if (new_interface == m_listen_interface + && !m_listen_sockets.empty()) + return; + + m_listen_interface = new_interface; + + open_listen_port(flags, ec); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + m_logger = create_log("main_session", listen_port(), false); + session_log("log created"); +#endif + } + + address session_impl::listen_address() const + { + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->external_address != address()) return i->external_address; + } + return address(); + } + + boost::uint16_t session_impl::listen_port() const + { + // if peer connections are set up to be received over a socks + // proxy, and it's the same one as we're using for the tracker + // just tell the tracker the socks5 port we're listening on + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + return m_socks_listen_port; + + // if not, don't tell the tracker anything if we're in force_proxy + // mode. We don't want to leak our listen port since it can + // potentially identify us if it is leaked elsewere + if (m_settings.force_proxy) return 0; + if (m_listen_sockets.empty()) return 0; + return m_listen_sockets.front().external_port; + } + + boost::uint16_t session_impl::ssl_listen_port() const + { +#ifdef TORRENT_USE_OPENSSL + // if peer connections are set up to be received over a socks + // proxy, and it's the same one as we're using for the tracker + // just tell the tracker the socks5 port we're listening on + if (m_socks_listen_socket && m_socks_listen_socket->is_open()) + return m_socks_listen_port; + + // if not, don't tell the tracker anything if we're in force_proxy + // mode. We don't want to leak our listen port since it can + // potentially identify us if it is leaked elsewere + if (m_settings.force_proxy) return 0; + if (m_listen_sockets.empty()) return 0; + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->ssl) return i->external_port; + } +#endif + return 0; + } + + void session_impl::announce_lsd(sha1_hash const& ih, int port, bool broadcast) + { + // use internal listen port for local peers + if (m_lsd.get()) + m_lsd->announce(ih, port, broadcast); + } + + void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) + { +#ifdef TORRENT_STATS + ++m_num_messages[on_lsd_peer_counter]; +#endif + TORRENT_ASSERT(is_network_thread()); + + INVARIANT_CHECK; + + boost::shared_ptr t = find_torrent(ih).lock(); + if (!t) return; + // don't add peers from lsd to private torrents + if (t->torrent_file().priv() || (t->torrent_file().is_i2p() + && !m_settings.allow_i2p_mixed)) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + session_log("added peer from local discovery: %s", print_endpoint(peer).c_str()); +#endif + t->get_policy().add_peer(peer, peer_id(0), peer_info::lsd, 0); + if (m_alerts.should_post()) + m_alerts.post_alert(lsd_peer_alert(t->get_handle(), peer)); + } + + void session_impl::on_port_map_log( + char const* msg, int map_transport) + { + TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); + // log message +#ifdef TORRENT_UPNP_LOGGING + char const* transport_names[] = {"NAT-PMP", "UPnP"}; + m_upnp_log << time_now_string() << " " + << transport_names[map_transport] << ": " << msg; +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_log_alert(map_transport, msg)); + } + + void session_impl::on_port_mapping(int mapping, address const& ip, int port + , error_code const& ec, int map_transport) + { + TORRENT_ASSERT(is_network_thread()); + + TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); + + if (mapping == m_udp_mapping[map_transport] && port != 0) + { + m_external_udp_port = port; + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + return; + } + + if (mapping == m_tcp_mapping[map_transport] && port != 0) + { + if (ip != address()) + { + // TODO: 1 report the proper address of the router as the source IP of + // this understanding of our external address, instead of the empty address + set_external_address(ip, source_router, address()); + } + + if (!m_listen_sockets.empty()) { + m_listen_sockets.front().external_address = ip; + m_listen_sockets.front().external_port = port; + } + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + return; + } + + if (ec) + { + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_error_alert(mapping + , map_transport, ec)); + } + else + { + if (m_alerts.should_post()) + m_alerts.post_alert(portmap_alert(mapping, port + , map_transport)); + } + } + + session_status session_impl::status() const + { +// INVARIANT_CHECK; + TORRENT_ASSERT(is_network_thread()); + + session_status s; + + s.optimistic_unchoke_counter = m_optimistic_unchoke_time_scaler; + s.unchoke_counter = m_unchoke_time_scaler; + + s.num_peers = (int)m_connections.size(); + s.num_unchoked = m_num_unchoked; + s.allowed_upload_slots = m_allowed_upload_slots; + + s.total_redundant_bytes = m_total_redundant_bytes; + s.total_failed_bytes = m_total_failed_bytes; + + s.up_bandwidth_queue = m_upload_rate.queue_size(); + s.down_bandwidth_queue = m_download_rate.queue_size(); + + s.up_bandwidth_bytes_queue = int(m_upload_rate.queued_bytes()); + s.down_bandwidth_bytes_queue = int(m_download_rate.queued_bytes()); + + s.disk_write_queue = m_disk_queues[peer_connection::download_channel]; + s.disk_read_queue = m_disk_queues[peer_connection::upload_channel]; + + s.has_incoming_connections = m_incoming_connection; + + // total + s.download_rate = m_stat.download_rate(); + s.total_upload = m_stat.total_upload(); + s.upload_rate = m_stat.upload_rate(); + s.total_download = m_stat.total_download(); + + // payload + s.payload_download_rate = m_stat.transfer_rate(stat::download_payload); + s.total_payload_download = m_stat.total_transfer(stat::download_payload); + s.payload_upload_rate = m_stat.transfer_rate(stat::upload_payload); + s.total_payload_upload = m_stat.total_transfer(stat::upload_payload); + +#ifndef TORRENT_DISABLE_FULL_STATS + // IP-overhead + s.ip_overhead_download_rate = m_stat.transfer_rate(stat::download_ip_protocol); + s.total_ip_overhead_download = m_stat.total_transfer(stat::download_ip_protocol); + s.ip_overhead_upload_rate = m_stat.transfer_rate(stat::upload_ip_protocol); + s.total_ip_overhead_upload = m_stat.total_transfer(stat::upload_ip_protocol); + +#ifndef TORRENT_DISABLE_DHT + // DHT protocol + s.dht_download_rate = m_stat.transfer_rate(stat::download_dht_protocol); + s.total_dht_download = m_stat.total_transfer(stat::download_dht_protocol); + s.dht_upload_rate = m_stat.transfer_rate(stat::upload_dht_protocol); + s.total_dht_upload = m_stat.total_transfer(stat::upload_dht_protocol); +#else + s.dht_download_rate = 0; + s.total_dht_download = 0; + s.dht_upload_rate = 0; + s.total_dht_upload = 0; +#endif // TORRENT_DISABLE_DHT + + // tracker + s.tracker_download_rate = m_stat.transfer_rate(stat::download_tracker_protocol); + s.total_tracker_download = m_stat.total_transfer(stat::download_tracker_protocol); + s.tracker_upload_rate = m_stat.transfer_rate(stat::upload_tracker_protocol); + s.total_tracker_upload = m_stat.total_transfer(stat::upload_tracker_protocol); +#else + // IP-overhead + s.ip_overhead_download_rate = 0; + s.total_ip_overhead_download = 0; + s.ip_overhead_upload_rate = 0; + s.total_ip_overhead_upload = 0; + + // DHT protocol + s.dht_download_rate = 0; + s.total_dht_download = 0; + s.dht_upload_rate = 0; + s.total_dht_upload = 0; + + // tracker + s.tracker_download_rate = 0; + s.total_tracker_download = 0; + s.tracker_upload_rate = 0; + s.total_tracker_upload = 0; +#endif + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + m_dht->dht_status(s); + } + else +#endif + { + s.dht_nodes = 0; + s.dht_node_cache = 0; + s.dht_torrents = 0; + s.dht_global_nodes = 0; + s.dht_total_allocations = 0; + } + + m_utp_socket_manager.get_status(s.utp_stats); + + int peerlist_size = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + peerlist_size += i->second->get_policy().num_peers(); + } + + s.peerlist_size = peerlist_size; + + return s; + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::start_dht() + { start_dht(m_dht_state); } + + void on_bootstrap(alert_manager& alerts) + { + if (alerts.should_post()) + alerts.post_alert(dht_bootstrap_alert()); + } + + void session_impl::start_dht(entry const& startup_state) + { + INVARIANT_CHECK; + + stop_dht(); + m_dht = new dht::dht_tracker(*this, m_udp_socket, m_dht_settings, &startup_state); + + for (std::list::iterator i = m_dht_router_nodes.begin() + , end(m_dht_router_nodes.end()); i != end; ++i) + { + m_dht->add_router_node(*i); + } + + m_dht->start(startup_state, boost::bind(&on_bootstrap, boost::ref(m_alerts))); + + m_udp_socket.subscribe(m_dht.get()); + } + + void session_impl::stop_dht() + { + if (!m_dht) return; + m_udp_socket.unsubscribe(m_dht.get()); + m_dht->stop(); + m_dht = 0; + } + + void session_impl::set_dht_settings(dht_settings const& settings) + { + m_dht_settings = settings; + } + +#ifndef TORRENT_NO_DEPRECATE + entry session_impl::dht_state() const + { + if (!m_dht) return entry(); + return m_dht->state(); + } +#endif + + void session_impl::add_dht_node_name(std::pair const& node) + { + if (m_dht) m_dht->add_node(node); + } + + void session_impl::add_dht_router(std::pair const& node) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("session_impl::on_dht_router_name_lookup"); +#endif + char port[7]; + snprintf(port, sizeof(port), "%d", node.second); + tcp::resolver::query q(node.first, port); + m_host_resolver.async_resolve(q, + boost::bind(&session_impl::on_dht_router_name_lookup, this, _1, _2)); + } + + void session_impl::on_dht_router_name_lookup(error_code const& e + , tcp::resolver::iterator host) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("session_impl::on_dht_router_name_lookup"); +#endif + if (e) + { + if (m_alerts.should_post()) + m_alerts.post_alert(dht_error_alert( + dht_error_alert::hostname_lookup, e)); + return; + } + + while (host != tcp::resolver::iterator()) + { + // router nodes should be added before the DHT is started (and bootstrapped) + udp::endpoint ep(host->endpoint().address(), host->endpoint().port()); + if (m_dht) m_dht->add_router_node(ep); + m_dht_router_nodes.push_back(ep); + ++host; + } + } + + // callback for dht_immutable_get + void session_impl::get_immutable_callback(sha1_hash target + , dht::item const& i) + { + TORRENT_ASSERT(!i.is_mutable()); + m_alerts.post_alert(dht_immutable_item_alert(target, i.value())); + } + + void session_impl::dht_get_immutable_item(sha1_hash const& target) + { + if (!m_dht) return; + m_dht->get_item(target, boost::bind(&session_impl::get_immutable_callback + , this, target, _1)); + } + + // callback for dht_mutable_get + void session_impl::get_mutable_callback(dht::item const& i) + { + TORRENT_ASSERT(i.is_mutable()); + m_alerts.post_alert(dht_mutable_item_alert(i.pk(), i.sig(), i.seq() + , i.salt(), i.value())); + } + + // key is a 32-byte binary string, the public key to look up. + // the salt is optional + void session_impl::dht_get_mutable_item(boost::array key + , std::string salt) + { + if (!m_dht) return; + m_dht->get_item(key.data(), boost::bind(&session_impl::get_mutable_callback + , this, _1), salt); + } + + void on_dht_put(alert_manager& alerts, sha1_hash target) + { + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(target)); + } + + void session_impl::dht_put_item(entry data, sha1_hash target) + { + if (!m_dht) return; + m_dht->put_item(data, boost::bind(&on_dht_put, boost::ref(m_alerts) + , target)); + } + + void put_mutable_callback(alert_manager& alerts, dht::item& i + , boost::function& + , boost::uint64_t&, std::string const&)> cb) + { + entry value = i.value(); + boost::array sig = i.sig(); + boost::array pk = i.pk(); + boost::uint64_t seq = i.seq(); + std::string salt = i.salt(); + cb(value, sig, seq, salt); + i.assign(value, salt, seq, pk.data(), sig.data()); + + if (alerts.should_post()) + alerts.post_alert(dht_put_alert(pk, sig, salt, seq)); + } + + void session_impl::dht_put_mutable_item(boost::array key + , boost::function& + , boost::uint64_t&, std::string const&)> cb + , std::string salt) + { + if (!m_dht) return; + m_dht->put_item(key.data(), boost::bind(&put_mutable_callback + , boost::ref(m_alerts), _1, cb), salt); + } + +#endif + + void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port) + { + int local, external, protocol; + if (nat == 0 && m_natpmp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_natpmp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_natpmp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_natpmp->add_mapping(natpmp::udp + , local_port, external_port); + return; + } + else if (nat == 1 && m_upnp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_upnp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_upnp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_upnp->add_mapping(upnp::udp + , local_port, external_port); + return; + } + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + void session_impl::set_pe_settings(pe_settings const& settings) + { + m_pe_settings = settings; + } +#endif + + bool session_impl::is_listening() const + { + return !m_listen_sockets.empty(); + } + + session_impl::~session_impl() + { + TORRENT_ASSERT(is_not_network_thread()); + + m_io_service.post(boost::bind(&session_impl::abort, this)); + + // we need to wait for the disk-io thread to + // die first, to make sure it won't post any + // more messages to the io_service containing references + // to disk_io_pool inside the disk_io_thread. Once + // the main thread has handled all the outstanding requests + // we know it's safe to destruct the disk thread. + m_disk_thread.join(); + +#if defined TORRENT_ASIO_DEBUGGING + int counter = 0; + while (log_async()) + { + sleep(1000); + ++counter; + printf("\x1b[2J\x1b[0;0H\x1b[33m==== Waiting to shut down: %d ==== conn-queue: %d connecting: %d timeout (next: %f max: %f)\x1b[0m\n\n" + , counter, m_half_open.size(), m_half_open.num_connecting(), m_half_open.next_timeout() + , m_half_open.max_timeout()); + } + async_dec_threads(); +#endif + + if (m_thread) m_thread->join(); + + m_udp_socket.unsubscribe(this); + m_udp_socket.unsubscribe(&m_utp_socket_manager); + m_udp_socket.unsubscribe(&m_tracker_manager); + + TORRENT_ASSERT(m_torrents.empty()); + TORRENT_ASSERT(m_connections.empty()); + TORRENT_ASSERT(m_connections.empty()); + +#ifdef TORRENT_REQUEST_LOGGING + if (m_request_log) fclose(m_request_log); +#endif + +#ifdef TORRENT_STATS + if (m_stats_logger) fclose(m_stats_logger); +#endif + } + +#ifndef TORRENT_NO_DEPRECATE + int session_impl::max_connections() const + { + return m_settings.connections_limit; + } + + int session_impl::max_uploads() const + { + return m_settings.unchoke_slots_limit; + } + + int session_impl::max_half_open_connections() const + { + return m_settings.half_open_limit; + } + + void session_impl::set_local_download_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.local_download_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_local_upload_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.local_upload_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_download_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.download_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_upload_rate_limit(int bytes_per_second) + { + session_settings s = m_settings; + s.upload_rate_limit = bytes_per_second; + set_settings(s); + } + + void session_impl::set_max_half_open_connections(int limit) + { + session_settings s = m_settings; + s.half_open_limit = limit; + set_settings(s); + } + + void session_impl::set_max_connections(int limit) + { + session_settings s = m_settings; + s.connections_limit = limit; + set_settings(s); + } + + void session_impl::set_max_uploads(int limit) + { + session_settings s = m_settings; + s.unchoke_slots_limit = limit; + set_settings(s); + } + + int session_impl::local_upload_rate_limit() const + { + return m_local_upload_channel.throttle(); + } + + int session_impl::local_download_rate_limit() const + { + return m_local_download_channel.throttle(); + } + + int session_impl::upload_rate_limit() const + { + return m_upload_channel.throttle(); + } + + int session_impl::download_rate_limit() const + { + return m_download_channel.throttle(); + } +#endif + + void session_impl::update_unchoke_limit() + { + m_allowed_upload_slots = m_settings.unchoke_slots_limit; + if (m_allowed_upload_slots < 0) + m_allowed_upload_slots = (std::numeric_limits::max)(); + + if (m_settings.num_optimistic_unchoke_slots >= m_allowed_upload_slots / 2) + { + if (m_alerts.should_post()) + m_alerts.post_alert(performance_alert(torrent_handle() + , performance_alert::too_many_optimistic_unchoke_slots)); + } + } + + void session_impl::update_rate_settings() + { + if (m_settings.half_open_limit <= 0) m_settings.half_open_limit + = (std::numeric_limits::max)(); + m_half_open.limit(m_settings.half_open_limit); + + if (m_settings.local_download_rate_limit < 0) + m_settings.local_download_rate_limit = 0; + m_local_download_channel.throttle(m_settings.local_download_rate_limit); + + if (m_settings.local_upload_rate_limit < 0) + m_settings.local_upload_rate_limit = 0; + m_local_upload_channel.throttle(m_settings.local_upload_rate_limit); + + if (m_settings.download_rate_limit < 0) + m_settings.download_rate_limit = 0; + m_download_channel.throttle(m_settings.download_rate_limit); + + if (m_settings.upload_rate_limit < 0) + m_settings.upload_rate_limit = 0; + m_upload_channel.throttle(m_settings.upload_rate_limit); + } + + void session_impl::update_connections_limit() + { + if (m_settings.connections_limit <= 0) + { + m_settings.connections_limit = (std::numeric_limits::max)(); +#if TORRENT_USE_RLIMIT + rlimit l; + if (getrlimit(RLIMIT_NOFILE, &l) == 0 + && l.rlim_cur != RLIM_INFINITY) + { + m_settings.connections_limit = l.rlim_cur - m_settings.file_pool_size; + if (m_settings.connections_limit < 5) m_settings.connections_limit = 5; + } +#endif + } + + if (num_connections() > m_settings.connections_limit && !m_torrents.empty()) + { + // if we have more connections that we're allowed, disconnect + // peers from the torrents so that they are all as even as possible + + int to_disconnect = num_connections() - m_settings.connections_limit; + + int last_average = 0; + int average = m_settings.connections_limit / m_torrents.size(); + + // the number of slots that are unused by torrents + int extra = m_settings.connections_limit % m_torrents.size(); + + // run 3 iterations of this, then we're probably close enough + for (int iter = 0; iter < 4; ++iter) + { + // the number of torrents that are above average + int num_above = 0; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int num = i->second->num_peers(); + if (num <= last_average) continue; + if (num > average) ++num_above; + if (num < average) extra += average - num; + } + + // distribute extra among the torrents that are above average + if (num_above == 0) num_above = 1; + last_average = average; + average += extra / num_above; + if (extra == 0) break; + // save the remainder for the next iteration + extra = extra % num_above; + } + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + int num = i->second->num_peers(); + if (num <= average) continue; + + // distribute the remainder + int my_average = average; + if (extra > 0) + { + ++my_average; + --extra; + } + + int disconnect = (std::min)(to_disconnect, num - my_average); + to_disconnect -= disconnect; + i->second->disconnect_peers(disconnect + , error_code(errors::too_many_connections, get_libtorrent_category())); + } + } + } + + void session_impl::set_alert_dispatch(boost::function)> const& fun) + { + m_alerts.set_dispatch_function(fun); + } + + std::auto_ptr session_impl::pop_alert() + { + return m_alerts.get(); + } + + void session_impl::pop_alerts(std::deque* alerts) + { + m_alerts.get_all(alerts); + } + + alert const* session_impl::wait_for_alert(time_duration max_wait) + { + return m_alerts.wait_for_alert(max_wait); + } + + void session_impl::set_alert_mask(boost::uint32_t m) + { + m_alerts.set_alert_mask(m); + } + +#ifndef TORRENT_NO_DEPRECATE + size_t session_impl::set_alert_queue_size_limit(size_t queue_size_limit_) + { + m_settings.alert_queue_size = queue_size_limit_; + return m_alerts.set_alert_queue_size_limit(queue_size_limit_); + } +#endif + + void session_impl::start_lsd() + { + INVARIANT_CHECK; + + if (m_lsd) return; + + m_lsd = new lsd(m_io_service + , m_listen_interface.address() + , boost::bind(&session_impl::on_lsd_peer, this, _1, _2)); + } + + natpmp* session_impl::start_natpmp() + { + INVARIANT_CHECK; + + if (m_natpmp) return m_natpmp.get(); + + // the natpmp constructor may fail and call the callbacks + // into the session_impl. + natpmp* n = new (std::nothrow) natpmp(m_io_service + , m_listen_interface.address() + , boost::bind(&session_impl::on_port_mapping + , this, _1, _2, _3, _4, 0) + , boost::bind(&session_impl::on_port_map_log + , this, _1, 0)); + if (n == 0) return 0; + + m_natpmp = n; + + if (m_listen_interface.port() > 0) + { + remap_tcp_ports(1, m_listen_interface.port(), ssl_listen_port()); + } + if (m_udp_socket.is_open()) + { + m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp + , m_listen_interface.port(), m_listen_interface.port()); + } + return n; + } + + upnp* session_impl::start_upnp() + { + INVARIANT_CHECK; + + if (m_upnp) return m_upnp.get(); + + // the upnp constructor may fail and call the callbacks + upnp* u = new (std::nothrow) upnp(m_io_service + , m_half_open + , m_listen_interface.address() + , m_settings.user_agent + , boost::bind(&session_impl::on_port_mapping + , this, _1, _2, _3, _4, 1) + , boost::bind(&session_impl::on_port_map_log + , this, _1, 1) + , m_settings.upnp_ignore_nonrouters); + + if (u == 0) return 0; + + m_upnp = u; + + m_upnp->discover_device(); + if (m_listen_interface.port() > 0 || ssl_listen_port() > 0) + { + remap_tcp_ports(2, m_listen_interface.port(), ssl_listen_port()); + } + if (m_udp_socket.is_open()) + { + m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp + , m_listen_interface.port(), m_listen_interface.port()); + } + return u; + } + + int session_impl::add_port_mapping(int t, int external_port + , int local_port) + { + int ret = 0; + if (m_upnp) ret = m_upnp->add_mapping((upnp::protocol_type)t, external_port + , local_port); + if (m_natpmp) ret = m_natpmp->add_mapping((natpmp::protocol_type)t, external_port + , local_port); + return ret; + } + + void session_impl::delete_port_mapping(int handle) + { + if (m_upnp) m_upnp->delete_mapping(handle); + if (m_natpmp) m_natpmp->delete_mapping(handle); + } + + void session_impl::stop_lsd() + { + if (m_lsd.get()) + m_lsd->close(); + m_lsd = 0; + } + + void session_impl::stop_natpmp() + { + if (m_natpmp.get()) + m_natpmp->close(); + m_natpmp = 0; + } + + void session_impl::stop_upnp() + { + if (m_upnp.get()) + { + m_upnp->close(); + m_udp_mapping[1] = -1; + m_tcp_mapping[1] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_mapping[1] = -1; +#endif + } + m_upnp = 0; + } + + external_ip const& session_impl::external_address() const + { return m_external_ip; } + + // this is the DHT observer version. DHT is the implied source + void session_impl::set_external_address(address const& ip + , address const& source) + { + set_external_address(ip, source_dht, source); + } + + void session_impl::set_external_address(address const& ip + , int source_type, address const& source) + { +#if defined TORRENT_VERBOSE_LOGGING + session_log(": set_external_address(%s, %d, %s)", print_address(ip).c_str() + , source_type, print_address(source).c_str()); +#endif + + if (!m_external_ip.cast_vote(ip, source_type, source)) return; + +#if defined TORRENT_VERBOSE_LOGGING + session_log(" external IP updated"); +#endif + + if (m_alerts.should_post()) + m_alerts.post_alert(external_ip_alert(ip)); + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + i->second->new_external_ip(); + } + + // since we have a new external IP now, we need to + // restart the DHT with a new node ID +#ifndef TORRENT_DISABLE_DHT + // TODO: 1 we only need to do this if our global IPv4 address has changed + // since the DHT (currently) only supports IPv4. Since restarting the DHT + // is kind of expensive, it would be nice to not do it unnecessarily + if (m_dht) + { + entry s = m_dht->state(); + int cur_state = 0; + int prev_state = 0; + entry* nodes1 = s.find_key("nodes"); + if (nodes1 && nodes1->type() == entry::list_t) cur_state = nodes1->list().size(); + entry* nodes2 = m_dht_state.find_key("nodes"); + if (nodes2 && nodes2->type() == entry::list_t) prev_state = nodes2->list().size(); + if (cur_state > prev_state) m_dht_state = s; + start_dht(m_dht_state); + } +#endif + } + + void session_impl::free_disk_buffer(char* buf) + { + m_disk_thread.free_buffer(buf); + } + + char* session_impl::allocate_disk_buffer(char const* category) + { + return m_disk_thread.allocate_buffer(category); + } + + char* session_impl::allocate_buffer() + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_DISK_STATS + TORRENT_ASSERT(m_buffer_allocations >= 0); + m_buffer_allocations++; + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + int num_bytes = send_buffer_size; + return (char*)malloc(num_bytes); +#else + return (char*)m_send_buffers.malloc(); +#endif + } + +#ifdef TORRENT_DISK_STATS + void session_impl::log_buffer_usage() + { + TORRENT_ASSERT(is_network_thread()); + + int send_buffer_capacity = 0; + int used_send_buffer = 0; + for (connection_map::const_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + send_buffer_capacity += (*i)->send_buffer_capacity(); + used_send_buffer += (*i)->send_buffer_size(); + } + TORRENT_ASSERT(send_buffer_capacity >= used_send_buffer); + m_buffer_usage_logger << log_time() << " send_buffer_size: " << send_buffer_capacity << std::endl; + m_buffer_usage_logger << log_time() << " used_send_buffer: " << used_send_buffer << std::endl; + m_buffer_usage_logger << log_time() << " send_buffer_utilization: " + << (used_send_buffer * 100.f / (std::max)(send_buffer_capacity, 1)) << std::endl; + } +#endif + + void session_impl::free_buffer(char* buf) + { + TORRENT_ASSERT(is_network_thread()); + +#ifdef TORRENT_DISK_STATS + m_buffer_allocations--; + TORRENT_ASSERT(m_buffer_allocations >= 0); + m_buffer_usage_logger << log_time() << " protocol_buffer: " + << (m_buffer_allocations * send_buffer_size) << std::endl; +#endif +#ifdef TORRENT_DISABLE_POOL_ALLOCATOR + free(buf); +#else + m_send_buffers.free(buf); +#endif + } + +#if TORRENT_USE_INVARIANT_CHECKS + void session_impl::check_invariant() const + { + TORRENT_ASSERT(is_network_thread()); + + if (m_settings.unchoke_slots_limit < 0 + && m_settings.choking_algorithm == session_settings::fixed_slots_choker) + TORRENT_ASSERT(m_allowed_upload_slots == (std::numeric_limits::max)()); + + int num_checking = 0; + int num_queued_for_checking = 0; + for (check_queue_t::const_iterator i = m_queued_for_checking.begin() + , end(m_queued_for_checking.end()); i != end; ++i) + { + if ((*i)->state() == torrent_status::checking_files) ++num_checking; + else if ((*i)->state() == torrent_status::queued_for_checking) + { + ++num_queued_for_checking; + } + } + + // the queue is either empty, or it has exactly one checking torrent in it + TORRENT_ASSERT(m_queued_for_checking.empty() || num_checking == 1 || (m_paused && num_checking == 0)); +// TORRENT_ASSERT(m_queued_for_checking.size() == num_queued_for_checking); + + std::set unique; + int num_active_downloading = 0; + int num_active_finished = 0; + int total_downloaders = 0; + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = i->second; + if (t->is_active_download()) ++num_active_downloading; + else if (t->is_active_finished()) ++num_active_finished; + + int pos = t->queue_position(); + if (pos < 0) + { + TORRENT_ASSERT(pos == -1); + continue; + } + ++total_downloaders; + + unique.insert(t->queue_position()); + } + TORRENT_ASSERT(int(unique.size()) == total_downloaders); + TORRENT_ASSERT(num_active_downloading == m_num_active_downloading); + TORRENT_ASSERT(num_active_finished == m_num_active_finished); + + std::set unique_peers; + TORRENT_ASSERT(m_settings.connections_limit > 0); + if (m_settings.choking_algorithm == session_settings::auto_expand_choker) + TORRENT_ASSERT(m_allowed_upload_slots >= m_settings.unchoke_slots_limit); + int unchokes = 0; + int num_optimistic = 0; + int disk_queue[2] = {0, 0}; + for (connection_map::const_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + TORRENT_ASSERT(*i); + boost::shared_ptr t = (*i)->associated_torrent().lock(); + TORRENT_ASSERT(unique_peers.find(i->get()) == unique_peers.end()); + unique_peers.insert(i->get()); + + if ((*i)->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0]; + if ((*i)->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1]; + + peer_connection* p = i->get(); + TORRENT_ASSERT(!p->is_disconnecting()); + if (p->ignore_unchoke_slots()) continue; + if (!p->is_choked()) ++unchokes; + if (p->peer_info_struct() + && p->peer_info_struct()->optimistically_unchoked) + { + ++num_optimistic; + TORRENT_ASSERT(!p->is_choked()); + } + if (t && p->peer_info_struct() && !p->peer_info_struct()->web_seed) + { + TORRENT_ASSERT(t->get_policy().has_connection(p)); + } + } + + TORRENT_ASSERT(disk_queue[0] == m_disk_queues[0]); + TORRENT_ASSERT(disk_queue[1] == m_disk_queues[1]); + + if (m_settings.num_optimistic_unchoke_slots) + { + TORRENT_ASSERT(num_optimistic <= m_settings.num_optimistic_unchoke_slots); + } + + if (m_num_unchoked != unchokes) + { + TORRENT_ASSERT(false); + } + for (torrent_map::const_iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + TORRENT_ASSERT(boost::get_pointer(j->second)); + } + } +#endif + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + tracker_logger::tracker_logger(session_impl& ses): m_ses(ses) {} + void tracker_logger::tracker_warning(tracker_request const& req + , std::string const& str) + { + debug_log("*** tracker warning: %s", str.c_str()); + } + + void tracker_logger::tracker_response(tracker_request const& + , libtorrent::address const& tracker_ip + , std::list
const& ip_list + , std::vector& peers + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , std::string const& tracker_id) + { + std::string s; + s = "TRACKER RESPONSE:\n"; + char tmp[200]; + snprintf(tmp, 200, "interval: %d\nmin_interval: %d\npeers:\n", interval, min_interval); + s += tmp; + for (std::vector::const_iterator i = peers.begin(); + i != peers.end(); ++i) + { + char pid[41]; + to_hex((const char*)&i->pid[0], 20, pid); + if (i->pid.is_all_zeros()) pid[0] = 0; + + snprintf(tmp, 200, " %-16s %-5d %s\n", i->ip.c_str(), i->port, pid); + s += tmp; + } + snprintf(tmp, 200, "external ip: %s\n", print_address(external_ip).c_str()); + s += tmp; + debug_log("%s", s.c_str()); + } + + void tracker_logger::tracker_request_timed_out( + tracker_request const&) + { + debug_log("*** tracker timed out"); + } + + void tracker_logger::tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, const std::string& str + , int retry_interval) + { + debug_log("*** tracker error: %d: %s %s" + , response_code, ec.message().c_str(), str.c_str()); + } + + void tracker_logger::debug_log(const char* fmt, ...) const + { + if (!m_ses.m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[1024]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[1280]; + snprintf(buf, sizeof(buf), "%s: %s\n", time_now_string(), usr); + (*m_ses.m_logger) << buf; + } +#endif +}} + diff --git a/apps/Launcher/ext/libtorrent/src/settings.cpp b/apps/Launcher/ext/libtorrent/src/settings.cpp new file mode 100644 index 0000000000..7616afc459 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/settings.cpp @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/settings.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/assert.hpp" + +#include + +namespace libtorrent +{ + + void load_struct(lazy_entry const& e, void* s, bencode_map_entry const* m, int num) + { + for (int i = 0; i < num; ++i) + { + lazy_entry const* key = e.dict_find(m[i].name); + if (key == 0) continue; + void* dest = ((char*)s) + m[i].offset; + switch (m[i].type) + { + case std_string: + { + if (key->type() != lazy_entry::string_t) continue; + *((std::string*)dest) = key->string_value(); + break; + } + case character: + case integer16: + case boolean: + case integer: + case size_integer: + case time_integer: + case floating_point: + { + if (key->type() != lazy_entry::int_t) continue; + size_type val = key->int_value(); + switch (m[i].type) + { + case character: *((char*)dest) = char(val); break; + case integer16: *((boost::uint16_t*)dest) = boost::uint16_t(val); break; + case integer: *((int*)dest) = int(val); break; + case size_integer: *((size_type*)dest) = size_type(val); break; + case time_integer: *((time_t*)dest) = time_t(val); break; + case floating_point: *((float*)dest) = float(val) / 1000.f; break; + case boolean: *((bool*)dest) = (val != 0); break; + } + } + } + } + } + + void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num, void const* def) + { + if (e.type() != entry::dictionary_t) e = entry(entry::dictionary_t); + for (int i = 0; i < num; ++i) + { + char const* key = m[i].name; + void const* src = ((char*)s) + m[i].offset; + if (def) + { + // if we have a default value for this field + // and it is the default, don't save it + void const* default_value = ((char*)def) + m[i].offset; + switch (m[i].type) + { + case std_string: + if (*((std::string*)src) == *((std::string*)default_value)) continue; + break; + case character: + if (*((char*)src) == *((char*)default_value)) continue; + break; + case integer: + if (*((int*)src) == *((int*)default_value)) continue; + break; + case integer16: + if (*((boost::uint16_t*)src) == *((boost::uint16_t*)default_value)) continue; + break; + case size_integer: + if (*((size_type*)src) == *((size_type*)default_value)) continue; + break; + case time_integer: + if (*((time_t*)src) == *((time_t*)default_value)) continue; + break; + case floating_point: + if (*((float*)src) == *((float*)default_value)) continue; + break; + case boolean: + if (*((bool*)src) == *((bool*)default_value)) continue; + break; + default: TORRENT_ASSERT(false); + } + } + entry& val = e[key]; + TORRENT_ASSERT_VAL(val.type() == entry::undefined_t, val.type()); + switch (m[i].type) + { + case std_string: val = *((std::string*)src); break; + case character: val = *((char*)src); break; + case integer: val = *((int*)src); break; + case integer16: val = *((boost::uint16_t*)src); break; + case size_integer: val = *((size_type*)src); break; + case time_integer: val = *((time_t*)src); break; + case floating_point: val = size_type(*((float*)src) * 1000.f); break; + case boolean: val = *((bool*)src); break; + default: TORRENT_ASSERT(false); + } + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/sha1.cpp b/apps/Launcher/ext/libtorrent/src/sha1.cpp new file mode 100644 index 0000000000..a3e64dfbb0 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/sha1.cpp @@ -0,0 +1,330 @@ +/* +SHA-1 C++ conversion + +original version: + +SHA-1 in C +By Steve Reid +100% Public Domain + +changelog at the end of the file. +*/ + +#include +#include + +// if you don't want boost +// replace with +// #include +// typedef uint32_t u32; +// typedef uint8_t u8; + +#include +typedef boost::uint32_t u32; +typedef boost::uint8_t u8; + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + +struct TORRENT_EXPORT sha_ctx +{ + u32 state[5]; + u32 count[2]; + u8 buffer[64]; +}; + +// we don't want these to clash with openssl's libcrypto +TORRENT_EXPORT void SHA1_init(sha_ctx* context); +TORRENT_EXPORT void SHA1_update(sha_ctx* context, u8 const* data, u32 len); +TORRENT_EXPORT void SHA1_final(u8* digest, sha_ctx* context); + +namespace +{ + union CHAR64LONG16 + { + u8 c[64]; + u32 l[16]; + }; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +// blk0() and blk() perform the initial expand. +// I got the idea of expanding during the round function from SSLeay + struct little_endian_blk0 + { + static u32 apply(CHAR64LONG16* block, int i) + { + return block->l[i] = (rol(block->l[i],24)&0xFF00FF00) + | (rol(block->l[i],8)&0x00FF00FF); + } + }; + + struct big_endian_blk0 + { + static u32 apply(CHAR64LONG16* block, int i) + { + return block->l[i]; + } + }; + + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +// (R0+R1), R2, R3, R4 are the different operations used in SHA1 +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + // Hash a single 512-bit block. This is the core of the algorithm. + template + void SHA1transform(u32 state[5], u8 const buffer[64]) + { + using namespace std; + u32 a, b, c, d, e; + + CHAR64LONG16* block; + u8 workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + + // Copy context->state[] to working vars + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + // 4 rounds of 20 operations each. Loop unrolled. + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + // Add the working vars back into context.state[] + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + +#ifdef VERBOSE + void SHAPrintContext(sha_ctx *context, char *msg) + { + using namespace std; + printf("%s (%d,%d) %x %x %x %x %x\n" + , msg, (unsigned int)context->count[0] + , (unsigned int)context->count[1] + , (unsigned int)context->state[0] + , (unsigned int)context->state[1] + , (unsigned int)context->state[2] + , (unsigned int)context->state[3] + , (unsigned int)context->state[4]); + } +#endif + + template + void internal_update(sha_ctx* context, u8 const* data, u32 len) + { + using namespace std; + u32 i, j; // JHB + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + { + SHA1transform(context->state, &data[i]); + } + j = 0; + } + else + { + i = 0; + } + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif + } + +#if !defined BOOST_BIG_ENDIAN && !defined BOOST_LITTLE_ENDIAN + bool is_big_endian() + { + u32 test = 1; + return *reinterpret_cast(&test) == 0; + } +#endif +} + +// SHA1Init - Initialize new context + +void SHA1_init(sha_ctx* context) +{ + // SHA1 initialization constants + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +// Run your data through this. + +void SHA1_update(sha_ctx* context, u8 const* data, u32 len) +{ + // GCC standard defines for endianness + // test with: cpp -dM /dev/null +#if defined BOOST_BIG_ENDIAN + internal_update(context, data, len); +#elif defined BOOST_LITTLE_ENDIAN + internal_update(context, data, len); +#else + // select different functions depending on endianess + // and figure out the endianess runtime + if (is_big_endian()) + internal_update(context, data, len); + else + internal_update(context, data, len); +#endif +} + + +// Add padding and return the message digest. + +void SHA1_final(u8* digest, sha_ctx* context) +{ + u8 finalcount[8]; + + for (u32 i = 0; i < 8; ++i) + { + // Endian independent + finalcount[i] = static_cast( + (context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); + } + + SHA1_update(context, (u8 const*)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1_update(context, (u8 const*)"\0", 1); + SHA1_update(context, finalcount, 8); // Should cause a SHA1transform() + + for (u32 i = 0; i < 20; ++i) + { + digest[i] = static_cast( + (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + +} // libtorrent namespace + +/************************************************************ + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Converted to C++ 6/04 +By Arvid Norberg +1- made the input buffer const, and made the + previous SHA1HANDSOFF implicit +2- uses C99 types with size guarantees + from boost +3- if none of BOOST_BIG_ENDIAN or BOOST_LITTLE_ENDIAN + are defined, endianess is determined + at runtime. templates are used to duplicate + the transform function for each endianess +4- using anonymous namespace to avoid external + linkage on internal functions +5- using standard C++ includes +6- made API compatible with openssl + +still 100% PD +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ diff --git a/apps/Launcher/ext/libtorrent/src/smart_ban.cpp b/apps/Launcher/ext/libtorrent/src/smart_ban.cpp new file mode 100644 index 0000000000..6b47569cfd --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/smart_ban.cpp @@ -0,0 +1,389 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include +#include + +#include "libtorrent/hasher.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/smart_ban.hpp" +#include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" + +//#define TORRENT_LOG_HASH_FAILURES + +#ifdef TORRENT_LOG_HASH_FAILURES + +#include "libtorrent/peer_id.hpp" // sha1_hash +#include "libtorrent/escape_string.hpp" // to_hex + +void log_hash_block(FILE** f, libtorrent::torrent const& t, int piece, int block + , libtorrent::address a, char const* bytes, int len, bool corrupt) +{ + using namespace libtorrent; + + mkdir("hash_failures", 0755); + + if (*f == NULL) + { + char filename[1024]; + snprintf(filename, sizeof(filename), "hash_failures/%s.log" + , to_hex(t.info_hash().to_string()).c_str()); + *f = fopen(filename, "w"); + } + + file_storage const& fs = t.torrent_file().files(); + std::vector files = fs.map_block(piece, block * 0x4000, len); + + std::string fn = fs.file_path(fs.internal_at(files[0].file_index)); + + char filename[4094]; + int offset = 0; + for (int i = 0; i < files.size(); ++i) + { + offset += snprintf(filename+offset, sizeof(filename)-offset + , "%s[%"PRId64",%d]", libtorrent::filename(fn).c_str(), files[i].offset, int(files[i].size)); + if (offset >= sizeof(filename)) break; + } + + fprintf(*f, "%s\t%04d\t%04d\t%s\t%s\t%s\n", to_hex(t.info_hash().to_string()).c_str(), piece + , block, corrupt ? " bad" : "good", print_address(a).c_str(), filename); + + snprintf(filename, sizeof(filename), "hash_failures/%s_%d_%d_%s.block" + , to_hex(t.info_hash().to_string()).c_str(), piece, block, corrupt ? "bad" : "good"); + FILE* data = fopen(filename, "w+"); + fwrite(bytes, 1, len, data); + fclose(data); +} + +#endif + + +namespace libtorrent { + + class torrent; + +namespace +{ + + struct smart_ban_plugin : torrent_plugin, boost::enable_shared_from_this + { + smart_ban_plugin(torrent& t) + : m_torrent(t) + , m_salt(random()) + { +#ifdef TORRENT_LOG_HASH_FAILURES + m_log_file = NULL; +#endif + } + +#ifdef TORRENT_LOG_HASH_FAILURES + ~smart_ban_plugin() + { fclose(m_log_file); } +#endif + + void on_piece_pass(int p) + { +#ifdef TORRENT_LOGGING + (*m_torrent.session().m_logger) << time_now_string() << " PIECE PASS [ p: " << p + << " | block_hash_size: " << m_block_hashes.size() << " ]\n"; +#endif + // has this piece failed earlier? If it has, go through the + // CRCs from the time it failed and ban the peers that + // sent bad blocks + std::map::iterator i = m_block_hashes.lower_bound(piece_block(p, 0)); + if (i == m_block_hashes.end() || int(i->first.piece_index) != p) return; + + int size = m_torrent.torrent_file().piece_size(p); + peer_request r = {p, 0, (std::min)(16*1024, size)}; + piece_block pb(p, 0); + while (size > 0) + { + if (i->first.block_index == pb.block_index) + { + m_torrent.filesystem().async_read(r, boost::bind(&smart_ban_plugin::on_read_ok_block + , shared_from_this(), *i, _1, _2)); + m_block_hashes.erase(i++); + } + else + { + TORRENT_ASSERT(i->first.block_index > pb.block_index); + } + + if (i == m_block_hashes.end() || int(i->first.piece_index) != p) + break; + + r.start += 16*1024; + size -= 16*1024; + r.length = (std::min)(16*1024, size); + ++pb.block_index; + } + +#ifndef NDEBUG + // make sure we actually removed all the entries for piece 'p' + i = m_block_hashes.lower_bound(piece_block(p, 0)); + TORRENT_ASSERT(i == m_block_hashes.end() || int(i->first.piece_index) != p); +#endif + + if (m_torrent.is_seed()) + { + std::map().swap(m_block_hashes); + return; + } + } + + void on_piece_failed(int p) + { + // The piece failed the hash check. Record + // the CRC and origin peer of every block + + // if the torrent is aborted, no point in starting + // a bunch of read operations on it + if (m_torrent.is_aborted()) return; + + std::vector downloaders; + m_torrent.picker().get_downloaders(downloaders, p); + + int size = m_torrent.torrent_file().piece_size(p); + peer_request r = {p, 0, (std::min)(16*1024, size)}; + piece_block pb(p, 0); + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + if (*i != 0) + { + m_torrent.filesystem().async_read(r, boost::bind(&smart_ban_plugin::on_read_failed_block + , shared_from_this(), pb, ((policy::peer*)*i)->address(), _1, _2)); + } + + r.start += 16*1024; + size -= 16*1024; + r.length = (std::min)(16*1024, size); + ++pb.block_index; + } + TORRENT_ASSERT(size <= 0); + } + + private: + + // this entry ties a specific block CRC to + // a peer. + struct block_entry + { + policy::peer* peer; + sha1_hash digest; + }; + + void on_read_failed_block(piece_block b, address a, int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_torrent.session().is_network_thread()); + + disk_buffer_holder buffer(m_torrent.session(), j.buffer); + + // ignore read errors + if (ret != j.buffer_size) return; + + hasher h; + h.update(j.buffer, j.buffer_size); + h.update((char const*)&m_salt, sizeof(m_salt)); + + std::pair range + = m_torrent.get_policy().find_peers(a); + + // there is no peer with this address anymore + if (range.first == range.second) return; + + policy::peer* p = (*range.first); + block_entry e = {p, h.final()}; + +#ifdef TORRENT_LOG_HASH_FAILURES + log_hash_block(&m_log_file, m_torrent, b.piece_index + , b.block_index, p->address(), j.buffer, j.buffer_size, true); +#endif + + std::map::iterator i = m_block_hashes.lower_bound(b); + + if (i != m_block_hashes.end() && i->first == b && i->second.peer == p) + { + // this peer has sent us this block before + if (i->second.digest != e.digest) + { + // this time the digest of the block is different + // from the first time it sent it + // at least one of them must be bad + + // verify that this is not a dangling pointer + // if the pointer is in the policy's list, it + // still live, if it's not, it has been removed + // and we can't use this pointer + if (!m_torrent.get_policy().has_peer(p)) return; + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.piece_index + << " | b: " << b.block_index + << " | c: " << client + << " | hash1: " << i->second.digest + << " | hash2: " << e.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + m_torrent.get_policy().ban_peer(p); + if (p->connection) p->connection->disconnect( + errors::peer_banned); + } + // we already have this exact entry in the map + // we don't have to insert it + return; + } + + m_block_hashes.insert(i, std::pair(b, e)); + +#ifdef TORRENT_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " STORE BLOCK CRC [ p: " << b.piece_index + << " | b: " << b.block_index + << " | c: " << client + << " | digest: " << e.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + } + + void on_read_ok_block(std::pair b, int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_torrent.session().is_network_thread()); + + disk_buffer_holder buffer(m_torrent.session(), j.buffer); + + // ignore read errors + if (ret != j.buffer_size) return; + + hasher h; + h.update(j.buffer, j.buffer_size); + h.update((char const*)&m_salt, sizeof(m_salt)); + sha1_hash ok_digest = h.final(); + + policy::peer* p = b.second.peer; + + if (b.second.digest == ok_digest) return; + if (p == 0) return; + +#ifdef TORRENT_LOG_HASH_FAILURES + log_hash_block(&m_log_file, m_torrent, b.first.piece_index + , b.first.block_index, p->address(), j.buffer, j.buffer_size, false); +#endif + + if (!m_torrent.get_policy().has_peer(p)) return; + +#ifdef TORRENT_LOGGING + char const* client = "-"; + peer_info info; + if (p->connection) + { + p->connection->get_peer_info(info); + client = info.client.c_str(); + } + (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.first.piece_index + << " | b: " << b.first.block_index + << " | c: " << client + << " | ok_digest: " << ok_digest + << " | bad_digest: " << b.second.digest + << " | ip: " << p->ip() << " ]\n"; +#endif + m_torrent.get_policy().ban_peer(p); + if (p->connection) p->connection->disconnect( + errors::peer_banned); + } + + torrent& m_torrent; + + // This table maps a piece_block (piece and block index + // pair) to a peer and the block CRC. The CRC is calculated + // from the data in the block + the salt + std::map m_block_hashes; + + // This salt is a random value used to calculate the block CRCs + // Since the CRC function that is used is not a one way function + // the salt is required to avoid attacks where bad data is sent + // that is forged to match the CRC of the good data. + int m_salt; + +#ifdef TORRENT_LOG_HASH_FAILURES + FILE* m_log_file; +#endif + }; + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_smart_ban_plugin(torrent* t, void*) + { + return boost::shared_ptr(new smart_ban_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/socket_io.cpp b/apps/Launcher/ext/libtorrent/src/socket_io.cpp new file mode 100644 index 0000000000..d17cded5bc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socket_io.cpp @@ -0,0 +1,104 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#include "libtorrent/escape_string.hpp" +#include "libtorrent/error_code.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/io.hpp" // for write_uint16 +#include "libtorrent/hasher.hpp" // for hasher + +namespace libtorrent +{ + + std::string print_address(address const& addr) + { + error_code ec; + return addr.to_string(ec); + } + + std::string address_to_bytes(address const& a) + { + std::string ret; + std::back_insert_iterator out(ret); + detail::write_address(a, out); + return ret; + } + + std::string endpoint_to_bytes(udp::endpoint const& ep) + { + std::string ret; + std::back_insert_iterator out(ret); + detail::write_endpoint(ep, out); + return ret; + } + + std::string print_endpoint(tcp::endpoint const& ep) + { + error_code ec; + char buf[200]; + address const& addr = ep.address(); +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + snprintf(buf, sizeof(buf), "[%s]:%d", addr.to_string(ec).c_str(), ep.port()); + else +#endif + snprintf(buf, sizeof(buf), "%s:%d", addr.to_string(ec).c_str(), ep.port()); + return buf; + } + + std::string print_endpoint(udp::endpoint const& ep) + { + return print_endpoint(tcp::endpoint(ep.address(), ep.port())); + } + + void hash_address(address const& ip, sha1_hash& h) + { +#if TORRENT_USE_IPV6 + if (ip.is_v6()) + { + address_v6::bytes_type b = ip.to_v6().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + else +#endif + { + address_v4::bytes_type b = ip.to_v4().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/socket_type.cpp b/apps/Launcher/ext/libtorrent/src/socket_type.cpp new file mode 100644 index 0000000000..dab172a9c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socket_type.cpp @@ -0,0 +1,336 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket_type.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include + +#if BOOST_VERSION >= 104700 +#include +#endif + +#endif + +namespace libtorrent +{ + + bool is_ssl(socket_type const& s) + { +#ifdef TORRENT_USE_OPENSSL +#define CASE(t) case socket_type_int_impl >::value: + switch (s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + return true; + default: return false; + }; +#undef CASE +#else + return false; +#endif + } + + bool is_utp(socket_type const& s) + { + return s.get() +#ifdef TORRENT_USE_OPENSSL + || s.get >() +#endif + ; + } + +#if TORRENT_USE_I2P + bool is_i2p(socket_type const& s) + { + return s.get() +#ifdef TORRENT_USE_OPENSSL + || s.get >() +#endif + ; + } +#endif + + void setup_ssl_hostname(socket_type& s, std::string const& hostname, error_code& ec) + { +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 + // for SSL connections, make sure to authenticate the hostname + // of the certificate +#define CASE(t) case socket_type_int_impl >::value: \ + s.get >()->set_verify_callback(asio::ssl::rfc2818_verification(hostname), ec); \ + ctx = SSL_get_SSL_CTX(s.get >()->native_handle()); \ + break; + + SSL_CTX* ctx = 0; + + switch(s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + } +#undef CASE + +#if OPENSSL_VERSION_NUMBER >= 0x90812f + if (ctx) + { + SSL_CTX_set_tlsext_servername_callback(ctx, 0); + SSL_CTX_set_tlsext_servername_arg(ctx, 0); + } +#endif // OPENSSL_VERSION_NUMBER + +#endif + } + + void on_close_socket(socket_type* s, boost::shared_ptr holder) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("on_close_socket"); +#endif + error_code ec; + s->close(ec); + } + + // the second argument is a shared pointer to an object that + // will keep the socket (s) alive for the duration of the async operation + void async_shutdown(socket_type& s, boost::shared_ptr holder) + { + error_code e; + +#ifdef TORRENT_USE_OPENSSL + // for SSL connections, first do an async_shutdown, before closing the socket +#if defined TORRENT_ASIO_DEBUGGING +#define MAYBE_ASIO_DEBUGGING add_outstanding_async("on_close_socket"); +#else +#define MAYBE_ASIO_DEBUGGING +#endif +#define CASE(t) case socket_type_int_impl >::value: \ + MAYBE_ASIO_DEBUGGING \ + s.get >()->async_shutdown(boost::bind(&on_close_socket, &s, holder)); \ + break; + + switch (s.type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + default: s.close(e); break; + } +#undef CASE +#else + s.close(e); +#endif // TORRENT_USE_OPENSSL + } + + void socket_type::destruct() + { + switch (m_type) + { + case 0: break; + case socket_type_int_impl::value: + get()->~stream_socket(); + break; + case socket_type_int_impl::value: + get()->~socks5_stream(); + break; + case socket_type_int_impl::value: + get()->~http_stream(); + break; + case socket_type_int_impl::value: + get()->~utp_stream(); + break; +#if TORRENT_USE_I2P + case socket_type_int_impl::value: + get()->~i2p_stream(); + break; +#endif +#ifdef TORRENT_USE_OPENSSL + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; + case socket_type_int_impl >::value: + get >()->~ssl_stream(); + break; +#endif + default: TORRENT_ASSERT(false); + } + m_type = 0; + } + + void socket_type::construct(int type, void* userdata) + { + destruct(); + switch (type) + { + case 0: break; + case socket_type_int_impl::value: + new ((stream_socket*)m_data) stream_socket(m_io_service); + break; + case socket_type_int_impl::value: + new ((socks5_stream*)m_data) socks5_stream(m_io_service); + break; + case socket_type_int_impl::value: + new ((http_stream*)m_data) http_stream(m_io_service); + break; + case socket_type_int_impl::value: + new ((utp_stream*)m_data) utp_stream(m_io_service); + break; +#if TORRENT_USE_I2P + case socket_type_int_impl::value: + new ((i2p_stream*)m_data) i2p_stream(m_io_service); + break; +#endif +#ifdef TORRENT_USE_OPENSSL + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; + case socket_type_int_impl >::value: + TORRENT_ASSERT(userdata); + new ((ssl_stream*)m_data) ssl_stream(m_io_service + , *((boost::asio::ssl::context*)userdata)); + break; +#endif + default: TORRENT_ASSERT(false); + } + + m_type = type; + } + + char const* socket_type::type_name() const + { + static char const* const names[] = + { + "uninitialized", + "TCP", + "Socks5", + "HTTP", + "uTP", +#if TORRENT_USE_I2P + "I2P", +#else + "", +#endif +#ifdef TORRENT_USE_OPENSSL + "SSL/TCP", + "SSL/Socks5", + "SSL/HTTP", + "SSL/uTP" +#else + "","","","" +#endif + }; + return names[m_type]; + } + + io_service& socket_type::get_io_service() const + { return m_io_service; } + + socket_type::~socket_type() + { destruct(); } + + bool socket_type::is_open() const + { + if (m_type == 0) return false; + TORRENT_SOCKTYPE_FORWARD_RET(is_open(), false) + } + + void socket_type::open(protocol_type const& p, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(open(p, ec)) } + + void socket_type::close(error_code& ec) + { + if (m_type == 0) return; + TORRENT_SOCKTYPE_FORWARD(close(ec)) + } + + socket_type::endpoint_type socket_type::local_endpoint(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(ec), socket_type::endpoint_type()) } + + socket_type::endpoint_type socket_type::remote_endpoint(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(ec), socket_type::endpoint_type()) } + + void socket_type::bind(endpoint_type const& endpoint, error_code& ec) + { TORRENT_SOCKTYPE_FORWARD(bind(endpoint, ec)) } + + std::size_t socket_type::available(error_code& ec) const + { TORRENT_SOCKTYPE_FORWARD_RET(available(ec), 0) } + + int socket_type::type() const { return m_type; } + +#ifndef BOOST_NO_EXCEPTIONS + void socket_type::open(protocol_type const& p) + { TORRENT_SOCKTYPE_FORWARD(open(p)) } + + void socket_type::close() + { + if (m_type == 0) return; + TORRENT_SOCKTYPE_FORWARD(close()) + } + + socket_type::endpoint_type socket_type::local_endpoint() const + { TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(), socket_type::endpoint_type()) } + + socket_type::endpoint_type socket_type::remote_endpoint() const + { TORRENT_SOCKTYPE_FORWARD_RET(remote_endpoint(), socket_type::endpoint_type()) } + + void socket_type::bind(endpoint_type const& endpoint) + { TORRENT_SOCKTYPE_FORWARD(bind(endpoint)) } + + std::size_t socket_type::available() const + { TORRENT_SOCKTYPE_FORWARD_RET(available(), 0) } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp b/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp new file mode 100644 index 0000000000..00b3500775 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/socks5_stream.cpp @@ -0,0 +1,600 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/socket_io.hpp" + +namespace libtorrent +{ + + namespace socks_error + { + boost::system::error_code make_error_code(socks_error_code e) + { + return error_code(e, get_socks_category()); + } + } + + struct socks_error_category : boost::system::error_category + { + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { return "socks error"; } + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + static char const* messages[] = + { + "SOCKS no error", + "SOCKS unsupported version", + "SOCKS unsupported authentication method", + "SOCKS unsupported authentication version", + "SOCKS authentication error", + "SOCKS username required", + "SOCKS general failure", + "SOCKS command not supported", + "SOCKS no identd running", + "SOCKS identd could not identify username" + }; + + if (ev < 0 || ev >= socks_error::num_errors) return "unknown error"; + return messages[ev]; + } + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { return boost::system::error_condition(ev, *this); } + }; + + TORRENT_EXPORT boost::system::error_category& get_socks_category() + { + static socks_error_category socks_category; + return socks_category; + } + + void socks5_stream::name_lookup(error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::name_lookup"); +#endif + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + error_code ec; + if (!m_sock.is_open()) + { + m_sock.open(i->endpoint().protocol(), ec); + if (ec) + { + (*h)(ec); + close(ec); + return; + } + } + + // TOOD: we could bind the socket here, since we know what the + // target endpoint is of the proxy +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connected"); +#endif + m_sock.async_connect(i->endpoint(), boost::bind( + &socks5_stream::connected, this, _1, h)); + } + + void socks5_stream::connected(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connected"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + if (m_version == 5) + { + // send SOCKS5 authentication methods + m_buffer.resize(m_user.empty()?3:4); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_user.empty()) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake1"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + else if (m_version == 4) + { + socks_connect(h); + } + else + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + } + } + + void socks5_stream::handshake1(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake1"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake2"); +#endif + m_buffer.resize(2); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake2, this, _1, h)); + } + + void socks5_stream::handshake2(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake2"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < m_version) + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + + if (method == 0) + { + socks_connect(h); + } + else if (method == 2) + { + if (m_user.empty()) + { + (*h)(socks_error::username_required); + error_code ec; + close(ec); + return; + } + + // start sub-negotiation + m_buffer.resize(m_user.size() + m_password.size() + 3); + char* p = &m_buffer[0]; + write_uint8(1, p); + write_uint8(m_user.size(), p); + write_string(m_user, p); + write_uint8(m_password.size(), p); + write_string(m_password, p); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake3"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake3, this, _1, h)); + } + else + { + (*h)(socks_error::unsupported_authentication_method); + error_code ec; + close(ec); + return; + } + } + + void socks5_stream::handshake3(error_code const& e + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake3"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::handshake4"); +#endif + m_buffer.resize(2); + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake4, this, _1, h)); + } + + void socks5_stream::handshake4(error_code const& e + , boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::handshake4"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1) + { + (*h)(socks_error::unsupported_authentication_version); + error_code ec; + close(ec); + return; + } + + if (status != 0) + { + (*h)(socks_error::authentication_error); + error_code ec; + close(ec); + return; + } + + std::vector().swap(m_buffer); + socks_connect(h); + } + + void socks5_stream::socks_connect(boost::shared_ptr h) + { + using namespace libtorrent::detail; + + if (m_version == 5) + { + // send SOCKS5 connect command + m_buffer.resize(6 + (!m_dst_name.empty() + ?m_dst_name.size() + 1 + :(m_remote_endpoint.address().is_v4()?4:16))); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(m_command, p); // CONNECT/BIND command + write_uint8(0, p); // reserved + if (!m_dst_name.empty()) + { + write_uint8(3, p); // address type + TORRENT_ASSERT(m_dst_name.size() <= 255); + write_uint8(m_dst_name.size(), p); + std::copy(m_dst_name.begin(), m_dst_name.end(), p); + p += m_dst_name.size(); + } + else + { + write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type + write_address(m_remote_endpoint.address(), p); + } + write_uint16(m_remote_endpoint.port(), p); + } + else if (m_version == 4) + { + // SOCKS4 only supports IPv4 + if (!m_remote_endpoint.address().is_v4()) + { + (*h)(boost::asio::error::address_family_not_supported); + error_code ec; + close(ec); + return; + } + m_buffer.resize(m_user.size() + 9); + char* p = &m_buffer[0]; + write_uint8(4, p); // SOCKS VERSION 4 + write_uint8(m_command, p); // CONNECT/BIND command + write_uint16(m_remote_endpoint.port(), p); + write_uint32(m_remote_endpoint.address().to_v4().to_ulong(), p); + std::copy(m_user.begin(), m_user.end(), p); + p += m_user.size(); + write_uint8(0, p); // NULL terminator + } + else + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect1, this, _1, h)); + } + + void socks5_stream::connect1(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect1"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + if (m_version == 5) + m_buffer.resize(6 + 4); // assume an IPv4 address + else if (m_version == 4) + m_buffer.resize(8); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect2"); +#endif + async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect2, this, _1, h)); + } + + void socks5_stream::connect2(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect2"); +#endif + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 connect command + char* p = &m_buffer[0]; + int version = read_uint8(p); + int response = read_uint8(p); + + if (m_version == 5) + { + if (version < m_version) + { + (*h)(socks_error::unsupported_version); + error_code ec; + close(ec); + return; + } + if (response != 0) + { + error_code ec(socks_error::general_failure, get_socks_category()); + switch (response) + { + case 2: ec = asio::error::no_permission; break; + case 3: ec = asio::error::network_unreachable; break; + case 4: ec = asio::error::host_unreachable; break; + case 5: ec = asio::error::connection_refused; break; + case 6: ec = asio::error::timed_out; break; + case 7: ec = socks_error::command_not_supported; break; + case 8: ec = asio::error::address_family_not_supported; break; + } + (*h)(ec); + close(ec); + return; + } + p += 1; // reserved + int atyp = read_uint8(p); + // we ignore the proxy IP it was bound to + if (atyp == 1) + { + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + std::vector().swap(m_buffer); + (*h)(e); + } + else + { + std::vector().swap(m_buffer); + (*h)(e); + } + return; + } + int extra_bytes = 0; + if (atyp == 4) + { + extra_bytes = 12; + } + else if (atyp == 3) + { + extra_bytes = read_uint8(p) - 3; + } + else + { + (*h)(asio::error::address_family_not_supported); + error_code ec; + close(ec); + return; + } + m_buffer.resize(m_buffer.size() + extra_bytes); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect3"); +#endif + TORRENT_ASSERT(extra_bytes > 0); + async_read(m_sock, asio::buffer(&m_buffer[m_buffer.size() - extra_bytes], extra_bytes) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + else if (m_version == 4) + { + if (version != 0) + { + (*h)(socks_error::general_failure); + error_code ec; + close(ec); + return; + } + + // access granted + if (response == 90) + { + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + std::vector().swap(m_buffer); + (*h)(e); + } + else + { + std::vector().swap(m_buffer); + (*h)(e); + } + return; + } + + int code = socks_error::general_failure; + switch (response) + { + case 91: code = socks_error::authentication_error; break; + case 92: code = socks_error::no_identd; break; + case 93: code = socks_error::identd_error; break; + } + error_code ec(code, get_socks_category()); + (*h)(ec); + close(ec); + } + } + + void socks5_stream::connect3(error_code const& e, boost::shared_ptr h) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("socks5_stream::connect3"); +#endif + using namespace libtorrent::detail; + + if (e) + { + (*h)(e); + error_code ec; + close(ec); + return; + } + + if (m_command == 2) + { + if (m_listen == 0) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("socks5_stream::connect1"); +#endif + m_listen = 1; + connect1(e, h); + return; + } + + char* p = &m_buffer[0]; + p += 2; // version and response code + int atyp = read_uint8(p); + TORRENT_ASSERT(atyp == 3 || atyp == 4); + if (atyp == 4) + { + // we don't support resolving the endpoint address + // if we receive a domain name, just set the remote + // endpoint to INADDR_ANY + m_remote_endpoint = tcp::endpoint(); + } + else if (atyp == 3) + { + m_remote_endpoint.address(read_v4_address(p)); + m_remote_endpoint.port(read_uint16(p)); + } + } + std::vector().swap(m_buffer); + (*h)(e); + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/stat.cpp b/apps/Launcher/ext/libtorrent/src/stat.cpp new file mode 100644 index 0000000000..353908928a --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/stat.cpp @@ -0,0 +1,50 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#include "libtorrent/stat.hpp" + +namespace libtorrent { + +void stat_channel::second_tick(int tick_interval_ms) +{ + int sample = int(size_type(m_counter) * 1000 / tick_interval_ms); + TORRENT_ASSERT(sample >= 0); + m_5_sec_average = size_type(m_5_sec_average) * 4 / 5 + sample / 5; + m_30_sec_average = size_type(m_30_sec_average) * 29 / 30 + sample / 30; + m_counter = 0; +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/storage.cpp b/apps/Launcher/ext/libtorrent/src/storage.cpp new file mode 100644 index 0000000000..574a6527bb --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/storage.cpp @@ -0,0 +1,3139 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#if BOOST_VERSION >= 103500 +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/disk_buffer_holder.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/allocator.hpp" // page_size + +#include + +//#define TORRENT_PARTIAL_HASH_LOG + +#if defined(__APPLE__) +// for getattrlist() +#include +#include +// for statfs() +#include +#include +#endif + +#if defined(__linux__) +#include +#endif + +#if defined(__FreeBSD__) +// for statfs() +#include +#include +#endif + +// for convert_to_wstring and convert_to_native +#include "libtorrent/escape_string.hpp" + +namespace libtorrent +{ + std::vector > get_filesizes( + file_storage const& storage, std::string const& p) + { + std::string save_path = complete(p); + std::vector > sizes; + for (int i = 0; i < storage.num_files(); ++i) + { + size_type size = 0; + std::time_t time = 0; + + if (!storage.pad_file_at(i)) + { + file_status s; + error_code ec; + stat_file(storage.file_path(i, save_path), &s, ec); + + if (!ec) + { + size = s.file_size; + time = s.mtime; + } + } + sizes.push_back(std::make_pair(size, time)); + } + return sizes; + } + + // matches the sizes and timestamps of the files passed in + // in non-compact mode, actual file sizes and timestamps + // are allowed to be bigger and more recent than the fast + // resume data. This is because full allocation will not move + // pieces, so any older version of the resume data will + // still be a correct subset of the actual data on disk. + enum flags_t + { + compact_mode = 1, + ignore_timestamps = 2 + }; + + bool match_filesizes( + file_storage const& fs + , std::string p + , std::vector > const& sizes + , int flags + , error_code& error) + { + if ((int)sizes.size() != fs.num_files()) + { + error = errors::mismatching_number_of_files; + return false; + } + p = complete(p); + + std::vector >::const_iterator size_iter + = sizes.begin(); + for (int i = 0; i < fs.num_files(); ++i, ++size_iter) + { + size_type size = 0; + std::time_t time = 0; + if (fs.pad_file_at(i)) continue; + + file_status s; + error_code ec; + stat_file(fs.file_path(i, p), &s, ec); + + if (!ec) + { + size = s.file_size; + time = s.mtime; + } + + if (((flags & compact_mode) && size != size_iter->first) + || (!(flags & compact_mode) && size < size_iter->first)) + { + error = errors::mismatching_file_size; + return false; + } + + if (flags & ignore_timestamps) continue; + + // if there is no timestamp in the resume data, ignore it + if (size_iter->second == 0) continue; + + // allow one second 'slack', because of FAT volumes + // in sparse mode, allow the files to be more recent + // than the resume data, but only by 5 minutes + if (((flags & compact_mode) && (time > size_iter->second + 1 || time < size_iter->second - 1)) || + (!(flags & compact_mode) && (time > size_iter->second + 5 * 60 || time < size_iter->second - 1))) + { + error = errors::mismatching_file_timestamp; + return false; + } + } + return true; + } + + void storage_interface::set_error(std::string const& file, error_code const& ec) const + { + m_error_file = file; + m_error = ec; + } + + // for backwards compatibility, let the default readv and + // writev implementations be implemented in terms of the + // old read and write + int storage_interface::readv(file::iovec_t const* bufs + , int slot, int offset, int num_bufs, int flags) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = read((char*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int storage_interface::writev(file::iovec_t const* bufs, int slot + , int offset, int num_bufs, int flags) + { + int ret = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + { + int r = write((char const*)i->iov_base, slot, offset, i->iov_len); + offset += i->iov_len; + if (r == -1) return -1; + ret += r; + } + return ret; + } + + int copy_bufs(file::iovec_t const* bufs, int bytes, file::iovec_t* target) + { + int size = 0; + int ret = 1; + for (;;) + { + *target = *bufs; + size += bufs->iov_len; + if (size >= bytes) + { + target->iov_len -= size - bytes; + return ret; + } + ++bufs; + ++target; + ++ret; + } + } + + void advance_bufs(file::iovec_t*& bufs, int bytes) + { + int size = 0; + for (;;) + { + size += bufs->iov_len; + if (size >= bytes) + { + ((char*&)bufs->iov_base) += bufs->iov_len - (size - bytes); + bufs->iov_len = size - bytes; + return; + } + ++bufs; + } + } + + int bufs_size(file::iovec_t const* bufs, int num_bufs) + { + int size = 0; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + size += i->iov_len; + return size; + } + + void clear_bufs(file::iovec_t const* bufs, int num_bufs) + { + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) + std::memset(i->iov_base, 0, i->iov_len); + } + +#if TORRENT_USE_ASSERTS + int count_bufs(file::iovec_t const* bufs, int bytes) + { + int size = 0; + int count = 1; + if (bytes == 0) return 0; + for (file::iovec_t const* i = bufs;; ++i, ++count) + { + size += i->iov_len; + TORRENT_ASSERT(size <= bytes); + if (size >= bytes) return count; + } + } +#endif + + int piece_manager::hash_for_slot(int slot, partial_hash& ph, int piece_size + , int small_piece_size, sha1_hash* small_hash) + { + TORRENT_ASSERT_VAL(!error(), error()); + int num_read = 0; + int slot_size = piece_size - ph.offset; + if (slot_size > 0) + { + int block_size = 16 * 1024; + if (m_storage->disk_pool()) block_size = m_storage->disk_pool()->block_size(); + int size = slot_size; + int num_blocks = (size + block_size - 1) / block_size; + + // when we optimize for speed we allocate all the buffers we + // need for the rest of the piece, and read it all in one call + // and then hash it. When optimizing for memory usage, we read + // one block at a time and hash it. This ends up only using a + // single buffer + if (m_storage->settings().optimize_hashing_for_speed) + { + file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); + for (int i = 0; i < num_blocks; ++i) + { + bufs[i].iov_base = m_storage->disk_pool()->allocate_buffer("hash temp"); + bufs[i].iov_len = (std::min)(block_size, size); + size -= bufs[i].iov_len; + } + // deliberately pass in 0 as flags, to disable random_access + num_read = m_storage->readv(bufs, slot, ph.offset, num_blocks, 0); + // TODO: if the read fails, set error and exit immediately + + for (int i = 0; i < num_blocks; ++i) + { + if (small_hash && small_piece_size <= block_size) + { + ph.h.update((char const*)bufs[i].iov_base, small_piece_size); + *small_hash = hasher(ph.h).final(); + small_hash = 0; // avoid this case again + if (int(bufs[i].iov_len) > small_piece_size) + ph.h.update((char const*)bufs[i].iov_base + small_piece_size + , bufs[i].iov_len - small_piece_size); + } + else + { + ph.h.update((char const*)bufs[i].iov_base, bufs[i].iov_len); + small_piece_size -= bufs[i].iov_len; + } + ph.offset += bufs[i].iov_len; + m_storage->disk_pool()->free_buffer((char*)bufs[i].iov_base); + } + } + else + { + file::iovec_t buf; + disk_buffer_holder holder(*m_storage->disk_pool() + , m_storage->disk_pool()->allocate_buffer("hash temp")); + buf.iov_base = holder.get(); + for (int i = 0; i < num_blocks; ++i) + { + buf.iov_len = (std::min)(block_size, size); + // deliberately pass in 0 as flags, to disable random_access + int ret = m_storage->readv(&buf, slot, ph.offset, 1, 0); + if (ret > 0) num_read += ret; + // TODO: if the read fails, set error and exit immediately + + if (small_hash && small_piece_size <= block_size) + { + if (small_piece_size > 0) ph.h.update((char const*)buf.iov_base, small_piece_size); + *small_hash = hasher(ph.h).final(); + small_hash = 0; // avoid this case again + if (int(buf.iov_len) > small_piece_size) + ph.h.update((char const*)buf.iov_base + small_piece_size + , buf.iov_len - small_piece_size); + } + else + { + ph.h.update((char const*)buf.iov_base, buf.iov_len); + small_piece_size -= buf.iov_len; + } + + ph.offset += buf.iov_len; + size -= buf.iov_len; + } + } + if (error()) return 0; + } + return num_read; + } + + default_storage::default_storage(file_storage const& fs, file_storage const* mapped, std::string const& path + , file_pool& fp, std::vector const& file_prio) + : m_files(fs) + , m_file_priority(file_prio) + , m_pool(fp) + , m_page_size(page_size()) + , m_allocate_files(false) + { + if (mapped) m_mapped_files.reset(new file_storage(*mapped)); + + TORRENT_ASSERT(m_files.num_files() > 0); + m_save_path = complete(path); + } + + default_storage::~default_storage() { m_pool.release(this); } + + void default_storage::set_file_priority(std::vector const& prio) + { + m_file_priority = prio; + } + + bool default_storage::initialize(bool allocate_files) + { + m_allocate_files = allocate_files; + +#ifdef TORRENT_WINDOWS + // don't do full file allocations on network drives +#if TORRENT_USE_WSTRING + std::wstring f = convert_to_wstring(m_save_path); + int drive_type = GetDriveTypeW(f.c_str()); +#else + int drive_type = GetDriveTypeA(m_save_path.c_str()); +#endif + + if (drive_type == DRIVE_REMOTE) + m_allocate_files = false; +#endif + + error_code ec; + m_file_created.resize(files().num_files(), false); + + // first, create all missing directories + std::string last_path; + for (int file_index = 0; file_index < files().num_files(); ++file_index) + { + // ignore files that have priority 0 + if (int(m_file_priority.size()) > file_index + && m_file_priority[file_index] == 0) continue; + + // ignore pad files + if (files().pad_file_at(file_index)) continue; + + std::string file_path = files().file_path(file_index, m_save_path); + + file_status s; + stat_file(file_path, &s, ec); + if (ec && ec != boost::system::errc::no_such_file_or_directory + && ec != boost::system::errc::not_a_directory) + { + set_error(file_path, ec); + break; + } + + // if the file already exists, but is larger than what + // it's supposed to be, truncate it + // if the file is empty, just create it either way. + if ((!ec && s.file_size > files().file_size(file_index)) || files().file_size(file_index) == 0) + { + std::string dir = parent_path(file_path); + + if (dir != last_path) + { + last_path = dir; + + create_directories(last_path, ec); + if (ec) + { + set_error(dir, ec); + break; + } + } + ec.clear(); + + boost::intrusive_ptr f = open_file(file_index, file::read_write | file::random_access, ec); + if (ec) set_error(file_path, ec); + else if (f) + { + f->set_size(files().file_size(file_index), ec); + if (ec) set_error(file_path, ec); + } + if (ec) break; + } + ec.clear(); + } + + // close files that were opened in write mode + m_pool.release(this); + + return error() ? true : false; + } + +#ifndef TORRENT_NO_DEPRECATE + void default_storage::finalize_file(int index) {} +#endif + + bool default_storage::has_any_file() + { + for (int i = 0; i < files().num_files(); ++i) + { + error_code ec; + file_status s; + stat_file(files().file_path(i, m_save_path), &s, ec); + if (ec) continue; + if (s.mode & file_status::regular_file && files().file_size(i) > 0) + return true; + } + return false; + } + + bool default_storage::rename_file(int index, std::string const& new_filename) + { + if (index < 0 || index >= files().num_files()) return true; + std::string old_name = files().file_path(index, m_save_path); + m_pool.release(this, index); + + error_code ec; + std::string new_path; + if (is_complete(new_filename)) new_path = new_filename; + else new_path = combine_path(m_save_path, new_filename); + std::string new_dir = parent_path(new_path); + + // create any missing directories that the new filename + // lands in + create_directories(new_dir, ec); + if (ec) + { + set_error(new_dir, ec); + return true; + } + + rename(old_name, new_path, ec); + + // if old_name doesn't exist, that's not an error + // here. Once we start writing to the file, it will + // be written to the new filename + if (ec && ec != boost::system::errc::no_such_file_or_directory) + { + set_error(old_name, ec); + return true; + } + + // if old path doesn't exist, just rename the file + // in our file_storage, so that when it is created + // it will get the new name + if (!m_mapped_files) + { m_mapped_files.reset(new file_storage(m_files)); } + m_mapped_files->rename_file(index, new_filename); + return false; + } + + bool default_storage::release_files() + { + m_pool.release(this); + return false; + } + + void default_storage::delete_one_file(std::string const& p) + { + error_code ec; + remove(p, ec); + + if (ec && ec != boost::system::errc::no_such_file_or_directory) + set_error(p, ec); + } + + bool default_storage::delete_files() + { + // make sure we don't have the files open + m_pool.release(this); + + // delete the files from disk + std::set directories; + typedef std::set::iterator iter_t; + for (int i = 0; i < files().num_files(); ++i) + { + std::string fp = files().file_path(i); + bool complete = is_complete(fp); + std::string p = complete ? fp : combine_path(m_save_path, fp); + if (!complete) + { + std::string bp = parent_path(fp); + std::pair ret; + ret.second = true; + while (ret.second && !bp.empty()) + { + ret = directories.insert(combine_path(m_save_path, bp)); + bp = parent_path(bp); + } + } + delete_one_file(p); + } + + // remove the directories. Reverse order to delete + // subdirectories first + + for (std::set::reverse_iterator i = directories.rbegin() + , end(directories.rend()); i != end; ++i) + { + delete_one_file(*i); + } + + if (error()) return true; + return false; + } + + bool default_storage::write_resume_data(entry& rd) const + { + TORRENT_ASSERT(rd.type() == entry::dictionary_t); + + std::vector > file_sizes + = get_filesizes(files(), m_save_path); + + entry::list_type& fl = rd["file sizes"].list(); + for (std::vector >::iterator i + = file_sizes.begin(), end(file_sizes.end()); i != end; ++i) + { + entry::list_type p; + p.push_back(entry(i->first)); + p.push_back(entry(i->second)); + fl.push_back(entry(p)); + } + + return false; + } + + int default_storage::sparse_end(int slot) const + { + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < files().num_pieces()); + + size_type file_offset = (size_type)slot * files().piece_length(); + int file_index = 0; + + for (;;) + { + if (file_offset < files().file_size(file_index)) + break; + + file_offset -= files().file_size(file_index); + ++file_index; + TORRENT_ASSERT(file_index != files().num_files()); + } + + error_code ec; + boost::intrusive_ptr file_handle = open_file(file_index, file::read_only, ec); + if (!file_handle || ec) return slot; + + size_type data_start = file_handle->sparse_end(file_offset); + return int((data_start + files().piece_length() - 1) / files().piece_length()); + } + + bool default_storage::verify_resume_data(lazy_entry const& rd, error_code& error) + { + // TODO: make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance + // maybe use the same format as .torrent files and reuse some code from torrent_info + lazy_entry const* mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files->list_size() == m_files.num_files()) + { + m_mapped_files.reset(new file_storage(m_files)); + for (int i = 0; i < m_files.num_files(); ++i) + { + std::string new_filename = mapped_files->list_string_value_at(i); + if (new_filename.empty()) continue; + m_mapped_files->rename_file(i, new_filename); + } + } + + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == files().num_files()) + { + m_file_priority.resize(file_priority->list_size()); + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = boost::uint8_t(file_priority->list_int_value_at(i, 1)); + } + + std::vector > file_sizes; + lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes"); + if (file_sizes_ent == 0) + { + error = errors::missing_file_sizes; + return false; + } + + for (int i = 0; i < file_sizes_ent->list_size(); ++i) + { + lazy_entry const* e = file_sizes_ent->list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::int_t) + continue; + file_sizes.push_back(std::pair( + e->list_int_value_at(0), std::time_t(e->list_int_value_at(1)))); + } + + if (file_sizes.empty()) + { + error = errors::no_files_in_resume_data; + return false; + } + + bool seed = false; + + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots) + { + if (int(slots->list_size()) == m_files.num_pieces()) + { + seed = true; + for (int i = 0; i < slots->list_size(); ++i) + { + if (slots->list_int_value_at(i, -1) >= 0) continue; + seed = false; + break; + } + } + } + else if (lazy_entry const* pieces = rd.dict_find_string("pieces")) + { + if (int(pieces->string_length()) == m_files.num_pieces()) + { + seed = true; + char const* p = pieces->string_ptr(); + for (int i = 0; i < pieces->string_length(); ++i) + { + if ((p[i] & 1) == 1) continue; + seed = false; + break; + } + } + } + else + { + error = errors::missing_pieces; + return false; + } + + bool full_allocation_mode = false; + if (rd.dict_find_string_value("allocation") != "compact") + full_allocation_mode = true; + + if (seed) + { + if (files().num_files() != (int)file_sizes.size()) + { + error = errors::mismatching_number_of_files; + return false; + } + + std::vector >::iterator + fs = file_sizes.begin(); + // the resume data says we have the entire torrent + // make sure the file sizes are the right ones + for (int i = 0; i < files().num_files(); ++i, ++fs) + { + if (!files().pad_file_at(i) && files().file_size(i) != fs->first) + { + error = errors::mismatching_file_size; + return false; + } + } + } + int flags = (full_allocation_mode ? 0 : compact_mode) + | (settings().ignore_resume_timestamps ? ignore_timestamps : 0); + + return match_filesizes(files(), m_save_path, file_sizes, flags, error); + + } + + // returns true on success + int default_storage::move_storage(std::string const& sp, int flags) + { + int ret = piece_manager::no_error; + std::string save_path = complete(sp); + + // check to see if any of the files exist + error_code ec; + file_storage const& f = files(); + + file_status s; + if (flags == fail_if_exist) + { + stat_file(combine_path(save_path, f.name()), &s, ec); + if (ec != boost::system::errc::no_such_file_or_directory) + { + // the directory exists, check all the files + for (int i = 0; i < f.num_files(); ++i) + { + // files moved out to absolute paths are ignored + if (is_complete(f.file_path(i))) continue; + + std::string new_path = f.file_path(i, save_path); + stat_file(new_path, &s, ec); + if (ec != boost::system::errc::no_such_file_or_directory) + return piece_manager::file_exist; + } + } + } + + // collect all directories in to_move. This is because we + // try to move entire directories by default (instead of + // files independently). + std::set to_move; + for (int i = 0; i < f.num_files(); ++i) + { + // files moved out to absolute paths are not moved + if (is_complete(f.file_path(i))) continue; + + std::string split = split_path(f.file_path(i)); + to_move.insert(to_move.begin(), split); + } + + ec.clear(); + stat_file(save_path, &s, ec); + if (ec == boost::system::errc::no_such_file_or_directory) + { + ec.clear(); + create_directories(save_path, ec); + } + + if (ec) + { + set_error(save_path, ec); + return piece_manager::fatal_disk_error; + } + + m_pool.release(this); + + for (std::set::const_iterator i = to_move.begin() + , end(to_move.end()); i != end; ++i) + { + std::string old_path = combine_path(m_save_path, *i); + std::string new_path = combine_path(save_path, *i); + + rename(old_path, new_path, ec); + if (ec) + { + if (flags == dont_replace && ec == boost::system::errc::file_exists) + { + if (ret == piece_manager::no_error) ret = piece_manager::need_full_check; + continue; + } + + if (ec != boost::system::errc::no_such_file_or_directory) + { + error_code ec; + recursive_copy(old_path, new_path, ec); + if (ec == boost::system::errc::no_such_file_or_directory) + { + // it's a bit weird that rename() would not return + // ENOENT, but the file still wouldn't exist. But, + // in case it does, we're done. + ec.clear(); + break; + } + if (ec) + { + set_error(old_path, ec); + ret = piece_manager::fatal_disk_error; + } + else + { + remove_all(old_path, ec); + } + break; + } + } + } + + if (ret == piece_manager::no_error || ret == piece_manager::need_full_check) + m_save_path = save_path; + + return ret; + } + +#ifdef TORRENT_DEBUG +/* + void default_storage::shuffle() + { + int num_pieces = files().num_pieces(); + + std::vector pieces(num_pieces); + for (std::vector::iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + *i = static_cast(i - pieces.begin()); + } + std::srand((unsigned int)std::time(0)); + std::vector targets(pieces); + std::random_shuffle(pieces.begin(), pieces.end()); + std::random_shuffle(targets.begin(), targets.end()); + + for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i) + { + const int slot_index = targets[i]; + const int piece_index = pieces[i]; + const int slot_size =static_cast(m_files.piece_size(slot_index)); + std::vector buf(slot_size); + read(&buf[0], piece_index, 0, slot_size); + write(&buf[0], slot_index, 0, slot_size); + } + } +*/ +#endif + +#define TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size) \ + int num_blocks = (piece_size + disk_pool()->block_size() - 1) / disk_pool()->block_size(); \ + file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); \ + for (int i = 0, size = piece_size; i < num_blocks; ++i) \ + { \ + bufs[i].iov_base = disk_pool()->allocate_buffer("move temp"); \ + bufs[i].iov_len = (std::min)(disk_pool()->block_size(), size); \ + size -= bufs[i].iov_len; \ + } + +#define TORRENT_FREE_BLOCKS(bufs, num_blocks) \ + for (int i = 0; i < num_blocks; ++i) \ + disk_pool()->free_buffer((char*)bufs[i].iov_base); + +#define TORRENT_SET_SIZE(bufs, size, num_bufs) \ + for (num_bufs = 0; size > 0; size -= disk_pool()->block_size(), ++num_bufs) \ + bufs[num_bufs].iov_len = (std::min)(disk_pool()->block_size(), size) + + + bool default_storage::move_slot(int src_slot, int dst_slot) + { + bool r = true; + int piece_size = m_files.piece_size(dst_slot); + + TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size); + + readv(bufs, src_slot, 0, num_blocks); if (error()) goto ret; + writev(bufs, dst_slot, 0, num_blocks); if (error()) goto ret; + + r = false; +ret: + TORRENT_FREE_BLOCKS(bufs, num_blocks) + return r; + } + + bool default_storage::swap_slots(int slot1, int slot2) + { + bool r = true; + + // the size of the target slot is the size of the piece + int piece1_size = m_files.piece_size(slot2); + int piece2_size = m_files.piece_size(slot1); + + TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece1_size); + TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece2_size); + + readv(bufs1, slot1, 0, num_blocks1); if (error()) goto ret; + readv(bufs2, slot2, 0, num_blocks2); if (error()) goto ret; + writev(bufs1, slot2, 0, num_blocks1); if (error()) goto ret; + writev(bufs2, slot1, 0, num_blocks2); if (error()) goto ret; + + r = false; +ret: + TORRENT_FREE_BLOCKS(bufs1, num_blocks1) + TORRENT_FREE_BLOCKS(bufs2, num_blocks2) + return r; + } + + bool default_storage::swap_slots3(int slot1, int slot2, int slot3) + { + bool r = true; + + // the size of the target slot is the size of the piece + int piece_size = m_files.piece_length(); + int piece1_size = m_files.piece_size(slot2); + int piece2_size = m_files.piece_size(slot3); + int piece3_size = m_files.piece_size(slot1); + + TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece_size); + TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece_size); + + int tmp1 = 0; + int tmp2 = 0; + TORRENT_SET_SIZE(bufs1, piece1_size, tmp1); + readv(bufs1, slot1, 0, tmp1); if (error()) goto ret; + TORRENT_SET_SIZE(bufs2, piece2_size, tmp2); + readv(bufs2, slot2, 0, tmp2); if (error()) goto ret; + writev(bufs1, slot2, 0, tmp1); if (error()) goto ret; + TORRENT_SET_SIZE(bufs1, piece3_size, tmp1); + readv(bufs1, slot3, 0, tmp1); if (error()) goto ret; + writev(bufs2, slot3, 0, tmp2); if (error()) goto ret; + writev(bufs1, slot1, 0, tmp1); if (error()) goto ret; +ret: + TORRENT_FREE_BLOCKS(bufs1, num_blocks1) + TORRENT_FREE_BLOCKS(bufs2, num_blocks2) + return r; + } + + int default_storage::writev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " write " + << physical_offset(slot, offset) << std::endl; + } +#endif + fileop op = { &file::writev, &default_storage::write_unaligned + , m_settings ? settings().disk_io_write_mode : 0, file::read_write | flags }; +#ifdef TORRENT_DISK_STATS + int ret = readwritev(bufs, slot, offset, num_bufs, op); + if (pool) + { + pool->m_disk_access_log << log_time() << " write_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } + return ret; +#else + return readwritev(bufs, slot, offset, num_bufs, op); +#endif + } + + size_type default_storage::physical_offset(int slot, int offset) + { + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < m_files.num_pieces()); + TORRENT_ASSERT(offset >= 0); + + // find the file and file + size_type tor_off = size_type(slot) + * files().piece_length() + offset; + int file_index = files().file_index_at_offset(tor_off); + while (files().pad_file_at(file_index)) + { + ++file_index; + if (file_index == files().num_files()) + return size_type(slot) * files().piece_length() + offset; + // update offset as well, since we're moving it up ahead + tor_off = files().file_offset(file_index); + + } + TORRENT_ASSERT(!files().pad_file_at(file_index)); + + size_type file_offset = tor_off - files().file_offset(file_index); + TORRENT_ASSERT(file_offset >= 0); + + // open the file read only to avoid re-opening + // it in case it's already opened in read-only mode + error_code ec; + boost::intrusive_ptr f = open_file(file_index, file::read_only | file::random_access, ec); + + size_type ret = 0; + if (f && !ec) ret = f->phys_offset(file_offset); + + if (ret == 0) + { + // this means we don't support true physical offset + // just make something up + return size_type(slot) * files().piece_length() + offset; + } + return ret; + } + + void default_storage::hint_read(int slot, int offset, int size) + { + size_type start = slot * (size_type)m_files.piece_length() + offset; + TORRENT_ASSERT(start + size <= m_files.total_size()); + + int file_index = files().file_index_at_offset(start); + TORRENT_ASSERT(start >= files().file_offset(file_index)); + TORRENT_ASSERT(start < files().file_offset(file_index) + files().file_size(file_index)); + size_type file_offset = start - files().file_offset(file_index); + + boost::intrusive_ptr file_handle; + int bytes_left = size; + int slot_size = static_cast(m_files.piece_size(slot)); + + if (offset + bytes_left > slot_size) + bytes_left = slot_size - offset; + + TORRENT_ASSERT(bytes_left >= 0); + + int file_bytes_left; + for (;bytes_left > 0; ++file_index, bytes_left -= file_bytes_left) + { + TORRENT_ASSERT(file_index < files().num_files()); + + file_bytes_left = bytes_left; + if (file_offset + file_bytes_left > files().file_size(file_index)) + file_bytes_left = (std::max)(static_cast(files().file_size(file_index) - file_offset), 0); + + if (file_bytes_left == 0) continue; + + if (files().pad_file_at(file_index)) continue; + + error_code ec; + file_handle = open_file(file_index, file::read_only | file::random_access, ec); + + // failing to hint that we want to read is not a big deal + // just swollow the error and keep going + if (!file_handle || ec) continue; + + file_handle->hint_read(file_offset, file_bytes_left); + file_offset = 0; + } + } + + int default_storage::readv(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " read " + << physical_offset(slot, offset) << std::endl; + } +#endif + fileop op = { &file::readv, &default_storage::read_unaligned + , m_settings ? settings().disk_io_read_mode : 0, file::read_only | flags }; +#ifdef TORRENT_SIMULATE_SLOW_READ + boost::thread::sleep(boost::get_system_time() + + boost::posix_time::milliseconds(1000)); +#endif +#ifdef TORRENT_DISK_STATS + int ret = readwritev(bufs, slot, offset, num_bufs, op); + if (pool) + { + pool->m_disk_access_log << log_time() << " read_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } + return ret; +#else + return readwritev(bufs, slot, offset, num_bufs, op); +#endif + } + + // much of what needs to be done when reading and writing + // is buffer management and piece to file mapping. Most + // of that is the same for reading and writing. This function + // is a template, and the fileop decides what to do with the + // file and the buffers. + int default_storage::readwritev(file::iovec_t const* bufs, int slot, int offset + , int num_bufs, fileop const& op) + { + TORRENT_ASSERT(bufs != 0); + TORRENT_ASSERT(slot >= 0); + TORRENT_ASSERT(slot < m_files.num_pieces()); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset < m_files.piece_size(slot)); + TORRENT_ASSERT(num_bufs > 0); + + int size = bufs_size(bufs, num_bufs); + TORRENT_ASSERT(size > 0); + +#if TORRENT_USE_ASSERTS + std::vector slices + = files().map_block(slot, offset, size); + TORRENT_ASSERT(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_files.piece_length() + offset; + TORRENT_ASSERT(start + size <= m_files.total_size()); + + // find the file iterator and file offset + int file_index = files().file_index_at_offset(start); + TORRENT_ASSERT(start >= files().file_offset(file_index)); + TORRENT_ASSERT(start < files().file_offset(file_index) + files().file_size(file_index)); + size_type file_offset = start - files().file_offset(file_index); + + int buf_pos = 0; + error_code ec; + + boost::intrusive_ptr file_handle; + int bytes_left = size; + int slot_size = static_cast(m_files.piece_size(slot)); + + if (offset + bytes_left > slot_size) + bytes_left = slot_size - offset; + + TORRENT_ASSERT(bytes_left >= 0); + +#if TORRENT_USE_ASSERTS + int counter = 0; +#endif + + file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); + file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs); + copy_bufs(bufs, size, current_buf); + TORRENT_ASSERT(count_bufs(current_buf, size) == num_bufs); + int file_bytes_left; + for (;bytes_left > 0; ++file_index, bytes_left -= file_bytes_left + , buf_pos += file_bytes_left) + { + TORRENT_ASSERT(file_index < files().num_files()); + TORRENT_ASSERT(buf_pos >= 0); + + file_bytes_left = bytes_left; + if (file_offset + file_bytes_left > files().file_size(file_index)) + file_bytes_left = (std::max)(static_cast(files().file_size(file_index) - file_offset), 0); + + if (file_bytes_left == 0) continue; + +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(int(slices.size()) > counter); + size_type slice_size = slices[counter].size; + TORRENT_ASSERT(slice_size == file_bytes_left); + TORRENT_ASSERT(slices[counter].file_index == file_index); + ++counter; +#endif + + if (files().pad_file_at(file_index)) + { + if ((op.mode & file::rw_mask) == file::read_only) + { + int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs); + TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs); + TORRENT_ASSERT(num_tmp_bufs <= num_bufs); + clear_bufs(tmp_bufs, num_tmp_bufs); + } + advance_bufs(current_buf, file_bytes_left); + TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs); + file_offset = 0; + continue; + } + + error_code ec; + file_handle = open_file(file_index, op.mode, ec); + if (((op.mode & file::rw_mask) != file::read_only) + && ec == boost::system::errc::no_such_file_or_directory) + { + // this means the directory the file is in doesn't exist. + // so create it + ec.clear(); + std::string path = files().file_path(file_index, m_save_path); + create_directories(parent_path(path), ec); + // if the directory creation failed, don't try to open the file again + // but actually just fail + if (!ec) file_handle = open_file(file_index, op.mode, ec); + } + + if (!file_handle || ec) + { + std::string path = files().file_path(file_index, m_save_path); + TORRENT_ASSERT(ec); + set_error(path, ec); + return -1; + } + + // if the file has priority 0, don't allocate it + if (m_allocate_files && (op.mode & file::rw_mask) != file::read_only + && (int(m_file_priority.size()) <= file_index || m_file_priority[file_index] > 0)) + { + TORRENT_ASSERT(int(m_file_created.size()) == files().num_files()); + if (m_file_created[file_index] == false) + { + file_handle->set_size(files().file_size(file_index), ec); + m_file_created.set_bit(file_index); + if (ec) + { + set_error(files().file_path(file_index, m_save_path), ec); + return -1; + } + } + } + + int num_tmp_bufs = copy_bufs(current_buf, file_bytes_left, tmp_bufs); + TORRENT_ASSERT(count_bufs(tmp_bufs, file_bytes_left) == num_tmp_bufs); + TORRENT_ASSERT(num_tmp_bufs <= num_bufs); + int bytes_transferred = 0; + // if the file is opened in no_buffer mode, and the + // read is unaligned, we need to fall back on a slow + // special read that reads aligned buffers and copies + // it into the one supplied + size_type adjusted_offset = files().file_base(file_index) + file_offset; + if ((file_handle->open_mode() & file::no_buffer) + && ((adjusted_offset & (file_handle->pos_alignment()-1)) != 0 + || (uintptr_t(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0)) + { + bytes_transferred = (int)(this->*op.unaligned_op)(file_handle, adjusted_offset + , tmp_bufs, num_tmp_bufs, ec); + if ((op.mode & file::rw_mask) != file::read_only + && adjusted_offset + bytes_transferred >= files().file_size(file_index) + && (file_handle->pos_alignment() > 0 || file_handle->size_alignment() > 0)) + { + // we were writing, and we just wrote the last block of the file + // we likely wrote a bit too much, since we're restricted to + // a specific alignment for writes. Make sure to truncate the size + + // TODO: 0 what if file_base is used to merge several virtual files + // into a single physical file? We should probably disable this + // if file_base is used. This is not a widely used feature though + file_handle->set_size(files().file_size(file_index), ec); + } + } + else + { + bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset + , tmp_bufs, num_tmp_bufs, ec); + TORRENT_ASSERT(bytes_transferred <= bufs_size(tmp_bufs, num_tmp_bufs)); + } + file_offset = 0; + + if (ec) + { + set_error(files().file_path(file_index, m_save_path), ec); + return -1; + } + + if (file_bytes_left != bytes_transferred) + return bytes_transferred; + + advance_bufs(current_buf, bytes_transferred); + TORRENT_ASSERT(count_bufs(current_buf, bytes_left - file_bytes_left) <= num_bufs); + } + return size; + } + + // these functions are inefficient, but should be fairly uncommon. The read + // case happens if unaligned files are opened in no_buffer mode or if clients + // makes unaligned requests (and the disk cache is disabled or fully utilized + // for write cache). + + // they read an unaligned buffer from a file that requires aligned access + + size_type default_storage::read_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec) + { + const int pos_align = file_handle->pos_alignment()-1; + const int size_align = file_handle->size_alignment()-1; + + const int size = bufs_size(bufs, num_bufs); + const int start_adjust = file_offset & pos_align; + TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment())); + const size_type aligned_start = file_offset - start_adjust; + const int aligned_size = ((size+start_adjust) & size_align) + ? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust; + TORRENT_ASSERT((aligned_size & size_align) == 0); + + // allocate a temporary, aligned, buffer + aligned_holder aligned_buf(aligned_size); + file::iovec_t b = {aligned_buf.get(), size_t(aligned_size) }; + size_type ret = file_handle->readv(aligned_start, &b, 1, ec); + if (ret < 0) + { + TORRENT_ASSERT(ec); + return ret; + } + if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0)); + + char* read_buf = aligned_buf.get() + start_adjust; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i) + { + memcpy(i->iov_base, read_buf, i->iov_len); + read_buf += i->iov_len; + } + + return size; + } + + // this is the really expensive one. To write unaligned, we need to read + // an aligned block, overlay the unaligned buffer, and then write it back + size_type default_storage::write_unaligned(boost::intrusive_ptr const& file_handle + , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec) + { + const int pos_align = file_handle->pos_alignment()-1; + const int size_align = file_handle->size_alignment()-1; + + const int size = bufs_size(bufs, num_bufs); + const int start_adjust = file_offset & pos_align; + TORRENT_ASSERT(start_adjust == (file_offset % file_handle->pos_alignment())); + const size_type aligned_start = file_offset - start_adjust; + const int aligned_size = ((size+start_adjust) & size_align) + ? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust; + TORRENT_ASSERT((aligned_size & size_align) == 0); + + size_type actual_file_size = file_handle->get_size(ec); + if (ec && ec != make_error_code(boost::system::errc::no_such_file_or_directory)) return -1; + ec.clear(); + + // allocate a temporary, aligned, buffer + aligned_holder aligned_buf(aligned_size); + file::iovec_t b = {aligned_buf.get(), size_t(aligned_size) }; + // we have something to read + if (aligned_start < actual_file_size && !ec) + { + size_type ret = file_handle->readv(aligned_start, &b, 1, ec); + if (ec +#ifdef TORRENT_WINDOWS + && ec != error_code(ERROR_HANDLE_EOF, get_system_category()) +#endif + ) + return ret; + } + + ec.clear(); + + // OK, we read the portion of the file. Now, overlay the buffer we're writing + + char* write_buf = aligned_buf.get() + start_adjust; + for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i) + { + memcpy(write_buf, i->iov_base, i->iov_len); + write_buf += i->iov_len; + } + + // write the buffer back to disk + size_type ret = file_handle->writev(aligned_start, &b, 1, ec); + + if (ret < 0) + { + TORRENT_ASSERT(ec); + return ret; + } + if (ret - start_adjust < size) return (std::max)(ret - start_adjust, size_type(0)); + return size; + } + + int default_storage::write( + const char* buf + , int slot + , int offset + , int size) + { + file::iovec_t b = { (file::iovec_base_t)buf, size_t(size) }; + return writev(&b, slot, offset, 1, 0); + } + + int default_storage::read( + char* buf + , int slot + , int offset + , int size) + { + file::iovec_t b = { (file::iovec_base_t)buf, size_t(size) }; + return readv(&b, slot, offset, 1); + } + + boost::intrusive_ptr default_storage::open_file(int file_index, int mode + , error_code& ec) const + { + int cache_setting = m_settings ? settings().disk_io_write_mode : 0; + if (cache_setting == session_settings::disable_os_cache + || (cache_setting == session_settings::disable_os_cache_for_aligned_files + && ((files().file_offset(file_index) + files().file_base(file_index)) & (m_page_size-1)) == 0)) + mode |= file::no_buffer; + bool lock_files = m_settings ? settings().lock_files : false; + if (lock_files) mode |= file::lock_file; + if (!m_allocate_files) mode |= file::sparse; + + // files with priority 0 should always be sparse + if (int(m_file_priority.size()) > file_index && m_file_priority[file_index] == 0) + mode |= file::sparse; + + if (m_settings && settings().no_atime_storage) mode |= file::no_atime; + + return m_pool.open_file(const_cast(this), m_save_path, file_index, files(), mode, ec); + } + + storage_interface* default_storage_constructor(file_storage const& fs + , file_storage const* mapped, std::string const& path, file_pool& fp + , std::vector const& file_prio) + { + return new default_storage(fs, mapped, path, fp, file_prio); + } + + int disabled_storage::readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " read " + << physical_offset(slot, offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " read_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } +#endif + return ret; + } + + int disabled_storage::writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs, int flags) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " write " + << physical_offset(slot, offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " write_end " + << (physical_offset(slot, offset) + ret) << std::endl; + } +#endif + return ret; + } + + storage_interface* disabled_storage_constructor(file_storage const& fs + , file_storage const* mapped, std::string const& path, file_pool& fp + , std::vector const&) + { + return new disabled_storage(fs.piece_length()); + } + + // -- piece_manager ----------------------------------------------------- + + piece_manager::piece_manager( + boost::shared_ptr const& torrent + , boost::intrusive_ptr info + , std::string const& save_path + , file_pool& fp + , disk_io_thread& io + , storage_constructor_type sc + , storage_mode_t sm + , std::vector const& file_prio) + : m_info(info) + , m_files(m_info->files()) + , m_storage(sc(m_info->orig_files(), &m_info->files() != &m_info->orig_files() + ? &m_info->files() : 0, save_path, fp, file_prio)) + , m_storage_mode(sm) + , m_save_path(complete(save_path)) + , m_state(state_none) + , m_current_slot(0) + , m_out_of_place(false) + , m_scratch_piece(-1) + , m_last_piece(-1) + , m_storage_constructor(sc) + , m_io_thread(io) + , m_torrent(torrent) + { + m_storage->m_disk_pool = &m_io_thread; + } + + piece_manager::~piece_manager() + { + } + + void piece_manager::async_set_file_priority( + std::vector const& prios + , boost::function const& handler) + { + std::vector* p = new std::vector(prios); + + disk_io_job j; + j.storage = this; + j.buffer = (char*)p; + j.action = disk_io_job::file_priority; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_save_resume_data( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::save_resume_data; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_clear_read_cache( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::clear_read_cache; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_release_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::release_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::abort_disk_io() + { + m_io_thread.stop(this); + } + + void piece_manager::async_delete_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::delete_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_move_storage(std::string const& p, int flags + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::move_storage; + j.str = p; + j.piece = flags; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_check_fastresume(lazy_entry const* resume_data + , boost::function const& handler) + { + TORRENT_ASSERT(resume_data != 0); + disk_io_job j; + j.storage = this; + j.action = disk_io_job::check_fastresume; + j.buffer = (char*)resume_data; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_rename_file(int index, std::string const& name + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.piece = index; + j.str = name; + j.action = disk_io_job::rename_file; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_check_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::check_files; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_read_and_hash( + peer_request const& r + , boost::function const& handler + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::read_and_hash; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = 0; + j.cache_min_time = cache_expiry; + TORRENT_ASSERT(r.length <= 16 * 1024); + m_io_thread.add_job(j, handler); +#ifdef TORRENT_USE_ASSERTS + mutex::scoped_lock l(m_mutex); + // if this assert is hit, it suggests + // that check_files was not successful + TORRENT_ASSERT(slot_for(r.piece) >= 0); +#endif + } + + void piece_manager::async_cache(int piece + , boost::function const& handler + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::cache_piece; + j.piece = piece; + j.offset = 0; + j.buffer_size = 0; + j.buffer = 0; + j.cache_min_time = cache_expiry; + m_io_thread.add_job(j, handler); + } + + void piece_manager::async_read( + peer_request const& r + , boost::function const& handler + , int cache_line_size + , int cache_expiry) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::read; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = 0; + j.max_cache_line = cache_line_size; + j.cache_min_time = cache_expiry; + + // if a buffer is not specified, only one block can be read + // since that is the size of the pool allocator's buffers + TORRENT_ASSERT(r.length <= 16 * 1024); + m_io_thread.add_job(j, handler); +#ifdef TORRENT_USE_ASSERTS + mutex::scoped_lock l(m_mutex); + // if this assert is hit, it suggests + // that check_files was not successful + TORRENT_ASSERT(slot_for(r.piece) >= 0); +#endif + } + + int piece_manager::async_write( + peer_request const& r + , disk_buffer_holder& buffer + , boost::function const& handler) + { + TORRENT_ASSERT(r.length <= 16 * 1024); + // the buffer needs to be allocated through the io_thread + TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get())); + + disk_io_job j; + j.storage = this; + j.action = disk_io_job::write; + j.piece = r.piece; + j.offset = r.start; + j.buffer_size = r.length; + j.buffer = buffer.get(); + int queue_size = m_io_thread.add_job(j, handler); + buffer.release(); + + return queue_size; + } + + void piece_manager::async_hash(int piece + , boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::hash; + j.piece = piece; + + m_io_thread.add_job(j, handler); + } + + std::string piece_manager::save_path() const + { + mutex::scoped_lock l(m_mutex); + return m_save_path; + } + + sha1_hash piece_manager::hash_for_piece_impl(int piece, int* readback) + { + TORRENT_ASSERT(!m_storage->error()); + + partial_hash ph; + + std::map::iterator i = m_piece_hasher.find(piece); + if (i != m_piece_hasher.end()) + { + ph = i->second; + m_piece_hasher.erase(i); + } + + int slot = slot_for(piece); + TORRENT_ASSERT(slot != has_no_slot); + if (slot < 0) return sha1_hash(0); + int read = hash_for_slot(slot, ph, m_files.piece_size(piece)); + if (readback) *readback = read; + if (m_storage->error()) return sha1_hash(0); + return ph.h.final(); + } + + int piece_manager::move_storage_impl(std::string const& save_path, int flags) + { + int ret = m_storage->move_storage(save_path, flags); + + if (ret == no_error || ret == need_full_check) + { + m_save_path = complete(save_path); + } + return ret; + } + + void piece_manager::write_resume_data(entry& rd) const + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + m_storage->write_resume_data(rd); + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + entry::list_type& slots = rd["slots"].list(); + slots.clear(); + std::vector::const_reverse_iterator last; + for (last = m_slot_to_piece.rbegin(); + last != m_slot_to_piece.rend(); ++last) + { + if (*last != unallocated) break; + } + + for (std::vector::const_iterator i = + m_slot_to_piece.begin(); + i != last.base(); ++i) + { + slots.push_back((*i >= 0) ? *i : unassigned); + } + } + + rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse" + :m_storage_mode == storage_mode_allocate?"full":"compact"; + } + + void piece_manager::mark_failed(int piece_index) + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + if (m_storage_mode != internal_storage_mode_compact_deprecated) return; + + TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + int slot_index = m_piece_to_slot[piece_index]; + TORRENT_ASSERT(slot_index >= 0); + + m_slot_to_piece[slot_index] = unassigned; + m_piece_to_slot[piece_index] = has_no_slot; + m_free_slots.push_back(slot_index); + } + + void piece_manager::hint_read_impl(int piece_index, int offset, int size) + { + m_last_piece = piece_index; + int slot = slot_for(piece_index); + if (slot <= 0) return; + m_storage->hint_read(slot, offset, size); + } + + int piece_manager::read_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs) + { + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(num_bufs > 0); + m_last_piece = piece_index; + int slot = slot_for(piece_index); + TORRENT_ASSERT(slot >= 0); + if (slot < 0) return 0; + return m_storage->readv(bufs, slot, offset, num_bufs); + } + + int piece_manager::write_impl( + file::iovec_t* bufs + , int piece_index + , int offset + , int num_bufs) + { + TORRENT_ASSERT(bufs); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(num_bufs > 0); + TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); + + int size = bufs_size(bufs, num_bufs); + + file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs); + std::copy(bufs, bufs + num_bufs, iov); + m_last_piece = piece_index; + int slot = allocate_slot_for_piece(piece_index); + int ret = m_storage->writev(bufs, slot, offset, num_bufs); + // only save the partial hash if the write succeeds + if (ret != size) return ret; + + if (m_storage->settings().disable_hash_checks) return ret; + + if (offset == 0) + { + partial_hash& ph = m_piece_hasher[piece_index]; + TORRENT_ASSERT(ph.offset == 0); + ph.offset = size; + + for (file::iovec_t* i = iov, *end(iov + num_bufs); i < end; ++i) + ph.h.update((char const*)i->iov_base, i->iov_len); + + } + else + { + std::map::iterator i = m_piece_hasher.find(piece_index); + if (i != m_piece_hasher.end()) + { +#ifdef TORRENT_USE_ASSERTS + TORRENT_ASSERT(i->second.offset > 0); + int hash_offset = i->second.offset; + TORRENT_ASSERT(offset >= hash_offset); +#endif + if (offset == i->second.offset) + { +#ifdef TORRENT_PARTIAL_HASH_LOG + out << time_now_string() << " UPDATING [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; +#endif + for (file::iovec_t* b = iov, *end(iov + num_bufs); b < end; ++b) + { + i->second.h.update((char const*)b->iov_base, b->iov_len); + i->second.offset += b->iov_len; + } + } +#ifdef TORRENT_PARTIAL_HASH_LOG + else + { + out << time_now_string() << " SKIPPING (out of order) [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; + } +#endif + } +#ifdef TORRENT_PARTIAL_HASH_LOG + else + { + out << time_now_string() << " SKIPPING (no entry) [" + " s: " << this + << " p: " << piece_index + << " off: " << offset + << " size: " << size + << " entries: " << m_piece_hasher.size() + << " ]" << std::endl; + } +#endif + } + + return ret; + } + + size_type piece_manager::physical_offset( + int piece_index + , int offset) + { + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); + + int slot = slot_for(piece_index); + // we may not have a slot for this piece yet. + // assume there is no re-mapping of slots + if (slot < 0) slot = piece_index; + return m_storage->physical_offset(slot, offset); + } + + int piece_manager::identify_data( + sha1_hash const& large_hash + , sha1_hash const& small_hash + , int current_slot) + { +// INVARIANT_CHECK; + typedef std::multimap::const_iterator map_iter; + map_iter begin1; + map_iter end1; + map_iter begin2; + map_iter end2; + + // makes the lookups for the small digest and the large digest + boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash); + boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash); + + // copy all potential piece indices into this vector + std::vector matching_pieces; + for (map_iter i = begin1; i != end1; ++i) + matching_pieces.push_back(i->second); + for (map_iter i = begin2; i != end2; ++i) + matching_pieces.push_back(i->second); + + // no piece matched the data in the slot + if (matching_pieces.empty()) + return unassigned; + + // ------------------------------------------ + // CHECK IF THE PIECE IS IN ITS CORRECT PLACE + // ------------------------------------------ + + if (std::find( + matching_pieces.begin() + , matching_pieces.end() + , current_slot) != matching_pieces.end()) + { + // the current slot is among the matching pieces, so + // we will assume that the piece is in the right place + const int piece_index = current_slot; + + int other_slot = m_piece_to_slot[piece_index]; + if (other_slot >= 0) + { + // we have already found a piece with + // this index. + + // take one of the other matching pieces + // that hasn't already been assigned + int other_piece = -1; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue; + other_piece = *i; + break; + } + if (other_piece >= 0) + { + // replace the old slot with 'other_piece' + m_slot_to_piece[other_slot] = other_piece; + m_piece_to_slot[other_piece] = other_slot; + } + else + { + // this index is the only piece with this + // hash. The previous slot we found with + // this hash must be the same piece. Mark + // that piece as unassigned, since this slot + // is the correct place for the piece. + m_slot_to_piece[other_slot] = unassigned; + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(other_slot); + } + TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot); + TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0); + m_piece_to_slot[piece_index] = has_no_slot; + } + + TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot); + + return piece_index; + } + + // find a matching piece that hasn't + // already been assigned + int free_piece = unassigned; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (m_piece_to_slot[*i] >= 0) continue; + free_piece = *i; + break; + } + + if (free_piece >= 0) + { + TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot); + return free_piece; + } + else + { + TORRENT_ASSERT(free_piece == unassigned); + return unassigned; + } + } + + int piece_manager::check_no_fastresume(error_code& error) + { + bool has_files = false; + if (!m_storage->settings().no_recheck_incomplete_resume) + { + has_files = m_storage->has_any_file(); + if (m_storage->error()) + return fatal_disk_error; + + if (has_files) + { + m_state = state_full_check; + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_unallocated_slots.clear(); + m_free_slots.clear(); + } + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + // in compact mode without checking, we need to + // populate the unallocated list + TORRENT_ASSERT(m_unallocated_slots.empty()); + for (int i = 0, end(m_files.num_pieces()); i < end; ++i) + m_unallocated_slots.push_back(i); + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + } + + return check_init_storage(error); + } + + int piece_manager::check_init_storage(error_code& error) + { + if (m_storage->initialize(m_storage_mode == storage_mode_allocate)) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + m_current_slot = 0; + return fatal_disk_error; + } + m_state = state_finished; + m_scratch_buffer.reset(); + m_scratch_buffer2.reset(); + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + // if no piece is out of place + // since we're in full allocation mode, we can + // forget the piece allocation tables + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + std::vector().swap(m_free_slots); + std::vector().swap(m_unallocated_slots); + } + return no_error; + } + + // check if the fastresume data is up to date + // if it is, use it and return true. If it + // isn't return false and the full check + // will be run + int piece_manager::check_fastresume( + lazy_entry const& rd, error_code& error) + { + mutex::scoped_lock lock(m_mutex); + + INVARIANT_CHECK; + + TORRENT_ASSERT(m_files.piece_length() > 0); + + m_current_slot = 0; + + // if we don't have any resume data, return + if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error); + + if (rd.type() != lazy_entry::dict_t) + { + error = errors::not_a_dictionary; + return check_no_fastresume(error); + } + + int block_size = (std::min)(16 * 1024, m_files.piece_length()); + int blocks_per_piece = int(rd.dict_find_int_value("blocks per piece", -1)); + if (blocks_per_piece != -1 + && blocks_per_piece != m_files.piece_length() / block_size) + { + error = errors::invalid_blocks_per_piece; + return check_no_fastresume(error); + } + + storage_mode_t storage_mode = internal_storage_mode_compact_deprecated; + if (rd.dict_find_string_value("allocation") != "compact") + storage_mode = storage_mode_sparse; + + if (!m_storage->verify_resume_data(rd, error)) + return check_no_fastresume(error); + + // assume no piece is out of place (i.e. in a slot + // other than the one it should be in) + bool out_of_place = false; + + // if we don't have a piece map, we need the slots + // if we're in compact mode, we also need the slots map + if (storage_mode == internal_storage_mode_compact_deprecated || rd.dict_find("pieces") == 0) + { + // read slots map + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots == 0) + { + error = errors::missing_slots; + return check_no_fastresume(error); + } + + if ((int)slots->list_size() > m_files.num_pieces()) + { + error = errors::too_many_slots; + return check_no_fastresume(error); + } + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + int num_pieces = int(m_files.num_pieces()); + m_slot_to_piece.resize(num_pieces, unallocated); + m_piece_to_slot.resize(num_pieces, has_no_slot); + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) + { + error = errors::invalid_slot_list; + return check_no_fastresume(error); + } + + int index = int(e->int_value()); + if (index >= num_pieces || index < -2) + { + error = errors::invalid_piece_index; + return check_no_fastresume(error); + } + if (index >= 0) + { + m_slot_to_piece[i] = index; + m_piece_to_slot[index] = i; + if (i != index) out_of_place = true; + } + else if (index == unassigned) + { + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(i); + } + else + { + TORRENT_ASSERT(index == unallocated); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_unallocated_slots.push_back(i); + } + } + } + else + { + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) + { + error = errors::invalid_slot_list; + return check_no_fastresume(error); + } + + int index = int(e->int_value()); + if (index != i && index >= 0) + { + error = errors::invalid_piece_index; + return check_no_fastresume(error); + } + } + } + + // This will corrupt the storage + // use while debugging to find + // states that cannot be scanned + // by check_pieces. + // m_storage->shuffle(); + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + if (m_unallocated_slots.empty()) switch_to_full_mode(); + } + else + { + TORRENT_ASSERT(m_free_slots.empty()); + TORRENT_ASSERT(m_unallocated_slots.empty()); + + if (out_of_place) + { + // in this case we're in full allocation mode, but + // we're resuming a compact allocated storage + m_state = state_expand_pieces; + m_current_slot = 0; + error = errors::pieces_need_reorder; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + + } + else if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + // read piece map + lazy_entry const* pieces = rd.dict_find("pieces"); + if (pieces == 0 || pieces->type() != lazy_entry::string_t) + { + error = errors::missing_pieces; + return check_no_fastresume(error); + } + + if ((int)pieces->string_length() != m_files.num_pieces()) + { + error = errors::too_many_slots; + return check_no_fastresume(error); + } + + int num_pieces = int(m_files.num_pieces()); + m_slot_to_piece.resize(num_pieces, unallocated); + m_piece_to_slot.resize(num_pieces, has_no_slot); + char const* have_pieces = pieces->string_ptr(); + for (int i = 0; i < num_pieces; ++i) + { + if (have_pieces[i] & 1) + { + m_slot_to_piece[i] = i; + m_piece_to_slot[i] = i; + } + else + { + m_free_slots.push_back(i); + } + } + if (m_unallocated_slots.empty()) switch_to_full_mode(); + } + + return check_init_storage(error); + } + +/* + state chart: + + check_fastresume() ----------+ + | + | | | + | v v + | +------------+ +---------------+ + | | full_check |-->| expand_pieses | + | +------------+ +---------------+ + | | | + | v | + | +--------------+ | + +->| finished | <------+ + +--------------+ +*/ + + + // performs the full check and full allocation + // (if necessary). returns true if finished and + // false if it should be called again + // the second return value is the progress the + // file check is at. 0 is nothing done, and 1 + // is finished + int piece_manager::check_files(int& current_slot, int& have_piece, error_code& error) + { + if (m_state == state_none) return check_no_fastresume(error); + + if (m_piece_to_slot.empty()) + { + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + } + if (m_slot_to_piece.empty()) + { + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + } + + current_slot = m_current_slot; + have_piece = -1; + if (m_state == state_expand_pieces) + { + INVARIANT_CHECK; + + if (m_scratch_piece >= 0) + { + int piece = m_scratch_piece; + int other_piece = m_slot_to_piece[piece]; + m_scratch_piece = -1; + + if (other_piece >= 0) + { + if (!m_scratch_buffer2.get()) + m_scratch_buffer2.reset(page_aligned_allocator::malloc(m_files.piece_length())); + + int piece_size = m_files.piece_size(other_piece); + file::iovec_t b = {m_scratch_buffer2.get(), size_t(piece_size) }; + if (m_storage->readv(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_scratch_piece = other_piece; + m_piece_to_slot[other_piece] = unassigned; + } + + // the slot where this piece belongs is + // free. Just move the piece there. + int piece_size = m_files.piece_size(piece); + file::iovec_t b = {m_scratch_buffer.get(), size_t(piece_size) }; + if (m_storage->writev(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_piece_to_slot[piece] = piece; + m_slot_to_piece[piece] = piece; + + if (other_piece >= 0) m_scratch_buffer.swap(m_scratch_buffer2); + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + while (m_current_slot < m_files.num_pieces() + && (m_slot_to_piece[m_current_slot] == m_current_slot + || m_slot_to_piece[m_current_slot] < 0)) + { + ++m_current_slot; + } + + if (m_current_slot == m_files.num_pieces()) + { + return check_init_storage(error); + } + + TORRENT_ASSERT(m_current_slot < m_files.num_pieces()); + + int piece = m_slot_to_piece[m_current_slot]; + TORRENT_ASSERT(piece >= 0); + int other_piece = m_slot_to_piece[piece]; + if (other_piece >= 0) + { + // there is another piece in the slot + // where this one goes. Store it in the scratch + // buffer until next iteration. + if (!m_scratch_buffer.get()) + m_scratch_buffer.reset(page_aligned_allocator::malloc(m_files.piece_length())); + + int piece_size = m_files.piece_size(other_piece); + file::iovec_t b = {m_scratch_buffer.get(), size_t(piece_size) }; + if (m_storage->readv(&b, piece, 0, 1) != piece_size) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + m_scratch_piece = other_piece; + m_piece_to_slot[other_piece] = unassigned; + } + + // the slot where this piece belongs is + // free. Just move the piece there. + m_last_piece = piece; + m_storage->move_slot(m_current_slot, piece); + if (m_storage->error()) return -1; + + m_piece_to_slot[piece] = piece; + m_slot_to_piece[m_current_slot] = unassigned; + m_slot_to_piece[piece] = piece; + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + TORRENT_ASSERT(m_state == state_full_check); + if (m_state == state_finished) return 0; + + int skip = check_one_piece(have_piece); + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + + if (skip == -1) + { + error = m_storage->error(); + TORRENT_ASSERT(error); + return fatal_disk_error; + } + + if (skip > 0) + { + clear_error(); + // skip means that the piece we checked failed to be read from disk + // completely. This may be caused by the file not being there, or the + // piece overlapping with a sparse region. We should skip 'skip' number + // of pieces + + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + for (int i = m_current_slot; i < m_current_slot + skip - 1; ++i) + { + TORRENT_ASSERT(m_slot_to_piece[i] == unallocated); + m_unallocated_slots.push_back(i); + } + } + + // current slot will increase by one below + m_current_slot += skip - 1; + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + } + + ++m_current_slot; + current_slot = m_current_slot; + + if (m_current_slot >= m_files.num_pieces()) + { + TORRENT_ASSERT(m_current_slot == m_files.num_pieces()); + + // clear the memory we've been using + std::multimap().swap(m_hash_to_piece); + + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + if (!m_out_of_place) + { + // if no piece is out of place + // since we're in full allocation mode, we can + // forget the piece allocation tables + + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + return check_init_storage(error); + } + else + { + // in this case we're in full allocation mode, but + // we're resuming a compact allocated storage + m_state = state_expand_pieces; + m_current_slot = 0; + current_slot = m_current_slot; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + } + else if (m_unallocated_slots.empty()) + { + switch_to_full_mode(); + } + return check_init_storage(error); + } + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; + } + + int piece_manager::skip_file() const + { + size_type file_offset = 0; + size_type current_offset = size_type(m_current_slot) * m_files.piece_length(); + for (int i = 0; i < m_files.num_files(); ++i) + { + file_offset += m_files.file_size(i); + if (file_offset > current_offset) break; + } + + TORRENT_ASSERT(file_offset > current_offset); + int ret = static_cast( + (file_offset - current_offset + m_files.piece_length() - 1) + / m_files.piece_length()); + TORRENT_ASSERT(ret >= 1); + return ret; + } + + // -1 = error, 0 = ok, >0 = skip this many pieces + int piece_manager::check_one_piece(int& have_piece) + { + // ------------------------ + // DO THE FULL CHECK + // ------------------------ + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces()); + TORRENT_ASSERT(have_piece == -1); + + // initialization for the full check + if (m_hash_to_piece.empty()) + { + for (int i = 0; i < m_files.num_pieces(); ++i) + m_hash_to_piece.insert(std::pair(m_info->hash_for_piece(i), i)); + } + + partial_hash ph; + int num_read = 0; + int piece_size = m_files.piece_size(m_current_slot); + int small_piece_size = m_files.piece_size(m_files.num_pieces() - 1); + bool read_short = true; + sha1_hash small_hash; + if (piece_size == small_piece_size) + { + num_read = hash_for_slot(m_current_slot, ph, piece_size, 0, 0); + } + else + { + num_read = hash_for_slot(m_current_slot, ph, piece_size + , small_piece_size, &small_hash); + } + read_short = num_read != piece_size; + + if (read_short) + { + if (m_storage->error() +#ifdef TORRENT_WINDOWS + && m_storage->error() != error_code(ERROR_PATH_NOT_FOUND, get_system_category()) + && m_storage->error() != error_code(ERROR_FILE_NOT_FOUND, get_system_category()) + && m_storage->error() != error_code(ERROR_HANDLE_EOF, get_system_category()) + && m_storage->error() != error_code(ERROR_INVALID_HANDLE, get_system_category())) +#else + && m_storage->error() != error_code(ENOENT, get_posix_category())) +#endif + { + return -1; + } + // if the file is incomplete, skip the rest of it + return skip_file(); + } + + sha1_hash large_hash = ph.h.final(); + int piece_index = identify_data(large_hash, small_hash, m_current_slot); + + if (piece_index >= 0) have_piece = piece_index; + + if (piece_index != m_current_slot + && piece_index >= 0) + m_out_of_place = true; + + TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); + + const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; + const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; + + // check if this piece should be swapped with any other slot + // this section will ensure that the storage is correctly sorted + // libtorrent will never leave the storage in a state that + // requires this sorting, but other clients may. + + // example of worst case: + // | m_current_slot = 5 + // V + // +---+- - - +---+- - - +---+- - + // | x | | 5 | | 3 | <- piece data in slots + // +---+- - - +---+- - - +---+- - + // 3 y 5 <- slot index + + // in this example, the data in the m_current_slot (5) + // is piece 3. It has to be moved into slot 3. The data + // in slot y (piece 5) should be moved into the m_current_slot. + // and the data in slot 3 (piece x) should be moved to slot y. + + // there are three possible cases. + // 1. There's another piece that should be placed into this slot + // 2. This piece should be placed into another slot. + // 3. There's another piece that should be placed into this slot + // and this piece should be placed into another slot + + // swap piece_index with this slot + + // case 1 + if (this_should_move && !other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_slot = piece_index; + TORRENT_ASSERT(other_slot >= 0); + int other_piece = m_slot_to_piece[other_slot]; + + m_slot_to_piece[other_slot] = piece_index; + m_slot_to_piece[m_current_slot] = other_piece; + m_piece_to_slot[piece_index] = piece_index; + if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; + + if (other_piece == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_free_slots.erase(i); + m_free_slots.push_back(m_current_slot); + } + } + + bool ret = false; + m_last_piece = piece_index; + if (other_piece >= 0) + ret |= m_storage->swap_slots(other_slot, m_current_slot); + else + ret |= m_storage->move_slot(m_current_slot, other_slot); + + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + // case 2 + else if (!this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_piece = m_current_slot; + const int other_slot = m_piece_to_slot[other_piece]; + TORRENT_ASSERT(other_slot >= 0); + + m_slot_to_piece[m_current_slot] = other_piece; + m_slot_to_piece[other_slot] = piece_index; + m_piece_to_slot[other_piece] = m_current_slot; + + if (piece_index == unassigned + && m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(other_slot); + + bool ret = false; + if (piece_index >= 0) + { + m_piece_to_slot[piece_index] = other_slot; + ret |= m_storage->swap_slots(other_slot, m_current_slot); + } + else + { + ret |= m_storage->move_slot(other_slot, m_current_slot); + + } + m_last_piece = other_piece; + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else if (this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + TORRENT_ASSERT(piece_index >= 0); + + const int piece1 = m_slot_to_piece[piece_index]; + const int piece2 = m_current_slot; + const int slot1 = piece_index; + const int slot2 = m_piece_to_slot[piece2]; + + TORRENT_ASSERT(slot1 >= 0); + TORRENT_ASSERT(slot2 >= 0); + TORRENT_ASSERT(piece2 >= 0); + + if (slot1 == slot2) + { + // this means there are only two pieces involved in the swap + TORRENT_ASSERT(piece1 >= 0); + + // movement diagram: + // +-------------------------------+ + // | | + // +--> slot1 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[m_current_slot] = piece1; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[piece1] = m_current_slot; + + TORRENT_ASSERT(piece1 == m_current_slot); + TORRENT_ASSERT(piece_index == slot1); + + m_last_piece = piece_index; + m_storage->swap_slots(m_current_slot, slot1); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else + { + TORRENT_ASSERT(slot1 != slot2); + TORRENT_ASSERT(piece1 != piece2); + + // movement diagram: + // +-----------------------------------------+ + // | | + // +--> slot1 --> slot2 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[slot2] = piece1; + m_slot_to_piece[m_current_slot] = piece2; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[m_current_slot] = piece2; + + if (piece1 == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), slot1); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == internal_storage_mode_compact_deprecated) + { + m_free_slots.erase(i); + m_free_slots.push_back(slot2); + } + } + + bool ret = false; + if (piece1 >= 0) + { + m_piece_to_slot[piece1] = slot2; + ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2); + } + else + { + ret |= m_storage->move_slot(m_current_slot, slot1); + ret |= m_storage->move_slot(slot2, m_current_slot); + } + + m_last_piece = piece_index; + if (ret) return skip_file(); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + else + { + TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated); + TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); + + // the slot was identified as piece 'piece_index' + if (piece_index != unassigned) + m_piece_to_slot[piece_index] = m_current_slot; + else if (m_storage_mode == internal_storage_mode_compact_deprecated) + m_free_slots.push_back(m_current_slot); + + m_slot_to_piece[m_current_slot] = piece_index; + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + + if (piece_index == unassigned) + { + // the data did not match any piece. Maybe we're reading + // from a sparse region, see if we are and skip + if (m_current_slot == m_files.num_pieces() -1) return 0; + + int next_slot = m_storage->sparse_end(m_current_slot + 1); + if (next_slot > m_current_slot + 1) return next_slot - m_current_slot; + } + + return 0; + } + + void piece_manager::switch_to_full_mode() + { + TORRENT_ASSERT(m_storage_mode == internal_storage_mode_compact_deprecated); + TORRENT_ASSERT(m_unallocated_slots.empty()); + // we have allocated all slots, switch to + // full allocation mode in order to free + // some unnecessary memory. + m_storage_mode = storage_mode_sparse; + std::vector().swap(m_unallocated_slots); + std::vector().swap(m_free_slots); + std::vector().swap(m_piece_to_slot); + std::vector().swap(m_slot_to_piece); + } + + int piece_manager::allocate_slot_for_piece(int piece_index) + { + mutex::scoped_lock lock(m_mutex); + + if (m_storage_mode != internal_storage_mode_compact_deprecated) return piece_index; + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(piece_index >= 0); + TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size()); + TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size()); + + int slot_index = m_piece_to_slot[piece_index]; + + if (slot_index != has_no_slot) + { + TORRENT_ASSERT(slot_index >= 0); + TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + if (m_free_slots.empty()) + { + allocate_slots_impl(1, lock); + TORRENT_ASSERT(!m_free_slots.empty()); + } + + std::vector::iterator iter( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , piece_index)); + + if (iter == m_free_slots.end()) + { + TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned); + TORRENT_ASSERT(!m_free_slots.empty()); + iter = m_free_slots.end() - 1; + + // special case to make sure we don't use the last slot + // when we shouldn't, since it's smaller than ordinary slots + if (*iter == m_files.num_pieces() - 1 && piece_index != *iter) + { + if (m_free_slots.size() == 1) + allocate_slots_impl(1, lock); + TORRENT_ASSERT(m_free_slots.size() > 1); + // assumes that all allocated slots + // are put at the end of the free_slots vector + iter = m_free_slots.end() - 1; + } + } + + slot_index = *iter; + m_free_slots.erase(iter); + + TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned); + + m_slot_to_piece[slot_index] = piece_index; + m_piece_to_slot[piece_index] = slot_index; + + // there is another piece already assigned to + // the slot we are interested in, swap positions + if (slot_index != piece_index + && m_slot_to_piece[piece_index] >= 0) + { + int piece_at_our_slot = m_slot_to_piece[piece_index]; + TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index); + + std::swap( + m_slot_to_piece[piece_index] + , m_slot_to_piece[slot_index]); + + std::swap( + m_piece_to_slot[piece_index] + , m_piece_to_slot[piece_at_our_slot]); + + m_last_piece = piece_index; + m_storage->move_slot(piece_index, slot_index); + + TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index); + TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index); + + slot_index = piece_index; + +#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG + debug_log(); +#endif + } + TORRENT_ASSERT(slot_index >= 0); + TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size()); + + if (m_free_slots.empty() && m_unallocated_slots.empty()) + switch_to_full_mode(); + + return slot_index; + } + + bool piece_manager::allocate_slots_impl(int num_slots, mutex::scoped_lock& l + , bool abort_on_disk) + { + TORRENT_ASSERT(num_slots > 0); + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + INVARIANT_CHECK; +#endif + + TORRENT_ASSERT(!m_unallocated_slots.empty()); + TORRENT_ASSERT(m_storage_mode == internal_storage_mode_compact_deprecated); + + bool written = false; + + for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) + { + int pos = m_unallocated_slots.front(); + TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated); + TORRENT_ASSERT(m_piece_to_slot[pos] != pos); + + int new_free_slot = pos; + if (m_piece_to_slot[pos] != has_no_slot) + { + m_last_piece = pos; + new_free_slot = m_piece_to_slot[pos]; + m_storage->move_slot(new_free_slot, pos); + m_slot_to_piece[pos] = pos; + m_piece_to_slot[pos] = pos; + written = true; + } + m_unallocated_slots.erase(m_unallocated_slots.begin()); + m_slot_to_piece[new_free_slot] = unassigned; + m_free_slots.push_back(new_free_slot); + if (abort_on_disk && written) break; + } + + TORRENT_ASSERT(m_free_slots.size() > 0); + return written; + } + + int piece_manager::slot_for(int piece) const + { + if (m_storage_mode != internal_storage_mode_compact_deprecated) return piece; + // this happens in seed mode, where we skip checking fastresume + if (m_piece_to_slot.empty()) return piece; + TORRENT_ASSERT(piece < int(m_piece_to_slot.size())); + TORRENT_ASSERT(piece >= 0); + return m_piece_to_slot[piece]; + } + + int piece_manager::piece_for(int slot) const + { + if (m_storage_mode != internal_storage_mode_compact_deprecated) return slot; + TORRENT_ASSERT(slot < int(m_slot_to_piece.size())); + TORRENT_ASSERT(slot >= 0); + return m_slot_to_piece[slot]; + } + +#if TORRENT_USE_INVARIANT_CHECKS + void piece_manager::check_invariant() const + { + TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + + if (m_unallocated_slots.empty() + && m_free_slots.empty() + && m_state == state_finished) + { + TORRENT_ASSERT(m_storage_mode != internal_storage_mode_compact_deprecated + || m_files.num_pieces() == 0); + } + + if (m_storage_mode != internal_storage_mode_compact_deprecated) + { + TORRENT_ASSERT(m_unallocated_slots.empty()); + TORRENT_ASSERT(m_free_slots.empty()); + } + + if (m_storage_mode != internal_storage_mode_compact_deprecated + && m_state != state_expand_pieces + && m_state != state_full_check) + { + TORRENT_ASSERT(m_piece_to_slot.empty()); + TORRENT_ASSERT(m_slot_to_piece.empty()); + } + else + { + if (m_piece_to_slot.empty()) return; + + TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces()); + TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces()); + + for (std::vector::const_iterator i = m_free_slots.begin(); + i != m_free_slots.end(); ++i) + { + TORRENT_ASSERT(*i < (int)m_slot_to_piece.size()); + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned); + TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i) + == m_free_slots.end()); + } + + for (std::vector::const_iterator i = m_unallocated_slots.begin(); + i != m_unallocated_slots.end(); ++i) + { + TORRENT_ASSERT(*i < (int)m_slot_to_piece.size()); + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated); + TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i) + == m_unallocated_slots.end()); + } + + for (int i = 0; i < m_files.num_pieces(); ++i) + { + // Check domain of piece_to_slot's elements + if (m_piece_to_slot[i] != has_no_slot) + { + TORRENT_ASSERT(m_piece_to_slot[i] >= 0); + TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size()); + } + + // Check domain of slot_to_piece's elements + if (m_slot_to_piece[i] != unallocated + && m_slot_to_piece[i] != unassigned) + { + TORRENT_ASSERT(m_slot_to_piece[i] >= 0); + TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + } + + // do more detailed checks on piece_to_slot + if (m_piece_to_slot[i] >= 0) + { + TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i); + if (m_piece_to_slot[i] != i) + { + TORRENT_ASSERT(m_slot_to_piece[i] == unallocated); + } + } + else + { + TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot); + } + + // do more detailed checks on slot_to_piece + + if (m_slot_to_piece[i] >= 0) + { + TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i); +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT( + std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) == m_unallocated_slots.end() + ); + TORRENT_ASSERT( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) == m_free_slots.end() + ); +#endif + } + else if (m_slot_to_piece[i] == unallocated) + { +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT(m_unallocated_slots.empty() + || (std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) != m_unallocated_slots.end()) + ); +#endif + } + else if (m_slot_to_piece[i] == unassigned) + { +#ifdef TORRENT_STORAGE_DEBUG + TORRENT_ASSERT( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) != m_free_slots.end() + ); +#endif + } + else + { + TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid"); + } + } + } + } + +#endif +} // namespace libtorrent + diff --git a/apps/Launcher/ext/libtorrent/src/string_util.cpp b/apps/Launcher/ext/libtorrent/src/string_util.cpp new file mode 100644 index 0000000000..576a1ca0a2 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/string_util.cpp @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/string_util.hpp" +#include "libtorrent/random.hpp" + +#include // for malloc/free +#include // for strcpy/strlen + +namespace libtorrent +{ + + bool is_alpha(char c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + bool is_digit(char c) + { + return c >= '0' && c <= '9'; + } + + bool is_print(char c) + { + return c >= 32 && c < 127; + } + + bool is_space(char c) + { + const static char* ws = " \t\n\r\f\v"; + return strchr(ws, c) != 0; + } + + char to_lower(char c) + { + return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; + } + + int split_string(char const** tags, int buf_size, char* in) + { + int ret = 0; + char* i = in; + for (;*i; ++i) + { + if (!is_print(*i) || is_space(*i)) + { + *i = 0; + if (ret == buf_size) return ret; + continue; + } + if (i == in || i[-1] == 0) + { + tags[ret++] = i; + } + } + return ret; + } + + bool string_begins_no_case(char const* s1, char const* s2) + { + while (*s1 != 0) + { + if (to_lower(*s1) != to_lower(*s2)) return false; + ++s1; + ++s2; + } + return true; + } + + bool string_equal_no_case(char const* s1, char const* s2) + { + while (to_lower(*s1) == to_lower(*s2)) + { + if (*s1 == 0) return true; + ++s1; + ++s2; + } + return false; + } + + // generate a url-safe random string + void url_random(char* begin, char* end) + { + // http-accepted characters: + // excluding ', since some buggy trackers don't support that + static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz-_.!~*()"; + + // the random number + while (begin != end) + *begin++ = printable[random() % (sizeof(printable)-1)]; + } + + char* allocate_string_copy(char const* str) + { + if (str == 0) return 0; + char* tmp = (char*)malloc(strlen(str) + 1); + if (tmp == 0) return 0; + strcpy(tmp, str); + return tmp; + } + + // 8-byte align pointer + void* align_pointer(void* p) + { + int offset = uintptr_t(p) & 0x7; + // if we're already aligned, don't do anything + if (offset == 0) return p; + + // offset is how far passed the last aligned address + // we are. We need to go forward to the next aligned + // one. Since aligned addresses are 8 bytes apart, add + // 8 - offset. + return static_cast(p) + (8 - offset); + } + + char* string_tokenize(char* last, char sep, char** next) + { + if (last == 0) return 0; + if (last[0] == '"') + { + *next = strchr(last + 1, '"'); + // consume the actual separator as well. + if (*next != NULL) + *next = strchr(*next, sep); + } + else + { + *next = strchr(last, sep); + } + if (*next == 0) return last; + **next = 0; + ++(*next); + while (**next == sep && **next) ++(*next); + return last; + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/thread.cpp b/apps/Launcher/ext/libtorrent/src/thread.cpp new file mode 100644 index 0000000000..c22aeb0e8d --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/thread.cpp @@ -0,0 +1,173 @@ +/* + +Copyright (c) 2010-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/thread.hpp" +#include "libtorrent/assert.hpp" + +#ifdef TORRENT_BEOS +#include +#endif + +#ifdef BOOST_HAS_PTHREADS +#include // for gettimeofday() +#include +#endif + +namespace libtorrent +{ + void sleep(int milliseconds) + { +#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + Sleep(milliseconds); +#elif defined TORRENT_BEOS + snooze_until(system_time() + boost::int64_t(milliseconds) * 1000, B_SYSTEM_TIMEBASE); +#else + usleep(milliseconds * 1000); +#endif + } + +#ifdef BOOST_HAS_PTHREADS + + condition_variable::condition_variable() + { + pthread_cond_init(&m_cond, 0); + } + + condition_variable::~condition_variable() + { + pthread_cond_destroy(&m_cond); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + // wow, this is quite a hack + pthread_cond_wait(&m_cond, (::pthread_mutex_t*)&l.mutex()); + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + + struct timeval tv; + struct timespec ts; + gettimeofday(&tv, NULL); + boost::uint64_t microseconds = tv.tv_usec + total_microseconds(rel_time) % 1000000; + ts.tv_nsec = (microseconds % 1000000) * 1000; + ts.tv_sec = tv.tv_sec + total_seconds(rel_time) + microseconds / 1000000; + + // wow, this is quite a hack + pthread_cond_timedwait(&m_cond, (::pthread_mutex_t*)&l.mutex(), &ts); + } + + void condition_variable::notify_all() + { + pthread_cond_broadcast(&m_cond); + } +#elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN + condition_variable::condition_variable() + : m_num_waiters(0) + { + m_sem = CreateSemaphore(0, 0, INT_MAX, 0); + } + + condition_variable::~condition_variable() + { + CloseHandle(m_sem); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + WaitForSingleObject(m_sem, INFINITE); + l.lock(); + --m_num_waiters; + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + WaitForSingleObject(m_sem, total_milliseconds(rel_time)); + l.lock(); + --m_num_waiters; + } + + void condition_variable::notify_all() + { + ReleaseSemaphore(m_sem, m_num_waiters, 0); + } +#elif defined TORRENT_BEOS + condition_variable::condition_variable() + : m_num_waiters(0) + { + m_sem = create_sem(0, 0); + } + + condition_variable::~condition_variable() + { + delete_sem(m_sem); + } + + void condition_variable::wait(mutex::scoped_lock& l) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + acquire_sem(m_sem); + l.lock(); + --m_num_waiters; + } + + void condition_variable::wait_for(mutex::scoped_lock& l, time_duration rel_time) + { + TORRENT_ASSERT(l.locked()); + ++m_num_waiters; + l.unlock(); + acquire_sem_etc(m_sem, 1, B_RELATIVE_TIMEOUT, total_microseconds(rel_time)); + l.lock(); + --m_num_waiters; + } + + void condition_variable::notify_all() + { + release_sem_etc(m_sem, m_num_waiters, 0); + } +#else +#error not implemented +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/time.cpp b/apps/Launcher/ext/libtorrent/src/time.cpp new file mode 100644 index 0000000000..bea93856c6 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/time.cpp @@ -0,0 +1,254 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +#ifndef _WIN32 +#include +#endif + +namespace libtorrent +{ + namespace aux + { + // used to cache the current time + // every 100 ms. This is cheaper + // than a system call and can be + // used where more accurate time + // is not necessary + ptime g_current_time; + } + + TORRENT_EXPORT ptime const& time_now() { return aux::g_current_time; } + + char const* time_now_string() + { + static const ptime start = time_now_hires(); + static char ret[200]; + int t = total_milliseconds(time_now_hires() - start); + int h = t / 1000 / 60 / 60; + t -= h * 60 * 60 * 1000; + int m = t / 1000 / 60; + t -= m * 60 * 1000; + int s = t / 1000; + t -= s * 1000; + int ms = t; + snprintf(ret, sizeof(ret), "%02d:%02d:%02d.%03d", h, m, s, ms); + return ret; + } + + std::string log_time() + { + static const ptime start = time_now_hires(); + char ret[200]; + snprintf(ret, sizeof(ret), "%" PRId64, total_microseconds(time_now_hires() - start)); + return ret; + } +} + +#if defined TORRENT_USE_BOOST_DATE_TIME + +#include + +namespace libtorrent +{ + ptime time_now_hires() + { return boost::date_time::microsec_clock::universal_time(); } + ptime min_time() + { return boost::posix_time::ptime(boost::posix_time::min_date_time); } + ptime max_time() + { return boost::posix_time::ptime(boost::posix_time::max_date_time); } + time_duration seconds(boost::int64_t s) { return boost::posix_time::seconds(s); } + time_duration milliseconds(boost::int64_t s) { return boost::posix_time::milliseconds(s); } + time_duration microsec(boost::int64_t s) { return boost::posix_time::microsec(s); } + time_duration minutes(boost::int64_t s) { return boost::posix_time::minutes(s); } + time_duration hours(boost::int64_t s) { return boost::posix_time::hours(s); } + + boost::int64_t total_seconds(time_duration td) + { return td.total_seconds(); } + boost::int64_t total_milliseconds(time_duration td) + { return td.total_milliseconds(); } + boost::int64_t total_microseconds(time_duration td) + { return td.total_microseconds(); } +} + +#else // TORRENT_USE_BOOST_DATE_TIME + +namespace libtorrent +{ + ptime min_time() { return ptime(0); } + ptime max_time() { return ptime((std::numeric_limits::max)()); } +} + +#if defined TORRENT_USE_ABSOLUTE_TIME + +#include +#include +#include "libtorrent/assert.hpp" + +// high precision timer for darwin intel and ppc + +namespace libtorrent +{ + ptime time_now_hires() + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + mach_timebase_info(&timebase_info); + boost::uint64_t at = mach_absolute_time(); + // make sure we don't overflow + TORRENT_ASSERT((at >= at / 1000 * timebase_info.numer / timebase_info.denom) + || (at < 0 && at < at / 1000 * timebase_info.numer / timebase_info.denom)); + return ptime(at / 1000 * timebase_info.numer / timebase_info.denom); + } +} +#elif defined TORRENT_USE_QUERY_PERFORMANCE_TIMER + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + boost::int64_t performance_counter_to_microseconds(boost::int64_t pc) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); + +#ifdef TORRENT_DEBUG + // make sure we don't overflow + boost::int64_t ret = (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + TORRENT_ASSERT((pc >= 0 && pc >= ret) || (pc < 0 && pc < ret)); +#endif + return ((pc * 1000 + performace_counter_frequency.QuadPart / 2) / performace_counter_frequency.QuadPart) * 1000; + } + + boost::int64_t microseconds_to_performance_counter(boost::int64_t ms) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); +#ifdef TORRENT_DEBUG + // make sure we don't overflow + boost::int64_t ret = (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + TORRENT_ASSERT((ms >= 0 && ms <= ret) + || (ms < 0 && ms > ret)); +#endif + return (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + } + + ptime time_now_hires() + { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ptime(now.QuadPart); + } + + boost::int64_t total_seconds(time_duration td) + { + return boost::int64_t(performance_counter_to_microseconds(td.diff) + / 1000000); + } + boost::int64_t total_milliseconds(time_duration td) + { + return boost::uint64_t(performance_counter_to_microseconds(td.diff) + / 1000); + } + boost::int64_t total_microseconds(time_duration td) + { + return performance_counter_to_microseconds(td.diff); + } + + time_duration microsec(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter(s)); + } + time_duration milliseconds(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000)); + } + time_duration seconds(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000)); + } + time_duration minutes(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000 * 60)); + } + time_duration hours(boost::int64_t s) + { + return time_duration(microseconds_to_performance_counter( + s * 1000000 * 60 * 60)); + } +} + +#elif defined TORRENT_USE_CLOCK_GETTIME + +#include +#include "libtorrent/assert.hpp" + +namespace libtorrent +{ + ptime time_now_hires() + { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ptime(boost::uint64_t(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000); + } +} + +#elif defined TORRENT_USE_SYSTEM_TIME + +#include + +namespace libtorrent +{ + ptime time_now_hires() + { return ptime(system_time()); } +} + +#endif // TORRENT_USE_SYSTEM_TIME + +#endif // TORRENT_USE_BOOST_DATE_TIME + diff --git a/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp b/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp new file mode 100644 index 0000000000..8a126e4410 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/timestamp_history.cpp @@ -0,0 +1,106 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#include "libtorrent/timestamp_history.hpp" + +namespace libtorrent { + +enum +{ + TIME_MASK = 0xffffffff +}; +// defined in utp_stream.cpp +bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs + , boost::uint32_t mask); + +boost::uint32_t timestamp_history::add_sample(boost::uint32_t sample, bool step) +{ + if (!m_initialized) + { + for (int i = 0; i < history_size; ++i) + m_history[i] = sample; + m_base = sample; + m_initialized = true; + } + + ++m_num_samples; + + // if sample is less than base, update the base + // and update the history entry (because it will + // be less than that too) + if (compare_less_wrap(sample, m_base, TIME_MASK)) + { + m_base = sample; + m_history[m_index] = sample; + } + // if sample is less than our history entry, update it + else if (compare_less_wrap(sample, m_history[m_index], TIME_MASK)) + { + m_history[m_index] = sample; + } + + boost::uint32_t ret = sample - m_base; + + // don't step base delay history unless we have at least 120 + // samples. Anything less would suggest that the connection is + // essentially idle and the samples are probably not very reliable + if (step && m_num_samples > 120) + { + m_num_samples = 0; + m_index = (m_index + 1) % history_size; + + m_history[m_index] = sample; + // update m_base + m_base = sample; + for (int i = 0; i < history_size; ++i) + { + if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) + m_base = m_history[i]; + } + } + return ret; +} + +void timestamp_history::adjust_base(int change) +{ + TORRENT_ASSERT(m_initialized); + m_base += change; + // make sure this adjustment sticks by updating all history slots + for (int i = 0; i < history_size; ++i) + { + if (compare_less_wrap(m_history[i], m_base, TIME_MASK)) + m_history[i] = m_base; + } +} + +} diff --git a/apps/Launcher/ext/libtorrent/src/torrent.cpp b/apps/Launcher/ext/libtorrent/src/torrent.cpp new file mode 100644 index 0000000000..ecda0668ab --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent.cpp @@ -0,0 +1,9598 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include // for numeric_limits + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/http_seed_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/assert.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/gzip.hpp" // for inflate_gzip +#include "libtorrent/random.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/alloca.hpp" + +#ifdef TORRENT_USE_OPENSSL +#include "libtorrent/ssl_stream.hpp" +#include +#include +#if BOOST_VERSION >= 104700 +#include +#endif // BOOST_VERSION +#endif // TORRENT_USE_OPENSSL + +using namespace libtorrent; +using boost::tuples::tuple; +using boost::tuples::get; +using boost::tuples::make_tuple; +using libtorrent::aux::session_impl; + +namespace +{ + struct find_peer_by_ip + { + find_peer_by_ip(tcp::endpoint const& a, const torrent* t) + : ip(a) + , tor(t) + { TORRENT_ASSERT(t != 0); } + + bool operator()(session_impl::connection_map::value_type const& c) const + { + tcp::endpoint const& sender = c->remote(); + if (sender.address() != ip.address()) return false; + if (tor != c->associated_torrent().lock().get()) return false; + return true; + } + + tcp::endpoint const& ip; + torrent const* tor; + }; + + struct peer_by_id + { + peer_by_id(const peer_id& i): pid(i) {} + + bool operator()(session_impl::connection_map::value_type const& p) const + { + if (p->pid() != pid) return false; + // have a special case for all zeros. We can have any number + // of peers with that pid, since it's used to indicate no pid. + if (pid.is_all_zeros()) return false; + return true; + } + + peer_id const& pid; + }; +} + +namespace libtorrent +{ + int root2(int x) + { + int ret = 0; + x >>= 1; + while (x > 0) + { + // if this assert triggers, the block size + // is not an even 2 exponent! + TORRENT_ASSERT(x == 1 || (x & 1) == 0); + ++ret; + x >>= 1; + } + return ret; + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + // defined in ut_pex.cpp + bool was_introduced_by(peer_plugin const*, tcp::endpoint const&); +#endif + + torrent::torrent( + session_impl& ses + , tcp::endpoint const& net_interface + , int block_size + , int seq + , add_torrent_params const& p + , sha1_hash const& info_hash) + : m_policy(this) + , m_total_uploaded(0) + , m_total_downloaded(0) + , m_started(time_now()) + , m_storage(0) + , m_num_connecting(0) + , m_tracker_timer(ses.m_io_service) + , m_ses(ses) + , m_host_resolver(ses.m_io_service) + , m_trackerid(p.trackerid) + , m_save_path(complete(p.save_path)) + , m_url(p.url) + , m_uuid(p.uuid) + , m_source_feed_url(p.source_feed_url) + , m_storage_constructor(p.storage) + , m_added_time(time(0)) + , m_completed_time(0) + , m_last_saved_resume(time(0)) + , m_last_seen_complete(0) + , m_swarm_last_seen_complete(0) + , m_num_verified(0) + , m_info_hash(info_hash) + , m_average_piece_time(0) + , m_piece_time_deviation(0) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_sequence_number(seq) + , m_upload_mode_time(0) + , m_state(torrent_status::checking_resume_data) + , m_storage_mode(p.storage_mode) + , m_announcing(false) + , m_waiting_tracker(false) + , m_seed_mode(false) + , m_active_time(0) + , m_last_working_tracker(-1) + , m_finished_time(0) + , m_sequential_download(false) + , m_got_tracker_response(false) + , m_connections_initialized(false) + , m_super_seeding(false) + , m_override_resume_data(p.flags & add_torrent_params::flag_override_resume_data) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + , m_resolving_country(false) + , m_resolve_countries(false) +#endif + , m_need_save_resume_data(true) + , m_seeding_time(0) + , m_time_scaler(0) + , m_max_uploads((1<<24)-1) + , m_save_resume_flags(0) + , m_num_uploads(0) + , m_block_size_shift(root2(block_size)) + , m_has_incoming(false) + , m_files_checked(false) + , m_queued_for_checking(false) + , m_max_connections((1<<24)-1) + , m_graceful_pause_mode(false) + , m_need_connect_boost(true) + , m_lsd_seq(0) + , m_magnet_link(false) + , m_apply_ip_filter(p.flags & add_torrent_params::flag_apply_ip_filter) + , m_merge_resume_trackers(p.flags & add_torrent_params::flag_merge_resume_trackers) + , m_padding(0) + , m_priority(0) + , m_complete(0xffffff) + , m_state_subscription(p.flags & add_torrent_params::flag_update_subscribe) + , m_in_state_updates(false) + , m_is_active_download(false) + , m_is_active_finished(false) + , m_ssl_torrent(false) + , m_deleted(false) + , m_moving_storage(false) + , m_inactive(false) + , m_incomplete(0xffffff) + , m_abort(false) + , m_announce_to_dht((p.flags & add_torrent_params::flag_paused) == 0) + , m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0) + , m_announce_to_lsd((p.flags & add_torrent_params::flag_paused) == 0) + , m_allow_peers((p.flags & add_torrent_params::flag_paused) == 0) + , m_upload_mode(p.flags & add_torrent_params::flag_upload_mode) + , m_auto_managed(p.flags & add_torrent_params::flag_auto_managed) + , m_share_mode(p.flags & add_torrent_params::flag_share_mode) + , m_last_download(0) + , m_last_scrape(0) + , m_last_upload(0) + , m_downloaded(0xffffff) + , m_interface_index(0) + , m_progress_ppm(0) + , m_inactive_counter(0) + , m_use_resume_save_path(p.flags & add_torrent_params::flag_use_resume_save_path) + { + // if there is resume data already, we don't need to trigger the initial save + // resume data + if (!p.resume_data.empty() && (p.flags & add_torrent_params::flag_override_resume_data) == 0) + m_need_save_resume_data = false; + +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = false; +#endif +#if TORRENT_USE_UNC_PATHS + m_save_path = canonicalize_path(m_save_path); +#endif + + if (!m_apply_ip_filter) ++m_ses.m_non_filtered_torrents; + + update_guage(); + + if (!p.ti || !p.ti->is_valid()) + { + // we don't have metadata for this torrent. We'll download + // it either through the URL passed in, or through a metadata + // extension. Make sure that when we save resume data for this + // torrent, we also save the metadata + m_magnet_link = true; + } + + if (!m_torrent_file) + m_torrent_file = (p.ti ? p.ti : new torrent_info(info_hash)); + + // add web seeds from add_torrent_params + for (std::vector::const_iterator i = p.url_seeds.begin() + , end(p.url_seeds.end()); i != end; ++i) + { + m_web_seeds.push_back(web_seed_entry(*i, web_seed_entry::url_seed)); + } + + m_trackers = m_torrent_file->trackers(); + if (m_torrent_file->is_valid()) + { + m_seed_mode = p.flags & add_torrent_params::flag_seed_mode; + m_connections_initialized = true; + m_block_size_shift = root2((std::min)(block_size, m_torrent_file->piece_length())); + } + else + { + if (!p.name.empty()) m_name.reset(new std::string(p.name)); + } + + if (!m_url.empty() && m_uuid.empty()) m_uuid = m_url; + + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("creating torrent: %s", torrent_file().name().c_str()); +#endif + m_net_interfaces.push_back(tcp::endpoint(net_interface.address(), 0)); + + m_file_priority = p.file_priorities; + + if (m_seed_mode) + m_verified.resize(m_torrent_file->num_pieces(), false); + + m_resume_data = p.resume_data; + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + INVARIANT_CHECK; + + if (p.flags & add_torrent_params::flag_sequential_download) + m_sequential_download = true; + + if (p.flags & add_torrent_params::flag_super_seeding) + m_super_seeding = true; + + set_max_uploads(p.max_uploads, false); + set_max_connections(p.max_connections, false); + set_upload_limit(p.upload_limit, false); + set_download_limit(p.download_limit, false); + + if (!m_name && !m_url.empty()) m_name.reset(new std::string(m_url)); + +#ifndef TORRENT_NO_DEPRECATE + if (p.tracker_url && std::strlen(p.tracker_url) > 0) + { + m_trackers.push_back(announce_entry(p.tracker_url)); + m_trackers.back().fail_limit = 0; + m_trackers.back().source = announce_entry::source_magnet_link; + m_torrent_file->add_tracker(p.tracker_url); + } +#endif + + for (std::vector::const_iterator i = p.trackers.begin() + , end(p.trackers.end()); i != end; ++i) + { + m_trackers.push_back(announce_entry(*i)); + m_trackers.back().fail_limit = 0; + m_trackers.back().source = announce_entry::source_magnet_link; + m_torrent_file->add_tracker(*i); + } + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + } + +#if 0 + + // NON BOTTLED VERSION. SUPPORTS PROGRESS REPORTING + + // since this download is not bottled, this callback will + // be called every time we receive another piece of the + // .torrent file + void torrent::on_torrent_download(error_code const& ec + , http_parser const& parser + , char const* data, int size) + { + if (m_abort) return; + + if (ec && ec != asio::error::eof) + { + set_error(ec, m_url); + pause(); + return; + } + + if (size > 0) + { + m_torrent_file_buf.insert(m_torrent_file_buf.end(), data, data + size); + if (parser.content_length() > 0) + set_progress_ppm(boost::int64_t(m_torrent_file_buf.size()) + * 1000000 / parser.content_length()); + } + + if (parser.header_finished() && parser.status_code() != 200) + { + set_error(error_code(parser.status_code(), get_http_category()), parser.message()); + pause(); + return; + } + + if (!ec) return; + + // if this was received with chunked encoding, we need to strip out + // the chunk headers + size = parser.collapse_chunk_headers((char*)&m_torrent_file_buf[0], m_torrent_file_buf.size()); + m_torrent_file_buf.resize(size); + + std::string const& encoding = parser.header("content-encoding"); + if ((encoding == "gzip" || encoding == "x-gzip") && m_torrent_file_buf.size()) + { + std::vector buf; + error_code ec; + inflate_gzip(&m_torrent_file_buf[0], m_torrent_file_buf.size() + , buf, 4 * 1024 * 1024, ex); + if (ec) + { + set_error(ec, m_url); + pause(); + std::vector().swap(m_torrent_file_buf); + return; + } + m_torrent_file_buf.swap(buf); + } + + // we're done! + error_code e; + intrusive_ptr tf(new torrent_info( + &m_torrent_file_buf[0], m_torrent_file_buf.size(), e)); + if (e) + { + set_error(e, m_url); + pause(); + std::vector().swap(m_torrent_file_buf); + return; + } + std::vector().swap(m_torrent_file_buf); + + // update our torrent_info object and move the + // torrent from the old info-hash to the new one + // as we replace the torrent_info object +#if TORRENT_USE_ASSERTS + int num_torrents = m_ses.m_torrents.size(); +#endif + // we're about to erase the session's reference to this + // torrent, create another reference + boost::shared_ptr me(shared_from_this()); + + m_ses.remove_torrent_impl(me, 0); + + m_torrent_file = tf; + + // now, we might already have this torrent in the session. + session_impl::torrent_map::iterator i = m_ses.m_torrents.find(m_torrent_file->info_hash()); + if (i != m_ses.m_torrents.end()) + { + if (!m_uuid.empty() && i->second->uuid().empty()) + i->second->set_uuid(m_uuid); + if (!m_url.empty() && i->second->url().empty()) + i->second->set_url(m_url); + if (!m_source_feed_url.empty() && i->second->source_feed_url().empty()) + i->second->set_source_feed_url(m_source_feed_url); + + // insert this torrent in the uuid index + if (!m_uuid.empty() || !m_url.empty()) + { + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, i->second)); + } + set_error(error_code(errors::duplicate_torrent, get_libtorrent_category()), ""); + abort(); + return; + } + + m_ses.m_torrents.insert(std::make_pair(m_torrent_file->info_hash(), me)); + if (!m_uuid.empty()) m_ses.m_uuids.insert(std::make_pair(m_uuid, me)); + + TORRENT_ASSERT(num_torrents == int(m_ses.m_torrents.size())); + + // if the user added any trackers while downloading the + // .torrent file, serge them into the new tracker list + std::vector new_trackers = m_torrent_file->trackers(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // if we already have this tracker, ignore it + if (std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) + continue; + + // insert the tracker ordered by tier + new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); + } + m_trackers.swap(new_trackers); + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + state_updated(); + + set_state(torrent_status::downloading); + + m_override_resume_data = true; + init(); + } +#else // if 0 + + void torrent::on_torrent_download(error_code const& ec + , http_parser const& parser, char const* data, int size) + { + if (m_abort) return; + + if (ec && ec != asio::error::eof) + { + set_error(ec, m_url); + pause(); + return; + } + + if (parser.status_code() != 200) + { + // #error there should really be an error code category for HTTP + set_error(errors::http_error, parser.message()); + pause(); + return; + } + + error_code e; + intrusive_ptr tf(new torrent_info(data, size, e)); + if (e) + { + set_error(e, m_url); + pause(); + return; + } + + // update our torrent_info object and move the + // torrent from the old info-hash to the new one + // as we replace the torrent_info object +#if TORRENT_USE_ASSERTS + int num_torrents = m_ses.m_torrents.size(); +#endif + + // we're about to erase the session's reference to this + // torrent, create another reference + boost::shared_ptr me(shared_from_this()); + + m_ses.remove_torrent_impl(me, 0); + + if (alerts().should_post()) + alerts().post_alert(torrent_update_alert(get_handle(), info_hash(), tf->info_hash())); + + m_torrent_file = tf; + m_info_hash = tf->info_hash(); + + // now, we might already have this torrent in the session. + session_impl::torrent_map::iterator i = m_ses.m_torrents.find(m_torrent_file->info_hash()); + if (i != m_ses.m_torrents.end()) + { + if (!m_uuid.empty() && i->second->uuid().empty()) + i->second->set_uuid(m_uuid); + if (!m_url.empty() && i->second->url().empty()) + i->second->set_url(m_url); + if (!m_source_feed_url.empty() && i->second->source_feed_url().empty()) + i->second->set_source_feed_url(m_source_feed_url); + + // insert this torrent in the uuid index + if (!m_uuid.empty() || !m_url.empty()) + { + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, i->second)); + } + set_error(error_code(errors::duplicate_torrent, get_libtorrent_category()), ""); + abort(); + return; + } + + m_ses.m_torrents.insert(std::make_pair(m_torrent_file->info_hash(), me)); + if (!m_uuid.empty()) m_ses.m_uuids.insert(std::make_pair(m_uuid, me)); + + TORRENT_ASSERT(num_torrents == int(m_ses.m_torrents.size())); + + // if the user added any trackers while downloading the + // .torrent file, serge them into the new tracker list + std::vector new_trackers = m_torrent_file->trackers(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // if we already have this tracker, ignore it + if (std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::url, _1) == i->url) != new_trackers.end()) + continue; + + // insert the tracker ordered by tier + new_trackers.insert(std::find_if(new_trackers.begin(), new_trackers.end() + , boost::bind(&announce_entry::tier, _1) >= i->tier), *i); + } + m_trackers.swap(new_trackers); + +#ifndef TORRENT_DISABLE_ENCRYPTION + hasher h; + h.update("req2", 4); + h.update((char*)&m_torrent_file->info_hash()[0], 20); + m_obfuscated_hash = h.final(); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + state_updated(); + + set_state(torrent_status::downloading); + + m_override_resume_data = true; + init(); + } + +#endif // if 0 + + void torrent::leave_seed_mode(bool seed) + { + if (!m_seed_mode) return; + + if (!seed) + { + // this means the user promised we had all the + // files, but it turned out we didn't. This is + // an error. + + // TODO: 2 post alert + +#if defined TORRENT_ERROR_LOGGING + debug_log("*** FAILED SEED MODE, rechecking"); +#endif + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** LEAVING SEED MODE (%s)", seed ? "as seed" : "as non-seed"); +#endif + m_seed_mode = false; + // seed is false if we turned out not + // to be a seed after all + if (!seed) + { + set_state(torrent_status::downloading); + force_recheck(); + } + m_num_verified = 0; + m_verified.clear(); + } + + void torrent::verified(int piece) + { + TORRENT_ASSERT(piece < int(m_verified.size())); + TORRENT_ASSERT(piece >= 0); + TORRENT_ASSERT(m_verified.get_bit(piece) == false); + ++m_num_verified; + m_verified.set_bit(piece); + } + + void torrent::start() + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("starting torrent"); +#endif + if (!m_seed_mode) + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); + + if (!m_resume_data.empty()) + { + int pos; + error_code ec; + if (lazy_bdecode(&m_resume_data[0], &m_resume_data[0] + + m_resume_data.size(), m_resume_entry, ec, &pos) != 0) + { + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("resume data rejected: %s pos: %d", ec.message().c_str(), pos); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), ec)); + } + } + } + + if (!m_torrent_file->is_valid() && !m_url.empty()) + { + // we need to download the .torrent file from m_url + start_download_url(); + } + else if (m_torrent_file->is_valid()) + { + init(); + } + else + { + // we need to start announcing since we don't have any + // metadata. To receive peers to ask for it. + set_state(torrent_status::downloading_metadata); + start_announcing(); + } + } + + bool torrent::is_active_download() const + { + return (m_state == torrent_status::downloading + || m_state == torrent_status::downloading_metadata) + && m_allow_peers + && !m_abort; + } + + bool torrent::is_active_finished() const + { + return (m_state == torrent_status::finished + || m_state == torrent_status::seeding) + && m_allow_peers + && !m_abort; + } + + void torrent::update_guage() + { + bool is_active_download = (m_state == torrent_status::downloading + || m_state == torrent_status::downloading_metadata) + && m_allow_peers + && !m_abort; + + bool is_active_finished = (m_state == torrent_status::finished + || m_state == torrent_status::seeding) + && m_allow_peers + && !m_abort; + + // update finished and downloading counters + if (is_active_download != m_is_active_download) + { + if (is_active_download) + m_ses.inc_active_downloading(); + else + m_ses.dec_active_downloading(); + m_is_active_download = is_active_download; + } + + if (is_active_finished != m_is_active_finished) + { + if (is_active_finished) + m_ses.inc_active_finished(); + else + m_ses.dec_active_finished(); + m_is_active_finished = is_active_finished; + } + } + + void torrent::start_download_url() + { + TORRENT_ASSERT(!m_url.empty()); + TORRENT_ASSERT(!m_torrent_file->is_valid()); + boost::shared_ptr conn( + new http_connection(m_ses.m_io_service, m_ses.m_half_open + , boost::bind(&torrent::on_torrent_download, shared_from_this() + , _1, _2, _3, _4) + , true // bottled + , m_ses.settings().max_http_recv_buffer_size // bottled buffer size + , http_connect_handler() + , http_filter_handler() +#ifdef TORRENT_USE_OPENSSL + , m_ssl_ctx.get() +#endif + )); + + conn->get(m_url, seconds(30), 0, &m_ses.proxy() + , 5, m_ses.m_settings.user_agent); + set_state(torrent_status::downloading_metadata); + } + + void torrent::set_apply_ip_filter(bool b) + { + if (b == m_apply_ip_filter) return; + if (b) + { + TORRENT_ASSERT(m_ses.m_non_filtered_torrents > 0); + --m_ses.m_non_filtered_torrents; + } + else + { + ++m_ses.m_non_filtered_torrents; + } + m_apply_ip_filter = b; + m_policy.ip_filter_updated(); + state_updated(); + } + +#ifndef TORRENT_DISABLE_DHT + bool torrent::should_announce_dht() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_ses.m_listen_sockets.empty()) return false; + + if (!m_ses.m_dht) return false; + if (m_torrent_file->is_valid() && !m_files_checked) return false; + if (!m_announce_to_dht) return false; + if (!m_allow_peers) return false; + + // if we don't have the metadata, and we're waiting + // for a web server to serve it to us, no need to announce + // because the info-hash is just the URL hash + if (!m_torrent_file->is_valid() && !m_url.empty()) return false; + + // don't announce private torrents + if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false; + if (m_trackers.empty()) return true; + if (!settings().use_dht_as_fallback) return true; + + int verified_trackers = 0; + for (std::vector::const_iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + if (i->verified) ++verified_trackers; + + return verified_trackers == 0; + } + +#endif + + torrent::~torrent() + { + if (!m_apply_ip_filter) + { + TORRENT_ASSERT(m_ses.m_non_filtered_torrents > 0); + --m_ses.m_non_filtered_torrents; + m_apply_ip_filter = true; + } + + TORRENT_ASSERT(m_ses.is_network_thread()); + // The invariant can't be maintained here, since the torrent + // is being destructed, all weak references to it have been + // reset, which means that all its peers already have an + // invalidated torrent pointer (so it cannot be verified to be correct) + + // i.e. the invariant can only be maintained if all connections have + // been closed by the time the torrent is destructed. And they are + // supposed to be closed. So we can still do the invariant check. + + INVARIANT_CHECK; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("DESTRUCTING TORRENT"); +#endif + + TORRENT_ASSERT(m_abort); + TORRENT_ASSERT(m_connections.empty()); + if (!m_connections.empty()) + disconnect_all(errors::torrent_aborted); + } + + void torrent::read_piece(int piece) + { + if (m_abort) + { + // failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + return; + } + + TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); + int piece_size = m_torrent_file->piece_size(piece); + int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); + + // if blocks_in_piece is 0, rp will leak + TORRENT_ASSERT(blocks_in_piece > 0); + TORRENT_ASSERT(piece_size > 0); + + read_piece_struct* rp = new read_piece_struct; + rp->piece_data.reset(new (std::nothrow) char[piece_size]); + rp->blocks_left = 0; + rp->fail = false; + + peer_request r; + r.piece = piece; + r.start = 0; + for (int i = 0; i < blocks_in_piece; ++i, r.start += block_size()) + { + r.length = (std::min)(piece_size - r.start, block_size()); + filesystem().async_read(r, boost::bind(&torrent::on_disk_read_complete + , shared_from_this(), _1, _2, r, rp)); + ++rp->blocks_left; + } + } + + void torrent::send_share_mode() + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + bt_peer_connection* p = (bt_peer_connection*)*i; + p->write_share_mode(); + } +#endif + } + + void torrent::send_upload_only() + { +#ifndef TORRENT_DISABLE_EXTENSIONS + if (share_mode()) return; + if (super_seeding()) return; + + for (std::set::iterator i = m_connections.begin(); + i != m_connections.end();) + { + // since the call to disconnect_if_redundant() may + // delete the entry from this container, make sure + // to increment the iterator early + bt_peer_connection* p = (bt_peer_connection*)*i; + ++i; + if (p->type() == peer_connection::bittorrent_connection) + p->write_upload_only(); + } +#endif + } + + void torrent::set_share_mode(bool s) + { + if (s == m_share_mode) return; + + m_share_mode = s; + + // in share mode, all pieces have their priorities initialized to 0 + std::fill(m_file_priority.begin(), m_file_priority.end(), !m_share_mode); + + update_piece_priorities(); + + if (m_share_mode) recalc_share_mode(); + } + + void torrent::set_upload_mode(bool b) + { + if (b == m_upload_mode) return; + + m_upload_mode = b; + + state_updated(); + send_upload_only(); + + if (m_upload_mode) + { + // clear request queues of all peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = (*i); + p->cancel_all_requests(); + } + // this is used to try leaving upload only mode periodically + m_upload_mode_time = 0; + } + else + { + // reset last_connected, to force fast reconnect after leaving upload mode + for (policy::iterator i = m_policy.begin_peer() + , end(m_policy.end_peer()); i != end; ++i) + { + (*i)->last_connected = 0; + } + + // send_block_requests on all peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = (*i); + p->send_block_requests(); + } + } + } + + void torrent::handle_disk_error(disk_io_job const& j, peer_connection* c) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!j.error) return; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("disk error: (%d) %s in file: %s", j.error.value(), j.error.message().c_str() + , j.error_file.c_str()); +#endif + + TORRENT_ASSERT(j.piece >= 0); + + piece_block block_finished(j.piece, j.offset / block_size()); + + if (j.action == disk_io_job::write) + { + // we failed to write j.piece to disk tell the piece picker + if (has_picker() && j.piece >= 0) picker().write_failed(block_finished); + } + + if (j.error == error_code(boost::system::errc::not_enough_memory, generic_category())) + { + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + if (c) c->disconnect(errors::no_memory); + return; + } + + // notify the user of the error + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + + // put the torrent in an error-state + set_error(j.error, j.error_file); + + if (j.action == disk_io_job::write + && (j.error == boost::system::errc::read_only_file_system + || j.error == boost::system::errc::permission_denied + || j.error == boost::system::errc::operation_not_permitted + || j.error == boost::system::errc::no_space_on_device + || j.error == boost::system::errc::file_too_large)) + { + // if we failed to write, stop downloading and just + // keep seeding. + // TODO: 1 make this depend on the error and on the filesystem the + // files are being downloaded to. If the error is no_space_left_on_device + // and the filesystem doesn't support sparse files, only zero the priorities + // of the pieces that are at the tails of all files, leaving everything + // up to the highest written piece in each file + set_upload_mode(true); + return; + } + + // if the error appears to be more serious than a full disk, just pause the torrent + pause(); + } + + void torrent::on_disk_read_complete(int ret, disk_io_job const& j + , peer_request r, read_piece_struct* rp) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + disk_buffer_holder buffer(m_ses, j.buffer); + + --rp->blocks_left; + if (ret != r.length) + { + rp->fail = true; + rp->error = j.error; + handle_disk_error(j); + } + else + { + std::memcpy(rp->piece_data.get() + r.start, j.buffer, r.length); + } + + if (rp->blocks_left == 0) + { + int size = m_torrent_file->piece_size(r.piece); + if (rp->fail) + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), r.piece, rp->error)); + } + else + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), r.piece, rp->piece_data, size)); + } + delete rp; + } + } + + void torrent::add_piece(int piece, char const* data, int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(piece >= 0 && piece < m_torrent_file->num_pieces()); + int piece_size = m_torrent_file->piece_size(piece); + int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); + + if (m_deleted) return; + + // avoid crash trying to access the picker when there is none + if (!has_picker()) return; + + if (picker().have_piece(piece) + && (flags & torrent::overwrite_existing) == 0) + return; + + peer_request p; + p.piece = piece; + p.start = 0; + picker().inc_refcount(piece, 0); + for (int i = 0; i < blocks_in_piece; ++i, p.start += block_size()) + { + if (picker().is_finished(piece_block(piece, i)) + && (flags & torrent::overwrite_existing) == 0) + continue; + + p.length = (std::min)(piece_size - p.start, int(block_size())); + char* buffer = m_ses.allocate_disk_buffer("add piece"); + // out of memory + if (buffer == 0) + { + picker().dec_refcount(piece, 0); + return; + } + disk_buffer_holder holder(m_ses, buffer); + std::memcpy(buffer, data + p.start, p.length); + filesystem().async_write(p, holder, boost::bind(&torrent::on_disk_write_complete + , shared_from_this(), _1, _2, p)); + piece_block block(piece, i); + picker().mark_as_downloading(block, 0, piece_picker::fast); + picker().mark_as_writing(block, 0); + } + async_verify_piece(piece, boost::bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); + picker().dec_refcount(piece, 0); + } + + void torrent::on_disk_write_complete(int ret, disk_io_job const& j + , peer_request p) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + if (m_abort) + { + piece_block block_finished(p.piece, p.start / block_size()); + return; + } + + piece_block block_finished(p.piece, p.start / block_size()); + + if (ret == -1) + { + handle_disk_error(j); + return; + } + + if (!has_picker()) return; + + // if we already have this block, just ignore it. + // this can happen if the same block is passed in through + // add_piece() multiple times + if (picker().is_finished(block_finished)) return; + + picker().mark_as_finished(block_finished, 0); + } + + void torrent::on_disk_cache_complete(int ret, disk_io_job const& j) + { + // suggest this piece to all peers + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + (*i)->send_suggest(j.piece); + } + + bool torrent::add_merkle_nodes(std::map const& nodes, int piece) + { + return m_torrent_file->add_merkle_nodes(nodes, piece); + } + + peer_request torrent::to_req(piece_block const& p) const + { + int block_offset = p.block_index * block_size(); + int block = (std::min)(torrent_file().piece_size( + p.piece_index) - block_offset, int(block_size())); + TORRENT_ASSERT(block > 0); + TORRENT_ASSERT(block <= block_size()); + + peer_request r; + r.piece = p.piece_index; + r.start = block_offset; + r.length = block; + return r; + } + + std::string torrent::name() const + { + if (valid_metadata()) return m_torrent_file->name(); + if (m_name) return *m_name; + return ""; + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + + void torrent::add_extension(boost::shared_ptr ext) + { + m_extensions.push_back(ext); + } + + void torrent::add_extension(boost::function(torrent*, void*)> const& ext + , void* userdata) + { + boost::shared_ptr tp(ext(this, userdata)); + if (!tp) return; + + add_extension(tp); + + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + boost::shared_ptr pp(tp->new_connection(p)); + if (pp) p->add_extension(pp); + } + + // if files are checked for this torrent, call the extension + // to let it initialize itself + if (m_connections_initialized) + tp->on_files_checked(); + } + +#endif + +#ifdef TORRENT_USE_OPENSSL + +#if BOOST_VERSION >= 104700 + bool torrent::verify_peer_cert(bool preverified, boost::asio::ssl::verify_context& ctx) + { + // if the cert wasn't signed by the correct CA, fail the verification + if (!preverified) return false; + + // we're only interested in checking the certificate at the end of the chain. + // TODO: is verify_peer_cert called once per certificate in the chain, and + // this function just tells us which depth we're at right now? If so, the comment + // makes sense. + // any certificate that isn't the leaf (i.e. the one presented by the peer) + // should be accepted automatically, given preverified is true. The leaf certificate + // need to be verified to make sure its DN matches the info-hash + int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); + if (depth > 0) return true; + + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + + // Go through the alternate names in the certificate looking for matching DNS entries + GENERAL_NAMES* gens = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string names; + bool match = false; +#endif + for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != GEN_DNS) continue; + ASN1_IA5STRING* domain = gen->d.dNSName; + if (domain->type != V_ASN1_IA5STRING || !domain->data || !domain->length) continue; + const char* torrent_name = reinterpret_cast(domain->data); + std::size_t name_length = domain->length; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (i > 1) names += " | n: "; + names.append(torrent_name, name_length); +#endif + if (strncmp(torrent_name, "*", name_length) == 0 + || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + match = true; + // if we're logging, keep looping over all names, + // for completeness of the log + continue; +#endif + return true; + } + } + + // no match in the alternate names, so try the common names. We should only + // use the "most specific" common name, which is the last one in the list. + X509_NAME* name = X509_get_subject_name(cert); + int i = -1; + ASN1_STRING* common_name = 0; + while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) + { + X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); + common_name = X509_NAME_ENTRY_get_data(name_entry); + } + if (common_name && common_name->data && common_name->length) + { + const char* torrent_name = reinterpret_cast(common_name->data); + std::size_t name_length = common_name->length; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (!names.empty()) names += " | n: "; + names.append(torrent_name, name_length); +#endif + + if (strncmp(torrent_name, "*", name_length) == 0 + || strncmp(torrent_name, m_torrent_file->name().c_str(), name_length) == 0) + { +#if !defined(TORRENT_VERBOSE_LOGGING) && !defined(TORRENT_LOGGING) + return true; +#else + match = true; +#endif + } + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("<== incoming SSL CONNECTION [ n: %s | match: %s ]" + , names.c_str(), match?"yes":"no"); + return match; +#endif + + return false; + } +#endif // BOOST_VERSION + + void torrent::init_ssl(std::string const& cert) + { + using boost::asio::ssl::context; + + // this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+ + OpenSSL_add_all_algorithms(); + + boost::uint64_t now = total_microseconds(time_now_hires() - min_time()); + // assume 9 bits of entropy (i.e. about 1 millisecond) + RAND_add(&now, 8, 1.125); + RAND_add(&info_hash()[0], 20, 3); + // entropy is also added on incoming and completed connection attempts + + TORRENT_ASSERT(RAND_status() == 1); + +#if BOOST_VERSION >= 104700 + // create the SSL context for this torrent. We need to + // inject the root certificate, and no other, to + // verify other peers against + boost::shared_ptr ctx( + new (std::nothrow) context(m_ses.m_io_service, context::sslv23)); + + if (!ctx) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + set_error(ec, "SSL context"); + pause(); + return; + } + + ctx->set_options(context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::single_dh_use); + + error_code ec; + ctx->set_verify_mode(context::verify_peer + | context::verify_fail_if_no_peer_cert + | context::verify_client_once, ec); + if (ec) + { + set_error(ec, "SSL verify mode"); + pause(); + return; + } + + // the verification function verifies the distinguished name + // of a peer certificate to make sure it matches the info-hash + // of the torrent, or that it's a "star-cert" + ctx->set_verify_callback(boost::bind(&torrent::verify_peer_cert, this, _1, _2), ec); + if (ec) + { + set_error(ec, "SSL verify callback"); + pause(); + return; + } + + SSL_CTX* ssl_ctx = ctx->impl(); + // create a new x.509 certificate store + X509_STORE* cert_store = X509_STORE_new(); + if (!cert_store) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + set_error(ec, "x.509 certificate store"); + pause(); + return; + } + + // wrap the PEM certificate in a BIO, for openssl to read + BIO* bp = BIO_new_mem_buf((void*)cert.c_str(), cert.size()); + + // parse the certificate into OpenSSL's internal + // representation + X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0); + + BIO_free(bp); + + if (!certificate) + { + error_code ec(::ERR_get_error(), + asio::error::get_ssl_category()); + X509_STORE_free(cert_store); + set_error(ec, "x.509 certificate"); + pause(); + return; + } + + // add cert to cert_store + X509_STORE_add_cert(cert_store, certificate); + + X509_free(certificate); + + // and lastly, replace the default cert store with ours + SSL_CTX_set_cert_store(ssl_ctx, cert_store); +#if 0 + char filename[100]; + snprintf(filename, sizeof(filename), "/tmp/%d.pem", rand()); + FILE* f = fopen(filename, "w+"); + fwrite(cert.c_str(), cert.size(), 1, f); + fclose(f); + ctx->load_verify_file(filename); +#endif + // if all went well, set the torrent ssl context to this one + m_ssl_ctx = ctx; + // tell the client we need a cert for this torrent + alerts().post_alert(torrent_need_cert_alert(get_handle())); +#else + set_error(asio::error::operation_not_supported, "x.509 certificate"); + pause(); +#endif + } + +#endif // TORRENT_OPENSSL + + peer_connection* torrent::find_lowest_ranking_peer() const + { + const_peer_iterator lowest_rank = end(); + for (const_peer_iterator i = begin(); i != end(); ++i) + { + // disconnecting peers don't count + if ((*i)->is_disconnecting()) continue; + if (lowest_rank == end() || (*lowest_rank)->peer_rank() > (*i)->peer_rank()) + lowest_rank = i; + } + + if (lowest_rank == end()) return NULL; + return *lowest_rank; + } + + // this may not be called from a constructor because of the call to + // shared_from_this() + void torrent::init() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(m_torrent_file->is_valid()); + TORRENT_ASSERT(m_torrent_file->num_files() > 0); + TORRENT_ASSERT(m_torrent_file->total_size() >= 0); + + if (int(m_file_priority.size()) > m_torrent_file->num_files()) + m_file_priority.resize(m_torrent_file->num_files()); + + std::string cert = m_torrent_file->ssl_cert(); + if (!cert.empty()) + { + m_ssl_torrent = true; +#ifdef TORRENT_USE_OPENSSL + init_ssl(cert); +#endif + } + + m_file_priority.resize(m_torrent_file->num_files(), 1); + + // initialize pad files to priority 0 + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + if (!fs.pad_file_at(i)) continue; + m_file_priority[i] = 0; + } + m_file_progress.resize(m_torrent_file->num_files(), 0); + + m_block_size_shift = root2((std::min)(int(block_size()), m_torrent_file->piece_length())); + + if (m_torrent_file->num_pieces() > piece_picker::max_pieces) + { + set_error(errors::too_many_pieces_in_torrent, ""); + pause(); + return; + } + + if (m_torrent_file->num_pieces() == 0) + { + set_error(errors::torrent_invalid_length, ""); + pause(); + return; + } + + if (m_resume_entry.type() == lazy_entry::dict_t) + { + int ev = 0; + if (m_resume_entry.dict_find_string_value("file-format") != "libtorrent resume file") + ev = errors::invalid_file_tag; + + std::string info_hash = m_resume_entry.dict_find_string_value("info-hash"); + if (!ev && info_hash.empty()) + ev = errors::missing_info_hash; + + if (!ev && sha1_hash(info_hash) != m_torrent_file->info_hash()) + ev = errors::mismatching_info_hash; + + if (ev && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle() + , error_code(ev, get_libtorrent_category()))); + } + + if (ev) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("fastresume data rejected: %s" + , error_code(ev, get_libtorrent_category()).message().c_str()); +#endif + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + } + else + { + read_resume_data(m_resume_entry); + } + } + + // the shared_from_this() will create an intentional + // cycle of ownership, se the hpp file for description. + m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file + , m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor + , (storage_mode_t)m_storage_mode, m_file_priority); + m_storage = m_owning_storage.get(); + + if (!m_seed_mode) + { + TORRENT_ASSERT(!m_picker); + + m_picker.reset(new piece_picker()); + + int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); + int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + + block_size() - 1) / block_size(); + m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); + + // this resume data reqires the piece picker to have been constructed + // that's why it's done later + if (m_resume_entry.type() == lazy_entry::dict_t) + { + lazy_entry const* piece_priority = m_resume_entry.dict_find_string("piece_priority"); + if (piece_priority && piece_priority->string_length() + == m_torrent_file->num_pieces()) + { + char const* p = piece_priority->string_ptr(); + for (int i = 0; i < piece_priority->string_length(); ++i) + m_picker->set_piece_priority(i, p[i]); + m_policy.recalculate_connect_candidates(); + } + } + } + + if (m_share_mode) + { + // in share mode, all pieces have their priorities initialized to 0 + std::fill(m_file_priority.begin(), m_file_priority.end(), 0); + } + + if (!m_connections_initialized) + { + m_connections_initialized = true; + // all peer connections have to initialize themselves now that the metadata + // is available + for (torrent::peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* pc = *i; + ++i; + if (pc->is_disconnecting()) continue; + pc->on_metadata_impl(); + if (pc->is_disconnecting()) continue; + pc->init(); + } + } + + // in case file priorities were passed in via the add_torrent_params + // and also in the case of share mode, we need to update the priorities + update_piece_priorities(); + + std::vector const& web_seeds = m_torrent_file->web_seeds(); + m_web_seeds.insert(m_web_seeds.end(), web_seeds.begin(), web_seeds.end()); + + set_state(torrent_status::checking_resume_data); + +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = true; +#endif + + if (m_seed_mode) + { + m_ses.m_io_service.post(boost::bind(&torrent::files_checked, shared_from_this())); + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); +#if TORRENT_USE_ASSERTS + m_resume_data_loaded = true; +#endif + return; + } + + int num_pad_files = 0; + TORRENT_ASSERT(block_size() > 0); + for (int i = 0; i < fs.num_files(); ++i) + { + if (fs.pad_file_at(i)) ++num_pad_files; + + if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue; + m_padding += boost::uint32_t(fs.file_size(i)); + + peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i)); + int off = pr.start & (block_size()-1); + if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; } + TORRENT_ASSERT((pr.start & (block_size()-1)) == 0); + + int block = block_size(); + int blocks_per_piece = m_torrent_file->piece_length() / block; + piece_block pb(pr.piece, pr.start / block); + for (; pr.length >= block; pr.length -= block, ++pb.block_index) + { + if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } + m_picker->mark_as_finished(pb, 0); + } + // ugly edge case where padfiles are not used they way they're + // supposed to be. i.e. added back-to back or at the end + if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } + if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1)) + || i + 1 == fs.num_files())) + { + m_picker->mark_as_finished(pb, 0); + } + } + + if (m_padding > 0) + { + // if we marked an entire piece as finished, we actually + // need to consider it finished + + std::vector const& dq + = m_picker->get_download_queue(); + + std::vector have_pieces; + + for (std::vector::const_iterator i + = dq.begin(); i != dq.end(); ++i) + { + int num_blocks = m_picker->blocks_in_piece(i->index); + if (i->finished < num_blocks) continue; + have_pieces.push_back(i->index); + } + + for (std::vector::iterator i = have_pieces.begin(); + i != have_pieces.end(); ++i) + { + we_have(*i); + } + } + + m_picker->set_num_pad_files(num_pad_files); + + m_storage->async_check_fastresume(&m_resume_entry + , boost::bind(&torrent::on_resume_data_checked + , shared_from_this(), _1, _2)); + } + + bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + { + if ((*i)->type() != peer_connection::bittorrent_connection) continue; + bt_peer_connection* p = (bt_peer_connection*)(*i); + if (!p->supports_holepunch()) continue; + peer_plugin const* pp = p->find_plugin("ut_pex"); + if (!pp) continue; + if (was_introduced_by(pp, ep)) return (bt_peer_connection*)p; + } +#endif + return 0; + } + + bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const + { + for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + { + peer_connection* p = *i; + if (p->type() != peer_connection::bittorrent_connection) continue; + if (p->remote() == ep) return (bt_peer_connection*)p; + } + return 0; + } + + void torrent::on_resume_data_checked(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret == piece_manager::fatal_disk_error) + { + handle_disk_error(j); + auto_managed(false); + pause(); + set_state(torrent_status::queued_for_checking); + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + return; + } + + state_updated(); + + if (m_resume_entry.type() == lazy_entry::dict_t) + { + using namespace libtorrent::detail; // for read_*_endpoint() + peer_id id(0); + + if (lazy_entry const* peers_entry = m_resume_entry.dict_find_string("peers")) + { + int num_peers = peers_entry->string_length() / (sizeof(address_v4::bytes_type) + 2); + char const* ptr = peers_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + m_policy.add_peer(read_v4_endpoint(ptr) + , id, peer_info::resume_data, 0); + } + } + + if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_string("banned_peers")) + { + int num_peers = banned_peers_entry->string_length() / (sizeof(address_v4::bytes_type) + 2); + char const* ptr = banned_peers_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + policy::peer* p = m_policy.add_peer(read_v4_endpoint(ptr) + , id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } + +#if TORRENT_USE_IPV6 + if (lazy_entry const* peers6_entry = m_resume_entry.dict_find_string("peers6")) + { + int num_peers = peers6_entry->string_length() / (sizeof(address_v6::bytes_type) + 2); + char const* ptr = peers6_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + m_policy.add_peer(read_v6_endpoint(ptr) + , id, peer_info::resume_data, 0); + } + } + + if (lazy_entry const* banned_peers6_entry = m_resume_entry.dict_find_string("banned_peers6")) + { + int num_peers = banned_peers6_entry->string_length() / (sizeof(address_v6::bytes_type) + 2); + char const* ptr = banned_peers6_entry->string_ptr(); + for (int i = 0; i < num_peers; ++i) + { + policy::peer* p = m_policy.add_peer(read_v6_endpoint(ptr) + , id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } +#endif + + // parse out "peers" from the resume data and add them to the peer list + if (lazy_entry const* peers_entry = m_resume_entry.dict_find_list("peers")) + { + for (int i = 0; i < peers_entry->list_size(); ++i) + { + lazy_entry const* e = peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + error_code ec; + tcp::endpoint a(address::from_string(ip, ec), (unsigned short)port); + if (ec) continue; + m_policy.add_peer(a, id, peer_info::resume_data, 0); + } + } + + // parse out "banned_peers" and add them as banned + if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_list("banned_peers")) + { + for (int i = 0; i < banned_peers_entry->list_size(); ++i) + { + lazy_entry const* e = banned_peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + error_code ec; + tcp::endpoint a(address::from_string(ip, ec), (unsigned short)port); + if (ec) continue; + policy::peer* p = m_policy.add_peer(a, id, peer_info::resume_data, 0); + if (p) m_policy.ban_peer(p); + } + } + } + + // only report this error if the user actually provided resume data + if ((j.error || ret != 0) && !m_resume_data.empty() + && m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), j.error)); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + if (ret != 0) + { + debug_log("fastresume data rejected: ret: %d (%d) %s" + , ret, j.error.value(), j.error.message().c_str()); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + else + debug_log("fastresume data accepted"); +#endif +#endif + + // if ret != 0, it means we need a full check. We don't necessarily need + // that when the resume data check fails. For instance, if the resume data + // is incorrect, but we don't have any files, we skip the check and initialize + // the storage to not have anything. + if (ret == 0) + { + // there are either no files for this torrent + // or the resume_data was accepted + + if (!j.error && m_resume_entry.type() == lazy_entry::dict_t) + { + // parse have bitmask + lazy_entry const* pieces = m_resume_entry.dict_find("pieces"); + if (pieces && pieces->type() == lazy_entry::string_t + && int(pieces->string_length()) == m_torrent_file->num_pieces()) + { + char const* pieces_str = pieces->string_ptr(); + for (int i = 0, end(pieces->string_length()); i < end; ++i) + { + if (pieces_str[i] & 1) we_have(i); + if (m_seed_mode && (pieces_str[i] & 2)) m_verified.set_bit(i); + } + } + else + { + lazy_entry const* slots = m_resume_entry.dict_find("slots"); + if (slots && slots->type() == lazy_entry::list_t) + { + for (int i = 0; i < slots->list_size(); ++i) + { + int piece = slots->list_int_value_at(i, -1); + if (piece >= 0) we_have(piece); + } + } + } + + // parse unfinished pieces + int num_blocks_per_piece = + static_cast(torrent_file().piece_length()) / block_size(); + + if (lazy_entry const* unfinished_ent = m_resume_entry.dict_find_list("unfinished")) + { + for (int i = 0; i < unfinished_ent->list_size(); ++i) + { + lazy_entry const* e = unfinished_ent->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + int piece = e->dict_find_int_value("piece", -1); + if (piece < 0 || piece > torrent_file().num_pieces()) continue; + + if (m_picker->have_piece(piece)) + m_picker->we_dont_have(piece); + + std::string bitmask = e->dict_find_string_value("bitmask"); + if (bitmask.empty()) continue; + + const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1); + if ((int)bitmask.size() != num_bitmask_bytes) continue; + for (int k = 0; k < num_bitmask_bytes; ++k) + { + unsigned char bits = bitmask[k]; + int num_bits = (std::min)(num_blocks_per_piece - k*8, 8); + for (int b = 0; b < num_bits; ++b) + { + const int block = k * 8 + b; + if (bits & (1 << b)) + { + m_picker->mark_as_finished(piece_block(piece, block), 0); + if (m_picker->is_piece_finished(piece)) + async_verify_piece(piece, boost::bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); + } + } + } + } + } + } + + files_checked(); + } + else + { + // either the fastresume data was rejected or there are + // some files + set_state(torrent_status::queued_for_checking); + if (should_check_files()) + queue_torrent_check(); + } + + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + } + + void torrent::queue_torrent_check() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_queued_for_checking) return; + m_queued_for_checking = true; + m_ses.queue_check_torrent(shared_from_this()); + } + + void torrent::dequeue_torrent_check() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_queued_for_checking) return; + m_queued_for_checking = false; + m_ses.dequeue_check_torrent(shared_from_this()); + } + + void torrent::force_recheck() + { + if (!valid_metadata()) return; + + // if the torrent is already queued to check its files + // don't do anything + if (should_check_files() + || m_state == torrent_status::checking_resume_data) + return; + + clear_error(); + + disconnect_all(errors::stopping_torrent); + stop_announcing(); + + m_owning_storage->async_release_files(); + if (!m_picker) m_picker.reset(new piece_picker()); + std::fill(m_file_progress.begin(), m_file_progress.end(), 0); + + int blocks_per_piece = (m_torrent_file->piece_length() + block_size() - 1) / block_size(); + int blocks_in_last_piece = ((m_torrent_file->total_size() % m_torrent_file->piece_length()) + + block_size() - 1) / block_size(); + m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces()); + + // assume that we don't have anything + TORRENT_ASSERT(m_picker->num_have() == 0); + m_files_checked = false; + set_state(torrent_status::checking_resume_data); + + m_policy.recalculate_connect_candidates(); + + if (m_auto_managed && !is_finished()) + set_queue_position((std::numeric_limits::max)()); + + std::vector().swap(m_resume_data); + lazy_entry().swap(m_resume_entry); + m_storage->async_check_fastresume(&m_resume_entry + , boost::bind(&torrent::on_force_recheck + , shared_from_this(), _1, _2)); + } + + void torrent::on_force_recheck(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + state_updated(); + + if (ret == piece_manager::fatal_disk_error) + { + handle_disk_error(j); + return; + } + if (ret == 0) + { + // if there are no files, just start + files_checked(); + } + else + { + set_state(torrent_status::queued_for_checking); + if (should_check_files()) + queue_torrent_check(); + } + } + + void torrent::start_checking() + { + TORRENT_ASSERT(should_check_files()); + set_state(torrent_status::checking_files); + + m_storage->async_check_files(boost::bind( + &torrent::on_piece_checked + , shared_from_this(), _1, _2)); + } + + void torrent::on_piece_checked(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + state_updated(); + + if (ret == piece_manager::disk_check_aborted) + { + dequeue_torrent_check(); + pause(); + return; + } + if (ret == piece_manager::fatal_disk_error) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(file_error_alert(j.error_file, get_handle(), j.error)); + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("fatal disk error: (%d) %s", j.error.value(), j.error.message().c_str()); +#endif + auto_managed(false); + pause(); + set_error(j.error, j.error_file); + // recalculate auto-managed torrents sooner + // in order to start checking the next torrent + m_ses.trigger_auto_manage(); + return; + } + + m_progress_ppm = size_type(j.piece) * 1000000 / torrent_file().num_pieces(); + + TORRENT_ASSERT(m_picker); + if (j.offset >= 0 && !m_picker->have_piece(j.offset)) + { + we_have(j.offset); + remove_time_critical_piece(j.offset); + } + + // we're not done checking yet + // this handler will be called repeatedly until + // we're done, or encounter a failure + if (ret == piece_manager::need_full_check) return; + + dequeue_torrent_check(); + files_checked(); + } + + void torrent::use_interface(std::string net_interfaces) + { + INVARIANT_CHECK; + m_net_interfaces.clear(); + + char* str = allocate_string_copy(net_interfaces.c_str()); + char* ptr = str; + + while (ptr) + { + char* space = strchr(ptr, ','); + if (space) *space++ = 0; + error_code ec; + address a(address::from_string(ptr, ec)); + ptr = space; + if (ec) continue; + m_net_interfaces.push_back(tcp::endpoint(a, 0)); + } + free(str); + } + + tcp::endpoint torrent::get_interface() const + { + if (m_net_interfaces.empty()) return tcp::endpoint(address_v4(), 0); + if (m_interface_index >= m_net_interfaces.size()) m_interface_index = 0; + return m_net_interfaces[m_interface_index++]; + } + + void torrent::on_tracker_announce_disp(boost::weak_ptr p + , error_code const& e) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("tracker::on_tracker_announce_disp"); +#endif + if (e) return; + boost::shared_ptr t = p.lock(); + if (!t) return; + t->on_tracker_announce(); + } + + void torrent::on_tracker_announce() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_waiting_tracker = false; + if (m_abort) return; + announce_with_tracker(); + } + + void torrent::lsd_announce() + { + if (m_abort) return; + + // if the files haven't been checked yet, we're + // not ready for peers. Except, if we don't have metadata, + // we need peers to download from + if (!m_files_checked && valid_metadata()) return; + + if (!m_announce_to_lsd) return; + + if (m_torrent_file->is_valid() + && (m_torrent_file->priv() + || (torrent_file().is_i2p() + && !settings().allow_i2p_mixed))) + return; + + if (is_paused()) return; + +#ifdef TORRENT_USE_OPENSSL + int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); +#else + int port = m_ses.listen_port(); +#endif + + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file->info_hash(), port + , m_ses.settings().broadcast_lsd && m_lsd_seq == 0); + ++m_lsd_seq; + } + +#ifndef TORRENT_DISABLE_DHT + + void torrent::dht_announce() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_ses.m_dht) return; + if (!should_announce_dht()) return; + + TORRENT_ASSERT(m_allow_peers); + +#ifdef TORRENT_USE_OPENSSL + int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); +#else + int port = m_ses.listen_port(); +#endif + + boost::weak_ptr self(shared_from_this()); + + // if we're a seed, we tell the DHT for better scrape stats + int flags = is_seed() ? dht::dht_tracker::flag_seed : 0; + // if we allow incoming uTP connections, set the implied_port + // argument in the announce, this will make the DHT node use + // our source port in the packet as our listen port, which is + // likely more accurate when behind a NAT + if (settings().enable_incoming_utp) flags |= dht::dht_tracker::flag_implied_port; + + m_ses.m_dht->announce(m_torrent_file->info_hash() + , port, flags + , boost::bind(&torrent::on_dht_announce_response_disp, self, _1)); + } + + void torrent::on_dht_announce_response_disp(boost::weak_ptr t + , std::vector const& peers) + { + boost::shared_ptr tor = t.lock(); + if (!tor) return; + tor->on_dht_announce_response(peers); + } + + void torrent::on_dht_announce_response(std::vector const& peers) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (peers.empty()) return; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(dht_reply_alert( + get_handle(), peers.size())); + } + + if (torrent_file().priv() || (torrent_file().is_i2p() + && !settings().allow_i2p_mixed)) return; + + std::for_each(peers.begin(), peers.end(), boost::bind( + &policy::add_peer, boost::ref(m_policy), _1, peer_id(0) + , peer_info::dht, 0)); + + do_connect_boost(); + } + +#endif + + void torrent::announce_with_tracker(tracker_request::event_t e + , address const& bind_interface) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_trackers.empty()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: no trackers"); +#endif + return; + } + + if (m_abort) e = tracker_request::stopped; + + // if we're not announcing to trackers, only allow + // stopping + if (e != tracker_request::stopped && !m_announce_to_trackers) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: event != stopped && !m_announce_to_trackers"); +#endif + return; + } + + // if we're not allowing peers, there's no point in announcing + if (e != tracker_request::stopped && !m_allow_peers) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** announce_with_tracker: event != stopped && !m_allow_peers"); +#endif + return; + } + + TORRENT_ASSERT(m_allow_peers || e == tracker_request::stopped); + + if (e == tracker_request::none && is_finished() && !is_seed()) + e = tracker_request::paused; + + tracker_request req; + req.apply_ip_filter = m_apply_ip_filter && m_ses.m_settings.apply_ip_filter_to_trackers; + req.info_hash = m_torrent_file->info_hash(); + req.pid = m_ses.get_peer_id(); + req.downloaded = m_stat.total_payload_download() - m_total_failed_bytes; + req.uploaded = m_stat.total_payload_upload(); + req.corrupt = m_total_failed_bytes; + req.left = bytes_left(); + if (req.left == -1) req.left = 16*1024; +#ifdef TORRENT_USE_OPENSSL + // if this torrent contains an SSL certificate, make sure + // any SSL tracker presents a certificate signed by it + req.ssl_ctx = m_ssl_ctx.get(); +#endif + + // exclude redundant bytes if we should + if (!settings().report_true_downloaded) + req.downloaded -= m_total_redundant_bytes; + if (req.downloaded < 0) req.downloaded = 0; + + req.event = e; + error_code ec; + + // if we are aborting. we don't want any new peers + req.num_want = (req.event == tracker_request::stopped) + ?0:settings().num_want; + + // SSL torrents use their own listen socket +#ifdef TORRENT_USE_OPENSSL + if (is_ssl_torrent()) req.listen_port = m_ses.ssl_listen_port(); + else +#endif + req.listen_port = m_ses.listen_port(); + if (m_ses.m_key) + req.key = m_ses.m_key; + else + req.key = tracker_key(); + + ptime now = time_now_hires(); + + // the tier is kept as INT_MAX until we find the first + // tracker that works, then it's set to that tracker's + // tier. + int tier = INT_MAX; + + // have we sent an announce in this tier yet? + bool sent_announce = false; + + for (int i = 0; i < int(m_trackers.size()); ++i) + { + announce_entry& ae = m_trackers[i]; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** announce with tracker: considering \"%s\" " + "[ announce_to_all_tiers: %d announce_to_all_trackers: %d" + " i->tier: %d tier: %d " + " is_working: %d fails: %d fail_limit: %d updating: %d" + " can_announce: %d sent_announce: %d ]" + , ae.url.c_str(), settings().announce_to_all_tiers + , settings().announce_to_all_trackers + , ae.tier, tier, ae.is_working(), ae.fails, ae.fail_limit + , ae.updating, ae.can_announce(now, is_seed()), sent_announce); +#endif + // if trackerid is not specified for tracker use default one, probably set explicitly + req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid; + if (settings().announce_to_all_tiers + && !settings().announce_to_all_trackers + && sent_announce + && ae.tier <= tier + && tier != INT_MAX) + continue; + + if (ae.tier > tier && sent_announce && !settings().announce_to_all_tiers) break; + if (ae.is_working()) { tier = ae.tier; sent_announce = false; } + if (!ae.can_announce(now, is_seed())) + { + // this counts + if (ae.is_working()) sent_announce = true; + continue; + } + + req.url = ae.url; + req.event = e; + if (req.event == tracker_request::none) + { + if (!ae.start_sent) req.event = tracker_request::started; + else if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed; + } + + if (!is_any(bind_interface)) req.bind_ip = bind_interface; + else req.bind_ip = m_ses.m_listen_interface.address(); + + if (settings().force_proxy) + { + // in force_proxy mode we don't talk directly to trackers + // we only allow trackers if there is a proxy and issue + // a warning if there isn't one + std::string protocol = req.url.substr(0, req.url.find(':')); + int proxy_type = m_ses.m_proxy.type; + + // http can run over any proxy, so as long as one is used + // it's OK. If no proxy is configured, skip this tracker + if ((protocol == "http" || protocol == "https") + && proxy_type == proxy_settings::none) + { + ae.next_announce = now + minutes(10); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + anonymous_mode_alert(get_handle() + , anonymous_mode_alert::tracker_not_anonymous, req.url)); + } + continue; + } + + // for UDP, only socks5 and i2p proxies will work. + // if we're not using one of those proxues with a UDP + // tracker, skip it + if (protocol == "udp" + && proxy_type != proxy_settings::socks5 + && proxy_type != proxy_settings::socks5_pw + && proxy_type != proxy_settings::i2p_proxy) + { + ae.next_announce = now + minutes(10); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + anonymous_mode_alert(get_handle() + , anonymous_mode_alert::tracker_not_anonymous, req.url)); + } + continue; + } + } +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d" + , req.url.c_str() + , (req.event==tracker_request::stopped?"stopped" + :req.event==tracker_request::started?"started":"") + , m_abort); + + if (m_abort) + { + boost::shared_ptr tl(new aux::tracker_logger(m_ses)); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), tl); + } + else +#endif + { + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login() , shared_from_this()); + } + + ae.updating = true; + ae.next_announce = now + seconds(20); + ae.min_announce = now + seconds(10); + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + tracker_announce_alert(get_handle(), req.url, req.event)); + } + + sent_announce = true; + if (ae.is_working() + && !settings().announce_to_all_trackers + && !settings().announce_to_all_tiers) + break; + } + update_tracker_timer(now); + } + + void torrent::scrape_tracker() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_last_scrape = 0; + + if (m_trackers.empty()) return; + + int i = m_last_working_tracker; + if (i == -1) i = 0; + + tracker_request req; + req.apply_ip_filter = m_apply_ip_filter && m_ses.m_settings.apply_ip_filter_to_trackers; + req.info_hash = m_torrent_file->info_hash(); + req.kind = tracker_request::scrape_request; + req.url = m_trackers[i].url; + req.bind_ip = m_ses.m_listen_interface.address(); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), shared_from_this()); + } + + void torrent::tracker_warning(tracker_request const& req, std::string const& msg) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), req.url, msg)); + } + + void torrent::tracker_scrape_response(tracker_request const& req + , int complete, int incomplete, int downloaded, int downloaders) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + TORRENT_ASSERT(req.kind == tracker_request::scrape_request); + + announce_entry* ae = find_tracker(req); + if (ae) + { + if (incomplete >= 0) ae->scrape_incomplete = incomplete; + if (complete >= 0) ae->scrape_complete = complete; + if (downloaded >= 0) ae->scrape_downloaded = downloaded; + + update_scrape_state(); + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(scrape_reply_alert( + get_handle(), incomplete, complete, req.url)); + } + } + + void torrent::update_scrape_state() + { + // loop over all trackers and find the largest numbers for each scrape field + // then update the torrent-wide understanding of number of downloaders and seeds + int complete = -1; + int incomplete = -1; + int downloaded = -1; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + complete = (std::max)(i->scrape_complete, complete); + incomplete = (std::max)(i->scrape_incomplete, incomplete); + downloaded = (std::max)(i->scrape_downloaded, downloaded); + } + + if ((complete >= 0 && m_complete != complete) + || (incomplete >= 0 && m_incomplete != incomplete) + || (downloaded >= 0 && m_downloaded != downloaded)) + state_updated(); + + m_complete = complete; + m_incomplete = incomplete; + m_downloaded = downloaded; + } + + void torrent::tracker_response( + tracker_request const& r + , address const& tracker_ip // this is the IP we connected to + , std::list
const& tracker_ips // these are all the IPs it resolved to + , std::vector& peer_list + , int interval + , int min_interval + , int complete + , int incomplete + , int downloaded + , address const& external_ip + , const std::string& trackerid) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + TORRENT_ASSERT(r.kind == tracker_request::announce_request); + + if (external_ip != address() && !tracker_ips.empty()) + m_ses.set_external_address(external_ip, aux::session_impl::source_tracker + , *tracker_ips.begin()); + + ptime now = time_now(); + + if (interval < settings().min_announce_interval) + interval = settings().min_announce_interval; + + announce_entry* ae = find_tracker(r); + if (ae) + { + if (incomplete >= 0) ae->scrape_incomplete = incomplete; + if (complete >= 0) ae->scrape_complete = complete; + if (downloaded >= 0) ae->scrape_downloaded = downloaded; + if (!ae->start_sent && r.event == tracker_request::started) + ae->start_sent = true; + if (!ae->complete_sent && r.event == tracker_request::completed) + ae->complete_sent = true; + ae->verified = true; + ae->updating = false; + ae->fails = 0; + ae->next_announce = now + seconds(interval); + ae->min_announce = now + seconds(min_interval); + int tracker_index = ae - &m_trackers[0]; + m_last_working_tracker = prioritize_tracker(tracker_index); + + if ((!trackerid.empty()) && (ae->trackerid != trackerid)) + { + ae->trackerid = trackerid; + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(trackerid_alert(get_handle(), r.url, trackerid)); + } + + update_scrape_state(); + } + update_tracker_timer(now); + + if (complete >= 0 && incomplete >= 0) + m_last_scrape = 0; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("TRACKER RESPONSE\n" + "interval: %d\n" + "external ip: %s\n" + "we connected to: %s\n" + "peers:" + , interval + , print_address(external_ip).c_str() + , print_address(tracker_ip).c_str()); + + for (std::vector::const_iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + debug_log(" %16s %5d %s %s", i->ip.c_str(), i->port + , i->pid.is_all_zeros()?"":to_hex(i->pid.to_string()).c_str() + , identify_client(i->pid).c_str()); + } +#endif + // for each of the peers we got from the tracker + for (std::vector::iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + // don't make connections to ourself + if (i->pid == m_ses.get_peer_id()) + continue; + + error_code ec; + tcp::endpoint a(address::from_string(i->ip, ec), i->port); + + if (ec) + { + // assume this is because we got a hostname instead of + // an ip address from the tracker + +#if TORRENT_USE_I2P + char const* top_domain = strrchr(i->ip.c_str(), '.'); + if (top_domain && strcmp(top_domain, ".i2p") == 0 && m_ses.m_i2p_conn.is_open()) + { + // this is an i2p name, we need to use the sam connection + // to do the name lookup + /* + m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str() + , boost::bind(&torrent::on_i2p_resolve + , shared_from_this(), _1, _2)); + */ + // it seems like you're not supposed to do a name lookup + // on the peers returned from the tracker, but just strip + // the .i2p and use it as a destination + i->ip.resize(i->ip.size() - 4); + m_policy.add_i2p_peer(i->ip.c_str(), peer_info::tracker, 0); + } + else +#endif + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("torrent::on_peer_name_lookup"); +#endif + tcp::resolver::query q(i->ip, to_string(i->port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid)); + } + } + else + { + // ignore local addresses from the tracker (unless the tracker is local too) + // there are 2 reasons to allow this: + // 1. retrackers are popular in russia, where an ISP runs a tracker within + // the AS (but not on the local network) giving out peers only from the + // local network + // 2. it might make sense to have a tracker extension in the future where + // trackers records a peer's internal and external IP, and match up + // peers on the same local network +// if (is_local(a.address()) && !is_local(tracker_ip)) continue; + m_policy.add_peer(a, i->pid, peer_info::tracker, 0); + } + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(tracker_reply_alert( + get_handle(), peer_list.size(), r.url)); + } + m_got_tracker_response = true; + + // we're listening on an interface type that was not used + // when talking to the tracker. If there is a matching interface + // type in the tracker IP list, make another tracker request + // using that interface + // in order to avoid triggering this case over and over, don't + // do it if the bind IP for the tracker request that just completed + // matches one of the listen interfaces, since that means this + // announce was the second one + // don't connect twice just to tell it we're stopping + + if (((!is_any(m_ses.m_ipv6_interface.address()) && tracker_ip.is_v4()) + || (!is_any(m_ses.m_ipv4_interface.address()) && tracker_ip.is_v6())) + && r.bind_ip != m_ses.m_ipv4_interface.address() + && r.bind_ip != m_ses.m_ipv6_interface.address() + && r.event != tracker_request::stopped) + { + std::list
::const_iterator i = std::find_if(tracker_ips.begin() + , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); + if (i != tracker_ips.end()) + { + // the tracker did resolve to a different type of address, so announce + // to that as well + + // tell the tracker to bind to the opposite protocol type + address bind_interface = tracker_ip.is_v4() + ?m_ses.m_ipv6_interface.address() + :m_ses.m_ipv4_interface.address(); + announce_with_tracker(r.event, bind_interface); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("announce again using %s as the bind interface" + , print_address(bind_interface).c_str()); +#endif + } + } + + do_connect_boost(); + + state_updated(); + } + + void torrent::do_connect_boost() + { + if (!m_need_connect_boost) return; + + m_need_connect_boost = false; + // this is the first tracker response for this torrent + // instead of waiting one second for session_impl::on_tick() + // to be called, connect to a few peers immediately + int conns = (std::min)((std::min)((std::min)(m_ses.m_settings.torrent_connect_boost + , m_ses.m_settings.connections_limit - m_ses.num_connections()) + , m_ses.m_half_open.free_slots()) + , m_ses.m_boost_connections - m_ses.m_settings.connection_speed); + + while (want_more_peers() && conns > 0) + { + if (!m_policy.connect_one_peer(m_ses.session_time())) break; + // increase m_ses.m_boost_connections for each connection + // attempt. This will be deducted from the connect speed + // the next time session_impl::on_tick() is triggered + --conns; + ++m_ses.m_boost_connections; + } + + if (want_more_peers()) m_ses.prioritize_connections(shared_from_this()); + } + + ptime torrent::next_announce() const + { + return m_waiting_tracker?m_tracker_timer.expires_at():min_time(); + } + + void torrent::force_tracker_request(ptime t, int tracker_idx) + { + if (is_paused()) return; + if (tracker_idx == -1) + { + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->next_announce = (std::max)(t, i->min_announce) + seconds(1); + } + else + { + TORRENT_ASSERT(tracker_idx >= 0 && tracker_idx < int(m_trackers.size())); + if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size())) + return; + announce_entry& e = m_trackers[tracker_idx]; + e.next_announce = (std::max)(t, e.min_announce) + seconds(1); + } + update_tracker_timer(time_now_hires()); + } + + void torrent::set_tracker_login( + std::string const& name + , std::string const& pw) + { + m_username = name; + m_password = pw; + } + +#if TORRENT_USE_I2P + void torrent::on_i2p_resolve(error_code const& ec, char const* dest) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_LOGGING + if (ec) + debug_log("i2p_resolve error: %s", ec.message().c_str()); +#endif + if (ec || m_ses.is_aborted()) return; + + m_policy.add_i2p_peer(dest, peer_info::tracker, 0); + } +#endif + + void torrent::on_peer_name_lookup(error_code const& e, tcp::resolver::iterator host + , peer_id pid) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_ASIO_DEBUGGING + complete_async("torrent::on_peer_name_lookup"); +#endif + +#if defined TORRENT_LOGGING + if (e) + debug_log("peer name lookup error: %s", e.message().c_str()); +#endif + if (e || host == tcp::resolver::iterator() || + m_ses.is_aborted()) return; + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(host->endpoint().address()) & ip_filter::blocked) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + error_code ec; + debug_log("blocked ip from tracker: %s", host->endpoint().address().to_string(ec).c_str()); +#endif + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , host->endpoint().address(), peer_blocked_alert::ip_filter)); + return; + } + + m_policy.add_peer(*host, pid, peer_info::tracker, 0); + } + + size_type torrent::bytes_left() const + { + // if we don't have the metadata yet, we + // cannot tell how big the torrent is. + if (!valid_metadata()) return -1; + return m_torrent_file->total_size() + - quantized_bytes_done(); + } + + size_type torrent::quantized_bytes_done() const + { +// INVARIANT_CHECK; + + if (!valid_metadata()) return 0; + + if (m_torrent_file->num_pieces() == 0) + return 0; + + if (is_seed()) return m_torrent_file->total_size(); + + // if any piece hash fails, we'll be taken out of seed mode + // and m_seed_mode will be false + if (m_seed_mode) return m_torrent_file->total_size(); + + const int last_piece = m_torrent_file->num_pieces() - 1; + + size_type total_done + = size_type(num_have()) * m_torrent_file->piece_length(); + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_picker->have_piece(last_piece)) + { + int corr = m_torrent_file->piece_size(last_piece) + - m_torrent_file->piece_length(); + total_done += corr; + } + return total_done; + } + + // returns the number of bytes we are interested + // in for the given block. This returns block_size() + // for all blocks except the last one (if it's smaller + // than block_size()) and blocks that overlap a padding + // file + int torrent::block_bytes_wanted(piece_block const& p) const + { + file_storage const& fs = m_torrent_file->files(); + int piece_size = m_torrent_file->piece_size(p.piece_index); + int offset = p.block_index * block_size(); + if (m_padding == 0) return (std::min)(piece_size - offset, int(block_size())); + + std::vector files = fs.map_block( + p.piece_index, offset, (std::min)(piece_size - offset, int(block_size()))); + int ret = 0; + for (std::vector::iterator i = files.begin() + , end(files.end()); i != end; ++i) + { + if (fs.pad_file_at(i->file_index)) continue; + ret += i->size; + } + TORRENT_ASSERT(ret <= (std::min)(piece_size - offset, int(block_size()))); + return ret; + } + + // fills in total_wanted, total_wanted_done and total_done + void torrent::bytes_done(torrent_status& st, bool accurate) const + { + INVARIANT_CHECK; + + st.total_done = 0; + st.total_wanted_done = 0; + st.total_wanted = m_torrent_file->total_size(); + + TORRENT_ASSERT(st.total_wanted >= m_padding); + TORRENT_ASSERT(st.total_wanted >= 0); + + if (!valid_metadata() || m_torrent_file->num_pieces() == 0) + return; + + TORRENT_ASSERT(st.total_wanted >= size_type(m_torrent_file->piece_length()) + * (m_torrent_file->num_pieces() - 1)); + + const int last_piece = m_torrent_file->num_pieces() - 1; + const int piece_size = m_torrent_file->piece_length(); + + // if any piece hash fails, we'll be taken out of seed mode + // and m_seed_mode will be false + if (m_seed_mode || is_seed()) + { + st.total_done = m_torrent_file->total_size() - m_padding; + st.total_wanted_done = st.total_done; + st.total_wanted = st.total_done; + return; + } + + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + st.total_wanted_done = size_type(num_have() - m_picker->num_have_filtered()) + * piece_size; + TORRENT_ASSERT(st.total_wanted_done >= 0); + + st.total_done = size_type(num_have()) * piece_size; + TORRENT_ASSERT(num_have() < m_torrent_file->num_pieces()); + + int num_filtered_pieces = m_picker->num_filtered() + + m_picker->num_have_filtered(); + int last_piece_index = m_torrent_file->num_pieces() - 1; + if (m_picker->piece_priority(last_piece_index) == 0) + { + st.total_wanted -= m_torrent_file->piece_size(last_piece_index); + --num_filtered_pieces; + } + st.total_wanted -= size_type(num_filtered_pieces) * piece_size; + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_picker->have_piece(last_piece)) + { + TORRENT_ASSERT(st.total_done >= piece_size); + int corr = m_torrent_file->piece_size(last_piece) + - piece_size; + TORRENT_ASSERT(corr <= 0); + TORRENT_ASSERT(corr > -piece_size); + st.total_done += corr; + if (m_picker->piece_priority(last_piece) != 0) + { + TORRENT_ASSERT(st.total_wanted_done >= piece_size); + st.total_wanted_done += corr; + } + } + TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done); + + // this is expensive, we might not want to do it all the time + if (!accurate) return; + + // subtract padding files + if (m_padding > 0) + { + file_storage const& files = m_torrent_file->files(); + for (int i = 0; i < files.num_files(); ++i) + { + if (!files.pad_file_at(i)) continue; + peer_request p = files.map_file(i, 0, files.file_size(i)); + for (int j = p.piece; p.length > 0; ++j) + { + int deduction = (std::min)(p.length, piece_size - p.start); + bool done = m_picker->have_piece(j); + bool wanted = m_picker->piece_priority(j) > 0; + if (done) st.total_done -= deduction; + if (wanted) st.total_wanted -= deduction; + if (wanted && done) st.total_wanted_done -= deduction; + TORRENT_ASSERT(st.total_done >= 0); + TORRENT_ASSERT(st.total_wanted >= 0); + TORRENT_ASSERT(st.total_wanted_done >= 0); + p.length -= piece_size - p.start; + p.start = 0; + ++p.piece; + } + } + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done >= 0); + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + + const std::vector& dl_queue + = m_picker->get_download_queue(); + + const int blocks_per_piece = (piece_size + block_size() - 1) / block_size(); + + // look at all unfinished pieces and add the completed + // blocks to our 'done' counter + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + int corr = 0; + int index = i->index; + // completed pieces are already accounted for + if (m_picker->have_piece(index)) continue; + TORRENT_ASSERT(i->finished <= m_picker->blocks_in_piece(index)); + +#if TORRENT_USE_ASSERTS + for (std::vector::const_iterator j = boost::next(i); + j != dl_queue.end(); ++j) + { + TORRENT_ASSERT(j->index != index); + } +#endif + + for (int j = 0; j < blocks_per_piece; ++j) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + TORRENT_ASSERT(m_picker->is_finished(piece_block(index, j)) + == (i->info[j].state == piece_picker::block_info::state_finished)); +#endif + if (i->info[j].state == piece_picker::block_info::state_finished) + { + corr += block_bytes_wanted(piece_block(index, j)); + } + TORRENT_ASSERT(corr >= 0); + TORRENT_ASSERT(index != last_piece || j < m_picker->blocks_in_last_piece() + || i->info[j].state != piece_picker::block_info::state_finished); + } + + st.total_done += corr; + if (m_picker->piece_priority(index) > 0) + st.total_wanted_done += corr; + } + + TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done >= 0); + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + + std::map downloading_piece; + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection* pc = *i; + boost::optional p + = pc->downloading_piece_progress(); + if (!p) continue; + + if (m_picker->have_piece(p->piece_index)) + continue; + + piece_block block(p->piece_index, p->block_index); + if (m_picker->is_finished(block)) + continue; + + std::map::iterator dp + = downloading_piece.find(block); + if (dp != downloading_piece.end()) + { + if (dp->second < p->bytes_downloaded) + dp->second = p->bytes_downloaded; + } + else + { + downloading_piece[block] = p->bytes_downloaded; + } +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(p->bytes_downloaded <= p->full_block_bytes); + TORRENT_ASSERT(p->full_block_bytes == to_req(piece_block( + p->piece_index, p->block_index)).length); +#endif + } + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + int done = (std::min)(block_bytes_wanted(i->first), i->second); + st.total_done += done; + if (m_picker->piece_priority(i->first.piece_index) != 0) + st.total_wanted_done += done; + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size() - m_padding); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size() - m_padding); + +#ifdef TORRENT_DEBUG + + if (st.total_done >= m_torrent_file->total_size()) + { + // Thist happens when a piece has been downloaded completely + // but not yet verified against the hash + fprintf(stderr, "num_have: %d\nunfinished:\n", num_have()); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + fprintf(stderr, " %d ", i->index); + for (int j = 0; j < blocks_per_piece; ++j) + { + char const* state = i->info[j].state == piece_picker::block_info::state_finished ? "1" : "0"; + fputs(state, stderr); + } + fputs("\n", stderr); + } + + fputs("downloading pieces:\n", stderr); + + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + fprintf(stderr, " %d:%d %d\n", int(i->first.piece_index), int(i->first.block_index), i->second); + } + + } + + TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size()); + TORRENT_ASSERT(st.total_wanted_done <= m_torrent_file->total_size()); + +#endif + + TORRENT_ASSERT(st.total_done >= st.total_wanted_done); + } + + // passed_hash_check + // 0: success, piece passed check + // -1: disk failure + // -2: piece failed check + void torrent::piece_finished(int index, int passed_hash_check) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** PIECE_FINISHED [ p: %d | chk: %s | size: %d ]" + , index, ((passed_hash_check == 0) + ?"passed":passed_hash_check == -1 + ?"disk failed":"failed") + , m_torrent_file->piece_size(index)); +#endif + + TORRENT_ASSERT(valid_metadata()); + + // it's possible to get here if the last piece was downloaded + // from peers and inserted with add_piece at the same time. + // if we're a seed, we won't have a piece picker, and can't continue + if (is_seed()) return; + + TORRENT_ASSERT(!m_picker->have_piece(index)); + + state_updated(); + + // even though the piece passed the hash-check + // it might still have failed being written to disk + // if so, piece_picker::write_failed() has been + // called, and the piece is no longer finished. + // in this case, we have to ignore the fact that + // it passed the check + if (!m_picker->is_piece_finished(index)) return; + + if (passed_hash_check == 0) + { + // the following call may cause picker to become invalid + // in case we just became a seed + piece_passed(index); + // if we're in seed mode, we just acquired this piece + // mark it as verified + if (m_seed_mode) verified(index); + } + else if (passed_hash_check == -2) + { + // piece_failed() will restore the piece + piece_failed(index); + } + else + { + TORRENT_ASSERT(passed_hash_check == -1); + m_picker->restore_piece(index); + restore_piece_state(index); + } + } + + void torrent::update_sparse_piece_prio(int i, int start, int end) + { + TORRENT_ASSERT(m_picker); + if (m_picker->have_piece(i) || m_picker->piece_priority(i) == 0) + return; + bool have_before = i == 0 || m_picker->have_piece(i - 1); + bool have_after = i == end - 1 || m_picker->have_piece(i + 1); + if (have_after && have_before) + m_picker->set_piece_priority(i, 7); + else if (have_after || have_before) + m_picker->set_piece_priority(i, 6); + } + + void torrent::we_have(int index) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // update m_file_progress + TORRENT_ASSERT(m_picker); + TORRENT_ASSERT(!have_piece(index)); + TORRENT_ASSERT(!m_picker->have_piece(index)); + + const int piece_size = m_torrent_file->piece_length(); + size_type off = size_type(index) * piece_size; + int file_index = m_torrent_file->files().file_index_at_offset(off); + int size = m_torrent_file->piece_size(index); + file_storage const& fs = m_torrent_file->files(); + for (; size > 0; ++file_index) + { + size_type file_offset = off - fs.file_offset(file_index); + TORRENT_ASSERT(file_index != fs.num_files()); + TORRENT_ASSERT(file_offset <= fs.file_size(file_index)); + int add = (std::min)(fs.file_size(file_index) - file_offset, size_type(size)); + m_file_progress[file_index] += add; + + TORRENT_ASSERT(m_file_progress[file_index] + <= m_torrent_file->files().file_size(file_index)); + + if (m_file_progress[file_index] >= m_torrent_file->files().file_size(file_index)) + { + if (!m_torrent_file->files().pad_file_at(file_index)) + { + if (m_ses.m_alerts.should_post()) + { + // this file just completed, post alert + m_ses.m_alerts.post_alert(file_completed_alert(get_handle() + , file_index)); + } + } + } + size -= add; + off += add; + TORRENT_ASSERT(size >= 0); + } + + remove_time_critical_piece(index, true); + + m_picker->we_have(index); + } + + void torrent::piece_passed(int index) + { +// INVARIANT_CHECK; + TORRENT_ASSERT(m_ses.is_network_thread()); + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); +#ifdef TORRENT_DEBUG + // make sure all blocks were successfully written before we + // declare the piece as "we have". + piece_picker::downloading_piece dp; + m_picker->piece_info(index, dp); + int blocks_in_piece = m_picker->blocks_in_piece(index); + TORRENT_ASSERT(dp.finished == blocks_in_piece); + TORRENT_ASSERT(dp.writing == 0); + TORRENT_ASSERT(dp.requested == 0); + TORRENT_ASSERT(dp.index == index); +#endif + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(piece_finished_alert(get_handle() + , index)); + } + + m_need_save_resume_data = true; + state_updated(); + + remove_time_critical_piece(index, true); + + bool was_finished = m_picker->num_filtered() + num_have() + == torrent_file().num_pieces(); + + std::vector downloaders; + m_picker->get_downloaders(downloaders, index); + + // increase the trust point of all peers that sent + // parts of this piece. + std::set peers; + + // these policy::peer pointers are owned by m_policy and they may be + // invalidated if a peer disconnects. We cannot keep them across any + // significant operations, but we should use them right away + // ignore NULL pointers + std::remove_copy(downloaders.begin(), downloaders.end() + , std::inserter(peers, peers.begin()), (policy::peer*)0); + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + TORRENT_ASSERT(p != 0); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + p->on_parole = false; + int trust_points = p->trust_points; + ++trust_points; + if (trust_points > 8) trust_points = 8; + p->trust_points = trust_points; + if (p->connection) + { + TORRENT_ASSERT(p->connection->m_in_use == 1337); + p->connection->received_valid_data(index); + } + } + + // announcing a piece may invalidate the policy::peer pointers + // so we can't use them anymore + + downloaders.clear(); + peers.clear(); + + we_have(index); + + for (peer_iterator i = m_connections.begin(); i != m_connections.end();) + { + intrusive_ptr p = *i; + ++i; + p->announce_piece(index); + } + + if (settings().max_sparse_regions > 0 + && m_picker->sparse_regions() > settings().max_sparse_regions) + { + // we have too many sparse regions. Prioritize pieces + // that won't introduce new sparse regions + // prioritize pieces that will reduce the number of sparse + // regions even higher + int start = m_picker->cursor(); + int end = m_picker->reverse_cursor(); + if (index > start) update_sparse_piece_prio(index - 1, start, end); + if (index < end - 1) update_sparse_piece_prio(index + 1, start, end); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_pass(index); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // since this piece just passed, we might have + // become uninterested in some peers where this + // was the last piece we were interested in + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = *i; + // update_interest may disconnect the peer and + // invalidate the iterator + ++i; + // if we're not interested already, no need to check + if (!p->is_interesting()) continue; + // if the peer doesn't have the piece we just got, it + // wouldn't affect our interest + if (!p->has_piece(index)) continue; + p->update_interest(); + } + + if (!was_finished && is_finished()) + { + // torrent finished + // i.e. all the pieces we're interested in have + // been downloaded. Release the files (they will open + // in read only mode if needed) + finished(); + // if we just became a seed, picker is now invalid, since it + // is deallocated by the torrent once it starts seeding + } + + m_last_download = 0; + + if (m_share_mode) + recalc_share_mode(); + } + + void torrent::piece_failed(int index) + { + // if the last piece fails the peer connection will still + // think that it has received all of it until this function + // resets the download queue. So, we cannot do the + // invariant check here since it assumes: + // (total_done == m_torrent_file->total_size()) => is_seed() + INVARIANT_CHECK; + TORRENT_ASSERT(m_ses.is_network_thread()); + + TORRENT_ASSERT(m_storage); + TORRENT_ASSERT(m_storage->refcount() > 0); + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index)); + + // increase the total amount of failed bytes + add_failed_bytes(m_torrent_file->piece_size(index)); + + std::vector downloaders; + m_picker->get_downloaders(downloaders, index); + + // decrease the trust point of all peers that sent + // parts of this piece. + // first, build a set of all peers that participated + std::set peers; + std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); + +#ifdef TORRENT_DEBUG + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + policy::peer* p = (policy::peer*)*i; + if (p && p->connection) + { + p->connection->piece_failed = true; + } + } +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_piece_failed(index); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // did we receive this piece from a single peer? + bool single_peer = peers.size() == 1; + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + policy::peer* p = static_cast(*i); + if (p == 0) continue; + TORRENT_ASSERT(p->in_use); + bool allow_disconnect = true; + if (p->connection) + { + TORRENT_ASSERT(p->connection->m_in_use == 1337); + + // the peer implementation can ask not to be disconnected. + // this is used for web seeds for instance, to instead of + // disconnecting, mark the file as not being haved. + allow_disconnect = p->connection->received_invalid_data(index, single_peer); + } + + if (m_ses.settings().use_parole_mode) + p->on_parole = true; + + int hashfails = p->hashfails; + int trust_points = p->trust_points; + + // we decrease more than we increase, to keep the + // allowed failed/passed ratio low. + trust_points -= 2; + ++hashfails; + if (trust_points < -7) trust_points = -7; + p->trust_points = trust_points; + if (hashfails > 255) hashfails = 255; + p->hashfails = hashfails; + + // either, we have received too many failed hashes + // or this was the only peer that sent us this piece. + // if we have failed more than 3 pieces from this peer, + // don't trust it regardless. + if (p->trust_points <= -7 + || (single_peer && allow_disconnect)) + { + // we don't trust this peer anymore + // ban it. + if (m_ses.m_alerts.should_post()) + { + peer_id pid(0); + if (p->connection) pid = p->connection->pid(); + m_ses.m_alerts.post_alert(peer_ban_alert( + get_handle(), p->ip(), pid)); + } + + // mark the peer as banned + m_policy.ban_peer(p); +#ifdef TORRENT_STATS + ++m_ses.m_banned_for_hash_failure; +#endif + + if (p->connection) + { +#ifdef TORRENT_LOGGING + debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces" + , print_endpoint(p->ip()).c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->connection->peer_log("*** BANNING PEER: Too many corrupt pieces"); +#endif + p->connection->disconnect(errors::too_many_corrupt_pieces); + } + } + } + + // we have to let the piece_picker know that + // this piece failed the check as it can restore it + // and mark it as being interesting for download + m_picker->restore_piece(index); + + // we might still have outstanding requests to this + // piece that hasn't been received yet. If this is the + // case, we need to re-open the piece and mark any + // blocks we're still waiting for as requested + restore_piece_state(index); + + TORRENT_ASSERT(m_storage); + + TORRENT_ASSERT(m_picker->have_piece(index) == false); + +#ifdef TORRENT_DEBUG + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + policy::peer* p = (policy::peer*)*i; + if (p && p->connection) + { + p->connection->piece_failed = false; + } + } +#endif + } + + void torrent::restore_piece_state(int index) + { + TORRENT_ASSERT(has_picker()); + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + std::vector const& dq = p->download_queue(); + std::vector const& rq = p->request_queue(); + for (std::vector::const_iterator k = dq.begin() + , end(dq.end()); k != end; ++k) + { + if (k->timed_out || k->not_wanted) continue; + if (int(k->block.piece_index) != index) continue; + m_picker->mark_as_downloading(k->block, p->peer_info_struct() + , (piece_picker::piece_state_t)p->peer_speed()); + } + for (std::vector::const_iterator k = rq.begin() + , end(rq.end()); k != end; ++k) + { + if (int(k->block.piece_index) != index) continue; + m_picker->mark_as_downloading(k->block, p->peer_info_struct() + , (piece_picker::piece_state_t)p->peer_speed()); + } + } + } + + void torrent::abort() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_abort) return; + + m_abort = true; + + update_guage(); + + // if the torrent is paused, it doesn't need + // to announce with even=stopped again. + if (!is_paused()) + { + stop_announcing(); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("ABORTING TORRENT"); +#endif + + // disconnect all peers and close all + // files belonging to the torrents + disconnect_all(errors::torrent_aborted); + + // post a message to the main thread to destruct + // the torrent object from there + if (m_owning_storage.get()) + { + m_storage->abort_disk_io(); + m_storage->async_release_files( + boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, _2)); + } + else + { + TORRENT_ASSERT(m_abort); + if (alerts().should_post()) + alerts().post_alert(cache_flushed_alert(get_handle())); + } + + dequeue_torrent_check(); + + if (m_state == torrent_status::checking_files) + set_state(torrent_status::queued_for_checking); + + m_owning_storage = 0; + m_host_resolver.cancel(); + } + + void torrent::super_seeding(bool on) + { + if (on == m_super_seeding) return; + + m_super_seeding = on; + + if (m_super_seeding) return; + + // disable super seeding for all peers + for (peer_iterator i = begin(); i != end(); ++i) + { + (*i)->superseed_piece(-1, -1); + } + } + + int torrent::get_piece_to_super_seed(bitfield const& bits) + { + // return a piece with low availability that is not in + // the bitfield and that is not currently being super + // seeded by any peer + TORRENT_ASSERT(m_super_seeding); + + // do a linear search from the first piece + int min_availability = 9999; + std::vector avail_vec; + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + if (bits[i]) continue; + + int availability = 0; + for (const_peer_iterator j = begin(); j != end(); ++j) + { + if ((*j)->super_seeded_piece(i)) + { + // avoid superseeding the same piece to more than one + // peer if we can avoid it. Do this by artificially + // increase the availability + availability = 999; + break; + } + if ((*j)->has_piece(i)) ++availability; + } + if (availability > min_availability) continue; + if (availability == min_availability) + { + avail_vec.push_back(i); + continue; + } + TORRENT_ASSERT(availability < min_availability); + min_availability = availability; + avail_vec.clear(); + avail_vec.push_back(i); + } + + return avail_vec[random() % avail_vec.size()]; + } + + void torrent::on_files_deleted(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret != 0) + { + alerts().post_alert(torrent_delete_failed_alert(get_handle(), j.error, m_torrent_file->info_hash())); + } + else + { + alerts().post_alert(torrent_deleted_alert(get_handle(), m_torrent_file->info_hash())); + } + } + + void torrent::on_files_released(int ret, disk_io_job const& j) + { +/* + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (alerts().should_post()) + { + alerts().post_alert(torrent_paused_alert(get_handle())); + } +*/ + } + + void torrent::on_save_resume_data(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (!j.resume_data) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle(), j.error)); + } + else + { + m_need_save_resume_data = false; + m_last_saved_resume = time(0); + write_resume_data(*j.resume_data); + alerts().post_alert(save_resume_data_alert(j.resume_data + , get_handle())); + state_updated(); + } + } + + void torrent::on_file_renamed(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (ret == 0) + { + if (alerts().should_post()) + alerts().post_alert(file_renamed_alert(get_handle(), j.str, j.piece)); + m_torrent_file->rename_file(j.piece, j.str); + } + else + { + if (alerts().should_post()) + alerts().post_alert(file_rename_failed_alert(get_handle() + , j.piece, j.error)); + } + } + + void torrent::on_torrent_paused(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (alerts().should_post()) + alerts().post_alert(torrent_paused_alert(get_handle())); + } + + std::string torrent::tracker_login() const + { + if (m_username.empty() && m_password.empty()) return ""; + return m_username + ":" + m_password; + } + + boost::uint32_t torrent::tracker_key() const + { + uintptr_t self = (uintptr_t)this; + uintptr_t ses = (uintptr_t)&m_ses; + sha1_hash h = hasher((char*)&self, sizeof(self)) + .update((char*)&m_storage, sizeof(m_storage)) + .update((char*)&ses, sizeof(ses)) + .final(); + unsigned char const* ptr = &h[0]; + return detail::read_uint32(ptr); + } + + void torrent::cancel_non_critical() + { + std::set time_critical; + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + time_critical.insert(i->piece); + } + + for (std::set::iterator i + = m_connections.begin(), end(m_connections.end()); i != end; ++i) + { + // for each peer, go through its download and request queue + // and cancel everything, except pieces that are time critical + peer_connection* p = *i; + + std::vector dq = p->download_queue(); + for (std::vector::iterator k = dq.begin() + , end(dq.end()); k != end; ++k) + { + if (time_critical.count(k->block.piece_index)) continue; + if (k->not_wanted || k->timed_out) continue; + p->cancel_request(k->block, true); + } + + std::vector rq = p->request_queue(); + for (std::vector::const_iterator k = rq.begin() + , end(rq.end()); k != end; ++k) + { + if (time_critical.count(k->block.piece_index)) continue; + p->cancel_request(k->block, true); + } + } + } + + void torrent::set_piece_deadline(int piece, int t, int flags) + { + INVARIANT_CHECK; + + if (m_abort) + { + // failed + if (flags & torrent_handle::alert_when_available) + { + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + return; + } + + ptime deadline = time_now() + milliseconds(t); + + // if we already have the piece, no need to set the deadline. + // however, if the user asked to get the piece data back, we still + // need to read it and post it back to the user + if (is_seed() || m_picker->have_piece(piece)) + { + if (flags & torrent_handle::alert_when_available) + read_piece(piece); + return; + } + + // if this is the first time critical piece we add. in order to make it + // react quickly, cancel all the currently outstanding requests + if (m_time_critical_pieces.empty()) + { + // defer this by posting it to the end of the message queue. + // this gives the client a chance to specify multiple time critical + // pieces before libtorrent cancels requests + m_ses.m_io_service.post(boost::bind(&torrent::cancel_non_critical, this)); + } + + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + if (i->piece != piece) continue; + i->deadline = deadline; + i->flags = flags; + + // resort i since deadline might have changed + while (boost::next(i) != m_time_critical_pieces.end() && i->deadline > boost::next(i)->deadline) + { + std::iter_swap(i, boost::next(i)); + ++i; + } + while (i != m_time_critical_pieces.begin() && i->deadline < boost::prior(i)->deadline) + { + std::iter_swap(i, boost::prior(i)); + --i; + } + // just in case this piece had priority 0 + m_picker->set_piece_priority(piece, 7); + return; + } + + time_critical_piece p; + p.first_requested = min_time(); + p.last_requested = min_time(); + p.flags = flags; + p.deadline = deadline; + p.peers = 0; + p.piece = piece; + std::deque::iterator i = std::upper_bound(m_time_critical_pieces.begin() + , m_time_critical_pieces.end(), p); + m_time_critical_pieces.insert(i, p); + + // just in case this piece had priority 0 + m_picker->set_piece_priority(piece, 7); + + piece_picker::downloading_piece pi; + m_picker->piece_info(piece, pi); + if (pi.requested == 0) return; + // this means we have outstanding requests (or queued + // up requests that haven't been sent yet). Promote them + // to deadline pieces immediately + std::vector downloaders; + m_picker->get_downloaders(downloaders, piece); + + int block = 0; + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i, ++block) + { + policy::peer* p = (policy::peer*)*i; + if (p == 0 || p->connection == 0) continue; + p->connection->make_time_critical(piece_block(piece, block)); + } + } + + void torrent::reset_piece_deadline(int piece) + { + remove_time_critical_piece(piece); + } + + void torrent::remove_time_critical_piece(int piece, bool finished) + { + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + if (i->piece != piece) continue; + if (finished) + { + if (i->flags & torrent_handle::alert_when_available) + { + read_piece(i->piece); + } + + // if first_requested is min_time(), it wasn't requested as a critical piece + // and we shouldn't adjust any average download times + if (i->first_requested != min_time()) + { + // update the average download time and average + // download time deviation + int dl_time = total_milliseconds(time_now() - i->first_requested); + + if (m_average_piece_time == 0) + { + m_average_piece_time = dl_time; + } + else + { + int diff = abs(int(dl_time - m_average_piece_time)); + if (m_piece_time_deviation == 0) m_piece_time_deviation = diff; + else m_piece_time_deviation = (m_piece_time_deviation * 9 + diff) / 10; + + m_average_piece_time = (m_average_piece_time * 9 + dl_time) / 10; + } + } + } + else if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + if (has_picker()) m_picker->set_piece_priority(piece, 1); + m_time_critical_pieces.erase(i); + return; + } + } + + void torrent::clear_time_critical() + { + for (std::deque::iterator i = m_time_critical_pieces.begin(); + i != m_time_critical_pieces.end();) + { + if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + if (has_picker()) m_picker->set_piece_priority(i->piece, 1); + i = m_time_critical_pieces.erase(i); + } + } + + // remove time critical pieces where priority is 0 + void torrent::remove_time_critical_pieces(std::vector const& priority) + { + for (std::deque::iterator i = m_time_critical_pieces.begin(); + i != m_time_critical_pieces.end();) + { + if (priority[i->piece] == 0) + { + if (i->flags & torrent_handle::alert_when_available) + { + // post an empty read_piece_alert to indicate it failed + m_ses.m_alerts.post_alert(read_piece_alert( + get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, get_system_category()))); + } + i = m_time_critical_pieces.erase(i); + continue; + } + ++i; + } + } + + void torrent::piece_availability(std::vector& avail) const + { + INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (!has_picker()) + { + avail.clear(); + return; + } + + m_picker->get_availability(avail); + } + + void torrent::set_piece_priority(int index, int priority) + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + if (index < 0 || index >= m_torrent_file->num_pieces()) return; + + bool was_finished = is_finished(); + bool filter_updated = m_picker->set_piece_priority(index, priority); + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + if (filter_updated) + { + update_peer_interest(was_finished); + if (priority == 0) remove_time_critical_piece(index); + } + + } + + int torrent::piece_priority(int index) const + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return 1; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + if (index < 0 || index >= m_torrent_file->num_pieces()) return 0; + + return m_picker->piece_priority(index); + } + + void torrent::prioritize_pieces(std::vector const& pieces) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + TORRENT_ASSERT(m_picker.get()); + + int index = 0; + bool filter_updated = false; + bool was_finished = is_finished(); + for (std::vector::const_iterator i = pieces.begin() + , end(pieces.end()); i != end; ++i, ++index) + { + TORRENT_ASSERT(*i >= 0); + TORRENT_ASSERT(*i <= 7); + filter_updated |= m_picker->set_piece_priority(index, *i); + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + } + if (filter_updated) + { + // we need to save this new state + m_need_save_resume_data = true; + + update_peer_interest(was_finished); + remove_time_critical_pieces(pieces); + } + + state_updated(); + } + + void torrent::piece_priorities(std::vector* pieces) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) + { + pieces->clear(); + pieces->resize(m_torrent_file->num_pieces(), 1); + return; + } + + TORRENT_ASSERT(m_picker.get()); + m_picker->piece_priorities(*pieces); + } + + namespace + { + void set_if_greater(int& piece_prio, int file_prio) + { + if (file_prio > piece_prio) piece_prio = file_prio; + } + } + + void nop() {} + + void torrent::prioritize_files(std::vector const& files) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + // the bitmask need to have exactly one bit for every file + // in the torrent + TORRENT_ASSERT(int(files.size()) == m_torrent_file->num_files()); + + if (m_torrent_file->num_pieces() == 0) return; + + int limit = int(files.size()); + if (valid_metadata() && limit > m_torrent_file->num_files()) + limit = m_torrent_file->num_files(); + + if (int(m_file_priority.size()) < limit) + m_file_priority.resize(limit); + + std::copy(files.begin(), files.begin() + limit, m_file_priority.begin()); + + if (valid_metadata() && m_torrent_file->num_files() > int(m_file_priority.size())) + m_file_priority.resize(m_torrent_file->num_files(), 1); + + // storage may be NULL during shutdown + if (m_torrent_file->num_pieces() > 0 && m_storage) + { + filesystem().async_set_file_priority(m_file_priority + , boost::bind(&nop)); + } + + update_piece_priorities(); + } + + void torrent::set_file_priority(int index, int prio) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + if (index < 0 || index >= m_torrent_file->num_files()) return; + if (prio < 0) prio = 0; + else if (prio > 7) prio = 7; + if (int(m_file_priority.size()) <= index) + { + if (prio == 1) return; + m_file_priority.resize(m_torrent_file->num_files(), 1); + } + if (m_file_priority[index] == prio) return; + m_file_priority[index] = prio; + // stoage may be NULL during shutdown + if (m_storage) + { + filesystem().async_set_file_priority(m_file_priority + , boost::bind(&nop)); + } + update_piece_priorities(); + } + + int torrent::file_priority(int index) const + { + // this call is only valid on torrents with metadata + if (!valid_metadata()) return 1; + + if (index < 0 || index >= m_torrent_file->num_files()) return 0; + if (int(m_file_priority.size()) <= index) return 1; + return m_file_priority[index]; + } + + void torrent::file_priorities(std::vector* files) const + { + INVARIANT_CHECK; + if (!valid_metadata()) + { + files->resize(m_file_priority.size()); + std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin()); + return; + } + + files->resize(m_torrent_file->num_files(), 1); + TORRENT_ASSERT(int(m_file_priority.size()) <= m_torrent_file->num_files()); + std::copy(m_file_priority.begin(), m_file_priority.end(), files->begin()); + } + + void torrent::update_piece_priorities() + { + INVARIANT_CHECK; + + if (m_torrent_file->num_pieces() == 0) return; + + size_type position = 0; + int piece_length = m_torrent_file->piece_length(); + // initialize the piece priorities to 0, then only allow + // setting higher priorities + std::vector pieces(m_torrent_file->num_pieces(), 0); + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + if (i >= m_torrent_file->num_files()) break; + size_type start = position; + size_type size = m_torrent_file->files().file_size(i); + if (size == 0) continue; + position += size; + if (m_file_priority[i] == 0) continue; + + // mark all pieces of the file with this file's priority + // but only if the priority is higher than the pieces + // already set (to avoid problems with overlapping pieces) + int start_piece = int(start / piece_length); + int last_piece = int((position - 1) / piece_length); + TORRENT_ASSERT(last_piece < int(pieces.size())); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::for_each(pieces.begin() + start_piece + , pieces.begin() + last_piece + 1 + , boost::bind(&set_if_greater, _1, m_file_priority[i])); + } + prioritize_pieces(pieces); + } + + // this is called when piece priorities have been updated + // updates the interested flag in peers + void torrent::update_peer_interest(bool was_finished) + { + for (peer_iterator i = begin(); i != end();) + { + peer_connection* p = *i; + // update_interest may disconnect the peer and + // invalidate the iterator + ++i; + p->update_interest(); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** UPDATE_PEER_INTEREST [ finished: %d was_finished %d ]" + , is_finished(), was_finished); +#endif + + // the torrent just became finished + if (is_finished() && !was_finished) + { + finished(); + } + else if (!is_finished() && was_finished) + { + // if we used to be finished, but we aren't anymore + // we may need to connect to peers again + resume_download(); + } + } + + void torrent::filter_piece(int index, bool filter) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (index < 0 || index >= m_torrent_file->num_pieces()) return; + + bool was_finished = is_finished(); + m_picker->set_piece_priority(index, filter ? 1 : 0); + update_peer_interest(was_finished); + } + + void torrent::filter_pieces(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return; + + TORRENT_ASSERT(m_picker.get()); + + bool was_finished = is_finished(); + int index = 0; + for (std::vector::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if ((m_picker->piece_priority(index) == 0) == *i) continue; + if (*i) + m_picker->set_piece_priority(index, 0); + else + m_picker->set_piece_priority(index, 1); + } + update_peer_interest(was_finished); + } + + bool torrent::is_piece_filtered(int index) const + { + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) return false; + + TORRENT_ASSERT(m_picker.get()); + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_pieces()); + + if (index < 0 || index >= m_torrent_file->num_pieces()) return true; + + return m_picker->piece_priority(index) == 0; + } + + void torrent::filtered_pieces(std::vector& bitmask) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + TORRENT_ASSERT(valid_metadata()); + if (is_seed()) + { + bitmask.clear(); + bitmask.resize(m_torrent_file->num_pieces(), false); + return; + } + + TORRENT_ASSERT(m_picker.get()); + m_picker->filtered_pieces(bitmask); + } + + void torrent::filter_files(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + // the bitmask need to have exactly one bit for every file + // in the torrent + TORRENT_ASSERT((int)bitmask.size() == m_torrent_file->num_files()); + + if (int(bitmask.size()) != m_torrent_file->num_files()) return; + + size_type position = 0; + + if (m_torrent_file->num_pieces()) + { + int piece_length = m_torrent_file->piece_length(); + // mark all pieces as filtered, then clear the bits for files + // that should be downloaded + std::vector piece_filter(m_torrent_file->num_pieces(), true); + for (int i = 0; i < (int)bitmask.size(); ++i) + { + size_type start = position; + position += m_torrent_file->files().file_size(i); + // is the file selected for download? + if (!bitmask[i]) + { + // mark all pieces of the file as downloadable + int start_piece = int(start / piece_length); + int last_piece = int(position / piece_length); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::fill(piece_filter.begin() + start_piece, piece_filter.begin() + + last_piece + 1, false); + } + } + filter_pieces(piece_filter); + } + } + + bool has_empty_url(announce_entry const& e) { return e.url.empty(); } + + void torrent::replace_trackers(std::vector const& urls) + { + m_trackers.clear(); + std::remove_copy_if(urls.begin(), urls.end(), back_inserter(m_trackers) + , &has_empty_url); + + m_last_working_tracker = -1; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->source == 0) i->source = announce_entry::source_client; + i->complete_sent = is_seed(); + } + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + + if (!m_trackers.empty()) announce_with_tracker(); + + m_need_save_resume_data = true; + } + + void torrent::prioritize_udp_trackers() + { + // look for udp-trackers + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->url.substr(0, 6) != "udp://") continue; + // now, look for trackers with the same hostname + // that is has higher priority than this one + // if we find one, swap with the udp-tracker + error_code ec; + std::string udp_hostname; + using boost::tuples::ignore; + boost::tie(ignore, ignore, udp_hostname, ignore, ignore) + = parse_url_components(i->url, ec); + for (std::vector::iterator j = m_trackers.begin(); + j != i; ++j) + { + std::string hostname; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(j->url, ec); + if (hostname != udp_hostname) continue; + if (j->url.substr(0, 6) == "udp://") continue; + using std::swap; + using std::iter_swap; + swap(i->tier, j->tier); + iter_swap(i, j); + break; + } + } + } + + bool torrent::add_tracker(announce_entry const& url) + { + std::vector::iterator k = std::find_if(m_trackers.begin() + , m_trackers.end(), boost::bind(&announce_entry::url, _1) == url.url); + if (k != m_trackers.end()) + { + k->source |= url.source; + return false; + } + k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url + , boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); + if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker; + k = m_trackers.insert(k, url); + if (k->source == 0) k->source = announce_entry::source_client; + if (!m_trackers.empty()) announce_with_tracker(); + return true; + } + + bool torrent::choke_peer(peer_connection& c) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!c.is_choked()); + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + TORRENT_ASSERT(m_num_uploads > 0); + if (!c.send_choke()) return false; + --m_num_uploads; + state_updated(); + return true; + } + + bool torrent::unchoke_peer(peer_connection& c, bool optimistic) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_graceful_pause_mode); + TORRENT_ASSERT(c.is_choked()); + TORRENT_ASSERT(!c.ignore_unchoke_slots()); + // when we're unchoking the optimistic slots, we might + // exceed the limit temporarily while we're iterating + // over the peers + if (m_num_uploads >= m_max_uploads && !optimistic) return false; + if (!c.send_unchoke()) return false; + ++m_num_uploads; + state_updated(); + return true; + } + + void torrent::cancel_block(piece_block block) + { + INVARIANT_CHECK; + + for (peer_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + (*i)->cancel_request(block); + } + } + +#ifdef TORRENT_USE_OPENSSL + std::string password_callback(int length, boost::asio::ssl::context::password_purpose p + , std::string pw) + { + if (p != boost::asio::ssl::context::for_reading) return ""; + return pw; + } + + // certificate is a filename to a .pem file which is our + // certificate. The certificate must be signed by the root + // cert of the torrent file. any peer we connect to or that + // connect to use must present a valid certificate signed + // by the torrent root cert as well + void torrent::set_ssl_cert(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase) + { + if (!m_ssl_ctx) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle() + , error_code(errors::not_an_ssl_torrent))); + return; + } + + using boost::asio::ssl::context; + error_code ec; + m_ssl_ctx->set_password_callback(boost::bind(&password_callback, _1, _2, passphrase), ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_certificate_file(certificate, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_private_key_file(private_key, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + m_ssl_ctx->use_tmp_dh_file(dh_params, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + } + + void torrent::set_ssl_cert_buffer(std::string const& certificate + , std::string const& private_key + , std::string const& dh_params) + { + if (!m_ssl_ctx) return; + +#if BOOST_VERSION < 105400 + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle() + , error_code(boost::system::errc::not_supported, get_system_category()))); +#else + boost::asio::const_buffer certificate_buf(certificate.c_str(), certificate.size()); + + using boost::asio::ssl::context; + error_code ec; + m_ssl_ctx->use_certificate(certificate_buf, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + + boost::asio::const_buffer private_key_buf(private_key.c_str(), private_key.size()); + m_ssl_ctx->use_private_key(private_key_buf, context::pem, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } + + boost::asio::const_buffer dh_params_buf(dh_params.c_str(), dh_params.size()); + m_ssl_ctx->use_tmp_dh(dh_params_buf, ec); + if (ec) + { + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + } +#endif // BOOST_VERSION + } + +#endif + + void torrent::remove_peer(peer_connection* p) + { +// INVARIANT_CHECK; + + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(m_ses.is_network_thread()); + + peer_iterator i = m_connections.find(p); + if (i == m_connections.end()) + { + TORRENT_ASSERT(false); + return; + } + + if (ready_for_connections()) + { + TORRENT_ASSERT(p->associated_torrent().lock().get() == NULL + || p->associated_torrent().lock().get() == this); + + if (p->is_seed()) + { + if (m_picker.get()) + { + m_picker->dec_refcount_all(p); + } + } + else + { + if (m_picker.get()) + { + bitfield const& pieces = p->get_bitfield(); + TORRENT_ASSERT(pieces.count() <= int(pieces.size())); + m_picker->dec_refcount(pieces, p); + } + } + } + + if (!p->is_choked() && !p->ignore_unchoke_slots()) + { + --m_num_uploads; + m_ses.m_unchoke_time_scaler = 0; + } + + policy::peer* pp = p->peer_info_struct(); + if (pp) + { + if (pp->optimistically_unchoked) + m_ses.m_optimistic_unchoke_time_scaler = 0; + + TORRENT_ASSERT(pp->prev_amount_upload == 0); + TORRENT_ASSERT(pp->prev_amount_download == 0); + pp->prev_amount_download += p->statistics().total_payload_download() >> 10; + pp->prev_amount_upload += p->statistics().total_payload_upload() >> 10; + } + + m_policy.connection_closed(*p, m_ses.session_time()); + p->set_peer_info(0); + TORRENT_ASSERT(i != m_connections.end()); + m_connections.erase(i); + } + + void torrent::remove_web_seed(std::list::iterator web) + { + if (web->resolving) + { + web->removed = true; + return; + } + peer_connection * peer = web->peer_info.connection; + if (peer) + { + // if we have a connection for this web seed, we also need to + // disconnect it and clear its reference to the peer_info object + // that's part of the web_seed_entry we're about to remove + TORRENT_ASSERT(peer->m_in_use == 1337); + peer->disconnect(boost::asio::error::operation_aborted); + peer->set_peer_info(0); + } + if (has_picker()) picker().clear_peer(&web->peer_info); + + + m_web_seeds.erase(web); + } + + void torrent::connect_to_url_seed(std::list::iterator web) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + TORRENT_ASSERT(!web->resolving); + if (web->resolving) return; + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + std::string protocol; + std::string auth; + std::string hostname; + int port; + std::string path; + error_code ec; + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(web->url, ec); + if (port == -1) + { + port = protocol == "http" ? 80 : 443; + } + + if (ec) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("failed to parse web seed url: %s", ec.message().c_str()); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, ec)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (web->peer_info.banned) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("banned web seed: %s", web->url.c_str()); +#endif + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url + , error_code(libtorrent::errors::peer_banned, get_libtorrent_category()))); + } + // never try it again + remove_web_seed(web); + return; + } + +#ifdef TORRENT_USE_OPENSSL + if (protocol != "http" && protocol != "https") +#else + if (protocol != "http") +#endif + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::unsupported_url_protocol)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (hostname.empty()) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::invalid_hostname)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (port == 0) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::invalid_port)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (m_ses.m_port_filter.access(port) & port_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, errors::port_blocked)); + } + // never try it again + remove_web_seed(web); + return; + } + + if (web->endpoint.port() != 0) + { + connect_web_seed(web, web->endpoint); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving web seed: %s", web->url.c_str()); +#endif + + proxy_settings const& ps = m_ses.proxy(); + if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving proxy for web seed: %s", web->url.c_str()); +#endif + + // use proxy + web->resolving = true; + tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, web)); + } + else if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + connect_web_seed(web, tcp::endpoint(address(), port)); + } + else + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("resolving web seed: %s", web->url.c_str()); +#endif + + web->resolving = true; + tcp::resolver::query q(hostname, to_string(port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web + , tcp::endpoint())); + } + } + + void torrent::on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator host + , std::list::iterator web) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + TORRENT_ASSERT(web->resolving == true); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("completed resolve proxy hostname for: %s", web->url.c_str()); +#endif +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + if (e) + debug_log("proxy name lookup error: %s", e.message().c_str()); +#endif + web->resolving = false; + + if (web->removed) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("removed web seed"); +#endif + remove_web_seed(web); + return; + } + + if (m_abort) return; + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, e)); + } + + // the name lookup failed for the http host. Don't try + // this host again + remove_web_seed(web); + return; + } + + if (m_ses.is_aborted()) return; + +#ifndef TORRENT_DISABLE_GEO_IP + int as = m_ses.as_for_ip(host->endpoint().address()); +#ifdef TORRENT_DEBUG + web->peer_info.inet_as_num = as; +#endif + web->peer_info.inet_as = m_ses.lookup_as(as); +#endif + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + tcp::endpoint a(host->endpoint()); + + using boost::tuples::ignore; + std::string hostname; + int port; + error_code ec; + std::string protocol; + boost::tie(protocol, ignore, hostname, port, ignore) + = parse_url_components(web->url, ec); + if (port == -1) port = protocol == "http" ? 80 : 443; + + if (ec) + { + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), web->url, ec)); + } + remove_web_seed(web); + return; + } + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , a.address(), peer_blocked_alert::ip_filter)); + return; + } + + web->resolving = true; + tcp::resolver::query q(hostname, to_string(port).elems); + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web, a)); + } + + void torrent::on_name_lookup(error_code const& e, tcp::resolver::iterator host + , std::list::iterator web, tcp::endpoint proxy) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + TORRENT_ASSERT(web->resolving == true); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("completed resolve: %s", web->url.c_str()); +#endif + web->resolving = false; + if (web->removed) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("removed web seed"); +#endif + remove_web_seed(web); + return; + } + + if (m_abort) return; + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, e)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** HOSTNAME LOOKUP FAILED: %s: (%d) %s" + , web->url.c_str(), e.value(), e.message().c_str()); +#endif + + // unavailable, retry in 30 minutes + web->retry = time_now() + minutes(30); + return; + } + + tcp::endpoint a(host->endpoint()); + + // fill in the peer struct's address field + web->endpoint = a; + + if (int(m_connections.size()) >= m_max_connections + || m_ses.num_connections() >= m_ses.settings().connections_limit) + return; + + connect_web_seed(web, a); + } + + void torrent::connect_web_seed(std::list::iterator web, tcp::endpoint a) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , a.address(), peer_blocked_alert::ip_filter)); + return; + } + + TORRENT_ASSERT(web->resolving == false); + TORRENT_ASSERT(web->peer_info.connection == 0); + + web->endpoint = a; + if (a.address().is_v4()) + { + web->peer_info.addr = a.address().to_v4(); + web->peer_info.port = a.port(); + } + + if (is_paused()) return; + if (m_ses.is_aborted()) return; + + boost::shared_ptr s(new (std::nothrow) socket_type(m_ses.m_io_service)); + if (!s) return; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + bool ssl = string_begins_no_case("https://", web->url.c_str()); + if (ssl) + { + userdata = m_ssl_ctx.get(); + if (!userdata) userdata = &m_ses.m_ssl_ctx; + } +#endif + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, 0, true); + (void)ret; + TORRENT_ASSERT(ret); + + proxy_settings const& ps = m_ses.proxy(); + if (s->get()) + { + // the web seed connection will talk immediately to + // the proxy, without requiring CONNECT support + s->get()->set_no_connect(true); + } + + using boost::tuples::ignore; + std::string hostname; + error_code ec; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(web->url, ec); + if (ec) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, ec)); + return; + } + + if (ps.proxy_hostnames + && (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw)) + { + // we're using a socks proxy and we're resolving + // hostnames through it + socks5_stream* str = +#ifdef TORRENT_USE_OPENSSL + ssl ? &s->get >()->next_layer() : +#endif + s->get(); + TORRENT_ASSERT(str); + + str->set_dst_name(hostname); + } + + setup_ssl_hostname(*s, hostname, ec); + if (ec) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(url_seed_alert(get_handle(), web->url, ec)); + return; + } + + boost::intrusive_ptr c; + if (web->type == web_seed_entry::url_seed) + { + c = new (std::nothrow) web_peer_connection( + m_ses, shared_from_this(), s, a, *web); + } + else if (web->type == web_seed_entry::http_seed) + { + c = new (std::nothrow) http_seed_connection( + m_ses, shared_from_this(), s, a, *web); + } + if (!c) return; + +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr + pp((*i)->new_connection(c.get())); + if (pp) c->add_extension(pp); + } +#endif + + TORRENT_TRY + { + TORRENT_ASSERT(!c->m_in_constructor); + // add the newly connected peer to this torrent's peer list + m_connections.insert(boost::get_pointer(c)); + m_ses.m_connections.insert(c); + + TORRENT_ASSERT(!web->peer_info.connection); + web->peer_info.connection = c.get(); +#if TORRENT_USE_ASSERTS + web->peer_info.in_use = true; +#endif + + c->add_stat(size_type(web->peer_info.prev_amount_download) << 10 + , size_type(web->peer_info.prev_amount_upload) << 10); + web->peer_info.prev_amount_download = 0; + web->peer_info.prev_amount_upload = 0; +#if defined TORRENT_VERBOSE_LOGGING + debug_log("web seed connection started: %s", web->url.c_str()); +#endif + + c->start(); + + if (c->is_disconnecting()) return; + + m_ses.m_half_open.enqueue( + boost::bind(&peer_connection::on_connect, c, _1) + , boost::bind(&peer_connection::on_timeout, c) + , seconds(settings().peer_connect_timeout)); + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** HOST NAME LOOKUP FAILED: %s", e.what()); +#endif + c->disconnect(errors::no_error, 1); + } + } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + namespace + { + unsigned long swap_bytes(unsigned long a) + { + return (a >> 24) | ((a & 0xff0000) >> 8) | ((a & 0xff00) << 8) | ((a & 0xff) << 24); + } + } + + void torrent::resolve_peer_country(boost::intrusive_ptr const& p) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_resolving_country + || is_local(p->remote().address()) + || p->has_country() + || p->is_connecting() + || p->is_queued() + || p->in_handshake() + || p->remote().address().is_v6()) return; + + asio::ip::address_v4 reversed(swap_bytes(p->remote().address().to_v4().to_ulong())); + error_code ec; + tcp::resolver::query q(reversed.to_string(ec) + ".zz.countries.nerd.dk", "0"); + if (ec) + { + p->set_country("!!"); + return; + } + m_resolving_country = true; + m_host_resolver.async_resolve(q, + boost::bind(&torrent::on_country_lookup, shared_from_this(), _1, _2, p)); + } + + namespace + { + struct country_entry + { + int code; + char const* name; + }; + } + + void torrent::on_country_lookup(error_code const& error, tcp::resolver::iterator i + , intrusive_ptr p) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + + m_resolving_country = false; + + if (m_abort) return; + + // must be ordered in increasing order + static const country_entry country_map[] = + { + { 4, "AF"}, { 8, "AL"}, { 10, "AQ"}, { 12, "DZ"}, { 16, "AS"} + , { 20, "AD"}, { 24, "AO"}, { 28, "AG"}, { 31, "AZ"}, { 32, "AR"} + , { 36, "AU"}, { 40, "AT"}, { 44, "BS"}, { 48, "BH"}, { 50, "BD"} + , { 51, "AM"}, { 52, "BB"}, { 56, "BE"}, { 60, "BM"}, { 64, "BT"} + , { 68, "BO"}, { 70, "BA"}, { 72, "BW"}, { 74, "BV"}, { 76, "BR"} + , { 84, "BZ"}, { 86, "IO"}, { 90, "SB"}, { 92, "VG"}, { 96, "BN"} + , {100, "BG"}, {104, "MM"}, {108, "BI"}, {112, "BY"}, {116, "KH"} + , {120, "CM"}, {124, "CA"}, {132, "CV"}, {136, "KY"}, {140, "CF"} + , {144, "LK"}, {148, "TD"}, {152, "CL"}, {156, "CN"}, {158, "TW"} + , {162, "CX"}, {166, "CC"}, {170, "CO"}, {174, "KM"}, {175, "YT"} + , {178, "CG"}, {180, "CD"}, {184, "CK"}, {188, "CR"}, {191, "HR"} + , {192, "CU"}, {203, "CZ"}, {204, "BJ"}, {208, "DK"}, {212, "DM"} + , {214, "DO"}, {218, "EC"}, {222, "SV"}, {226, "GQ"}, {231, "ET"} + , {232, "ER"}, {233, "EE"}, {234, "FO"}, {238, "FK"}, {239, "GS"} + , {242, "FJ"}, {246, "FI"}, {248, "AX"}, {250, "FR"}, {254, "GF"} + , {258, "PF"}, {260, "TF"}, {262, "DJ"}, {266, "GA"}, {268, "GE"} + , {270, "GM"}, {275, "PS"}, {276, "DE"}, {288, "GH"}, {292, "GI"} + , {296, "KI"}, {300, "GR"}, {304, "GL"}, {308, "GD"}, {312, "GP"} + , {316, "GU"}, {320, "GT"}, {324, "GN"}, {328, "GY"}, {332, "HT"} + , {334, "HM"}, {336, "VA"}, {340, "HN"}, {344, "HK"}, {348, "HU"} + , {352, "IS"}, {356, "IN"}, {360, "ID"}, {364, "IR"}, {368, "IQ"} + , {372, "IE"}, {376, "IL"}, {380, "IT"}, {384, "CI"}, {388, "JM"} + , {392, "JP"}, {398, "KZ"}, {400, "JO"}, {404, "KE"}, {408, "KP"} + , {410, "KR"}, {414, "KW"}, {417, "KG"}, {418, "LA"}, {422, "LB"} + , {426, "LS"}, {428, "LV"}, {430, "LR"}, {434, "LY"}, {438, "LI"} + , {440, "LT"}, {442, "LU"}, {446, "MO"}, {450, "MG"}, {454, "MW"} + , {458, "MY"}, {462, "MV"}, {466, "ML"}, {470, "MT"}, {474, "MQ"} + , {478, "MR"}, {480, "MU"}, {484, "MX"}, {492, "MC"}, {496, "MN"} + , {498, "MD"}, {500, "MS"}, {504, "MA"}, {508, "MZ"}, {512, "OM"} + , {516, "NA"}, {520, "NR"}, {524, "NP"}, {528, "NL"}, {530, "AN"} + , {533, "AW"}, {540, "NC"}, {548, "VU"}, {554, "NZ"}, {558, "NI"} + , {562, "NE"}, {566, "NG"}, {570, "NU"}, {574, "NF"}, {578, "NO"} + , {580, "MP"}, {581, "UM"}, {583, "FM"}, {584, "MH"}, {585, "PW"} + , {586, "PK"}, {591, "PA"}, {598, "PG"}, {600, "PY"}, {604, "PE"} + , {608, "PH"}, {612, "PN"}, {616, "PL"}, {620, "PT"}, {624, "GW"} + , {626, "TL"}, {630, "PR"}, {634, "QA"}, {634, "QA"}, {638, "RE"} + , {642, "RO"}, {643, "RU"}, {646, "RW"}, {654, "SH"}, {659, "KN"} + , {660, "AI"}, {662, "LC"}, {666, "PM"}, {670, "VC"}, {674, "SM"} + , {678, "ST"}, {682, "SA"}, {686, "SN"}, {690, "SC"}, {694, "SL"} + , {702, "SG"}, {703, "SK"}, {704, "VN"}, {705, "SI"}, {706, "SO"} + , {710, "ZA"}, {716, "ZW"}, {724, "ES"}, {732, "EH"}, {736, "SD"} + , {740, "SR"}, {744, "SJ"}, {748, "SZ"}, {752, "SE"}, {756, "CH"} + , {760, "SY"}, {762, "TJ"}, {764, "TH"}, {768, "TG"}, {772, "TK"} + , {776, "TO"}, {780, "TT"}, {784, "AE"}, {788, "TN"}, {792, "TR"} + , {795, "TM"}, {796, "TC"}, {798, "TV"}, {800, "UG"}, {804, "UA"} + , {807, "MK"}, {818, "EG"}, {826, "GB"}, {834, "TZ"}, {840, "US"} + , {850, "VI"}, {854, "BF"}, {858, "UY"}, {860, "UZ"}, {862, "VE"} + , {876, "WF"}, {882, "WS"}, {887, "YE"}, {891, "CS"}, {894, "ZM"} + }; + + if (error || i == tcp::resolver::iterator()) + { + // this is used to indicate that we shouldn't + // try to resolve it again + p->set_country("--"); + return; + } + + while (i != tcp::resolver::iterator() + && !i->endpoint().address().is_v4()) ++i; + if (i != tcp::resolver::iterator()) + { + // country is an ISO 3166 country code + int country = i->endpoint().address().to_v4().to_ulong() & 0xffff; + + // look up the country code in the map + const int size = sizeof(country_map)/sizeof(country_map[0]); + country_entry tmp = {country, ""}; + country_entry const* j = + std::lower_bound(country_map, country_map + size, tmp + , boost::bind(&country_entry::code, _1) < boost::bind(&country_entry::code, _2)); + if (j == country_map + size + || j->code != country) + { + // unknown country! + p->set_country("!!"); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("IP \"%s\" was mapped to unknown country: %d" + , print_address(p->remote().address()).c_str(), country); +#endif + return; + } + + p->set_country(j->name); + } + } +#endif + + void torrent::read_resume_data(lazy_entry const& rd) + { + m_total_uploaded = rd.dict_find_int_value("total_uploaded"); + m_total_downloaded = rd.dict_find_int_value("total_downloaded"); + m_active_time = rd.dict_find_int_value("active_time"); + m_finished_time = rd.dict_find_int_value("finished_time"); + m_seeding_time = rd.dict_find_int_value("seeding_time"); + m_last_seen_complete = rd.dict_find_int_value("last_seen_complete"); + m_complete = rd.dict_find_int_value("num_complete", 0xffffff); + m_incomplete = rd.dict_find_int_value("num_incomplete", 0xffffff); + m_downloaded = rd.dict_find_int_value("num_downloaded", 0xffffff); + + if (!m_override_resume_data) + { + int up_limit_ = rd.dict_find_int_value("upload_rate_limit", -1); + if (up_limit_ != -1) set_upload_limit(up_limit_); + + int down_limit_ = rd.dict_find_int_value("download_rate_limit", -1); + if (down_limit_ != -1) set_download_limit(down_limit_); + + int max_connections_ = rd.dict_find_int_value("max_connections", -1); + if (max_connections_ != -1) set_max_connections(max_connections_); + + int max_uploads_ = rd.dict_find_int_value("max_uploads", -1); + if (max_uploads_ != -1) set_max_uploads(max_uploads_); + + int seed_mode_ = rd.dict_find_int_value("seed_mode", -1); + if (seed_mode_ != -1) m_seed_mode = seed_mode_ && m_torrent_file->is_valid(); + + int super_seeding_ = rd.dict_find_int_value("super_seeding", -1); + if (super_seeding_ != -1) m_super_seeding = super_seeding_; + + int auto_managed_ = rd.dict_find_int_value("auto_managed", -1); + if (auto_managed_ != -1) m_auto_managed = auto_managed_; + + int sequential_ = rd.dict_find_int_value("sequential_download", -1); + if (sequential_ != -1) set_sequential_download(sequential_); + + int paused_ = rd.dict_find_int_value("paused", -1); + if (paused_ != -1) + { + set_allow_peers(!paused_); + m_announce_to_dht = !paused_; + m_announce_to_trackers = !paused_; + m_announce_to_lsd = !paused_; + } + int dht_ = rd.dict_find_int_value("announce_to_dht", -1); + if (dht_ != -1) m_announce_to_dht = dht_; + int lsd_ = rd.dict_find_int_value("announce_to_lsd", -1); + if (lsd_ != -1) m_announce_to_lsd = lsd_; + int track_ = rd.dict_find_int_value("announce_to_trackers", -1); + if (track_ != -1) m_announce_to_trackers = track_; + } + + if (m_seed_mode) + m_verified.resize(m_torrent_file->num_pieces(), false); + + m_last_scrape = rd.dict_find_int_value("last_scrape", 0); + m_last_download = rd.dict_find_int_value("last_download", 0); + m_last_upload = rd.dict_find_int_value("last_upload", 0); + + if (m_use_resume_save_path) + { + std::string p = rd.dict_find_string_value("save_path"); + if (!p.empty()) m_save_path = p; + } + + m_url = rd.dict_find_string_value("url"); + m_uuid = rd.dict_find_string_value("uuid"); + m_source_feed_url = rd.dict_find_string_value("feed"); + + if (!m_uuid.empty() || !m_url.empty()) + { + boost::shared_ptr me(shared_from_this()); + + // insert this torrent in the uuid index + m_ses.m_uuids.insert(std::make_pair(m_uuid.empty() + ? m_url : m_uuid, me)); + } + + // TODO: make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance + // maybe use the same format as .torrent files and reuse some code from torrent_info + // The mapped_files needs to be read both in the network thread + // and in the disk thread, since they both have their own mapped files structures + // which are kept in sync + lazy_entry const* mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files->list_size() == m_torrent_file->num_files()) + { + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + std::string new_filename = mapped_files->list_string_value_at(i); + if (new_filename.empty()) continue; + m_torrent_file->rename_file(i, new_filename); + } + } + + m_added_time = rd.dict_find_int_value("added_time", m_added_time); + m_completed_time = rd.dict_find_int_value("completed_time", m_completed_time); + if (m_completed_time != 0 && m_completed_time < m_added_time) + m_completed_time = m_added_time; + + if (!m_seed_mode && !m_override_resume_data) + { + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == m_torrent_file->num_files()) + { + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = file_priority->list_int_value_at(i, 1); + update_piece_priorities(); + } + } + + lazy_entry const* trackers = rd.dict_find_list("trackers"); + if (trackers) + { + if (!m_merge_resume_trackers) m_trackers.clear(); + int tier = 0; + for (int i = 0; i < trackers->list_size(); ++i) + { + lazy_entry const* tier_list = trackers->list_at(i); + if (tier_list == 0 || tier_list->type() != lazy_entry::list_t) + continue; + for (int j = 0; j < tier_list->list_size(); ++j) + { + announce_entry e(tier_list->list_string_value_at(j)); + if (std::find_if(m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::url, _1) == e.url) != m_trackers.end()) + continue; + e.tier = tier; + e.fail_limit = 0; + m_trackers.push_back(e); + } + ++tier; + } + std::sort(m_trackers.begin(), m_trackers.end(), boost::bind(&announce_entry::tier, _1) + < boost::bind(&announce_entry::tier, _2)); + + if (settings().prefer_udp_trackers) + prioritize_udp_trackers(); + } + + lazy_entry const* url_list = rd.dict_find_list("url-list"); + if (url_list) + { + for (int i = 0; i < url_list->list_size(); ++i) + { + std::string url = url_list->list_string_value_at(i); + if (url.empty()) continue; + if (m_torrent_file->num_files() > 1 && url[url.size()-1] != '/') url += '/'; + add_web_seed(url, web_seed_entry::url_seed); + } + } + + lazy_entry const* httpseeds = rd.dict_find_list("httpseeds"); + if (httpseeds) + { + for (int i = 0; i < httpseeds->list_size(); ++i) + { + std::string url = httpseeds->list_string_value_at(i); + if (url.empty()) continue; + add_web_seed(url, web_seed_entry::http_seed); + } + } + + if (m_torrent_file->is_merkle_torrent()) + { + lazy_entry const* mt = rd.dict_find_string("merkle tree"); + if (mt) + { + std::vector tree; + tree.resize(m_torrent_file->merkle_tree().size()); + std::memcpy(&tree[0], mt->string_ptr() + , (std::min)(mt->string_length(), int(tree.size()) * 20)); + if (mt->string_length() < int(tree.size()) * 20) + std::memset(&tree[0] + mt->string_length() / 20, 0 + , tree.size() - mt->string_length() / 20); + m_torrent_file->set_merkle_tree(tree); + } + else + { + // TODO: 0 if this is a merkle torrent and we can't + // restore the tree, we need to wipe all the + // bits in the have array, but not necessarily + // we might want to do a full check to see if we have + // all the pieces. This is low priority since almost + // no one uses merkle torrents + TORRENT_ASSERT(false); + } + } + } + + boost::intrusive_ptr torrent::get_torrent_copy() + { + if (!m_torrent_file->is_valid()) return boost::intrusive_ptr(); + + // copy the torrent_info object + return boost::intrusive_ptr(new torrent_info(*m_torrent_file)); + } + + void torrent::write_resume_data(entry& ret) const + { + using namespace libtorrent::detail; // for write_*_endpoint() + ret["file-format"] = "libtorrent resume file"; + ret["file-version"] = 1; + ret["libtorrent-version"] = LIBTORRENT_VERSION; + + ret["total_uploaded"] = m_total_uploaded; + ret["total_downloaded"] = m_total_downloaded; + + ret["active_time"] = m_active_time; + ret["finished_time"] = m_finished_time; + ret["seeding_time"] = m_seeding_time; + ret["last_seen_complete"] = m_last_seen_complete; + + ret["num_complete"] = m_complete; + ret["num_incomplete"] = m_incomplete; + ret["num_downloaded"] = m_downloaded; + + ret["sequential_download"] = m_sequential_download; + + ret["seed_mode"] = m_seed_mode; + ret["super_seeding"] = m_super_seeding; + + ret["added_time"] = m_added_time; + ret["completed_time"] = m_completed_time; + + ret["last_scrape"] = m_last_scrape; + ret["last_download"] = m_last_download; + ret["last_upload"] = m_last_upload; + + ret["save_path"] = m_save_path; + + if (!m_url.empty()) ret["url"] = m_url; + if (!m_uuid.empty()) ret["uuid"] = m_uuid; + if (!m_source_feed_url.empty()) ret["feed"] = m_source_feed_url; + + const sha1_hash& info_hash = torrent_file().info_hash(); + ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); + + if (valid_metadata()) + { + if (m_magnet_link || (m_save_resume_flags & torrent_handle::save_info_dict)) + ret["info"] = bdecode(&torrent_file().metadata()[0] + , &torrent_file().metadata()[0] + torrent_file().metadata_size()); + } + + // blocks per piece + int num_blocks_per_piece = + static_cast(torrent_file().piece_length()) / block_size(); + ret["blocks per piece"] = num_blocks_per_piece; + + if (m_torrent_file->is_merkle_torrent()) + { + // we need to save the whole merkle hash tree + // in order to resume + std::string& tree_str = ret["merkle tree"].string(); + std::vector const& tree = m_torrent_file->merkle_tree(); + tree_str.resize(tree.size() * 20); + std::memcpy(&tree_str[0], &tree[0], tree.size() * 20); + } + + // if this torrent is a seed, we won't have a piece picker + // and there will be no half-finished pieces. + if (has_picker()) + { + const std::vector& q + = m_picker->get_download_queue(); + + // unfinished pieces + ret["unfinished"] = entry::list_type(); + entry::list_type& up = ret["unfinished"].list(); + + // info for each unfinished piece + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i) + { + if (i->finished == 0) continue; + + entry piece_struct(entry::dictionary_t); + + // the unfinished piece's index + piece_struct["piece"] = i->index; + + std::string bitmask; + const int num_bitmask_bytes + = (std::max)(num_blocks_per_piece / 8, 1); + + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char v = 0; + int bits = (std::min)(num_blocks_per_piece - j*8, 8); + for (int k = 0; k < bits; ++k) + v |= (i->info[j*8+k].state == piece_picker::block_info::state_finished) + ? (1 << k) : 0; + bitmask.append(1, v); + TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1); + } + piece_struct["bitmask"] = bitmask; + // push the struct onto the unfinished-piece list + up.push_back(piece_struct); + } + } + + // save trackers + entry::list_type& tr_list = ret["trackers"].list(); + tr_list.push_back(entry::list_type()); + int tier = 0; + for (std::vector::const_iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + // don't save trackers we can't trust + // TODO: 1 save the send_stats state instead of throwing them away + // it may pose an issue when downgrading though + if (i->send_stats == false) continue; + if (i->tier == tier) + { + tr_list.back().list().push_back(i->url); + } + else + { + tr_list.push_back(entry::list_t); + tr_list.back().list().push_back(i->url); + tier = i->tier; + } + } + + // save web seeds + if (!m_web_seeds.empty()) + { + entry::list_type& url_list = ret["url-list"].list(); + entry::list_type& httpseed_list = ret["httpseeds"].list(); + for (std::list::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->type == web_seed_entry::url_seed) + url_list.push_back(i->url); + else if (i->type == web_seed_entry::http_seed) + httpseed_list.push_back(i->url); + } + } + + // write have bitmask + // the pieces string has one byte per piece. Each + // byte is a bitmask representing different properties + // for the piece + // bit 0: set if we have the piece + // bit 1: set if we have verified the piece (in seed mode) + entry::string_type& pieces = ret["pieces"].string(); + pieces.resize(m_torrent_file->num_pieces()); + if (is_seed()) + { + std::memset(&pieces[0], 1, pieces.size()); + } + else if (has_picker()) + { + for (int i = 0, end(pieces.size()); i < end; ++i) + pieces[i] = m_picker->have_piece(i) ? 1 : 0; + } + + if (m_seed_mode) + { + TORRENT_ASSERT(m_verified.size() == pieces.size()); + for (int i = 0, end(pieces.size()); i < end; ++i) + pieces[i] |= m_verified[i] ? 2 : 0; + } + + // write renamed files + // TODO: 0 make this more generic to not just work if files have been + // renamed, but also if they have been merged into a single file for instance. + // using file_base + if (&m_torrent_file->files() != &m_torrent_file->orig_files() + && m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files()) + { + entry::list_type& fl = ret["mapped_files"].list(); + file_storage const& fs = m_torrent_file->files(); + for (int i = 0; i < fs.num_files(); ++i) + { + fl.push_back(fs.file_path(i)); + } + } + + // write local peers + + std::back_insert_iterator peers(ret["peers"].string()); + std::back_insert_iterator banned_peers(ret["banned_peers"].string()); +#if TORRENT_USE_IPV6 + std::back_insert_iterator peers6(ret["peers6"].string()); + std::back_insert_iterator banned_peers6(ret["banned_peers6"].string()); +#endif + + // failcount is a 5 bit value + int max_failcount = (std::min)(settings().max_failcount, 31); + + int num_saved_peers = 0; + + for (policy::const_iterator i = m_policy.begin_peer() + , end(m_policy.end_peer()); i != end; ++i) + { + error_code ec; + policy::peer const* p = *i; + address addr = p->address(); + if (p->banned) + { +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + { + write_address(addr, banned_peers6); + write_uint16(p->port, banned_peers6); + } + else +#endif + { + write_address(addr, banned_peers); + write_uint16(p->port, banned_peers); + } + continue; + } + + // we cannot save remote connection + // since we don't know their listen port + // unless they gave us their listen port + // through the extension handshake + // so, if the peer is not connectable (i.e. we + // don't know its listen port) or if it has + // been banned, don't save it. + if (!p->connectable) continue; + + // don't save peers that don't work + if (int(p->failcount) >= max_failcount) continue; + + // the more peers we've saved, the more picky we get + // about which ones are worth saving + if (num_saved_peers > 10 + && int (p->failcount) > 0 + && int(p->failcount) > (40 - (num_saved_peers - 10)) * max_failcount / 40) + continue; + + // if we have 40 peers, don't save any peers whom + // we've only heard from through the resume data + if (num_saved_peers > 40 && p->source == peer_info::resume_data) + continue; + +#if TORRENT_USE_IPV6 + if (addr.is_v6()) + { + write_address(addr, peers6); + write_uint16(p->port, peers6); + } + else +#endif + { + write_address(addr, peers); + write_uint16(p->port, peers); + } + ++num_saved_peers; + } + + ret["upload_rate_limit"] = upload_limit(); + ret["download_rate_limit"] = download_limit(); + ret["max_connections"] = max_connections(); + ret["max_uploads"] = max_uploads(); + ret["paused"] = is_torrent_paused(); + ret["announce_to_dht"] = m_announce_to_dht; + ret["announce_to_trackers"] = m_announce_to_trackers; + ret["announce_to_lsd"] = m_announce_to_lsd; + ret["auto_managed"] = m_auto_managed; + + // write piece priorities + entry::string_type& piece_priority = ret["piece_priority"].string(); + piece_priority.resize(m_torrent_file->num_pieces()); + if (is_seed()) + { + std::memset(&piece_priority[0], 1, pieces.size()); + } + else if (has_picker()) + { + for (int i = 0, end(piece_priority.size()); i < end; ++i) + piece_priority[i] = m_picker->piece_priority(i); + } + + // write file priorities + entry::list_type& file_priority = ret["file_priority"].list(); + file_priority.clear(); + for (int i = 0, end(m_file_priority.size()); i < end; ++i) + file_priority.push_back(m_file_priority[i]); + } + + void torrent::get_full_peer_list(std::vector& v) const + { + v.clear(); + v.reserve(m_policy.num_peers()); + for (policy::const_iterator i = m_policy.begin_peer(); + i != m_policy.end_peer(); ++i) + { + peer_list_entry e; + e.ip = (*i)->ip(); + e.flags = (*i)->banned ? peer_list_entry::banned : 0; + e.failcount = (*i)->failcount; + e.source = (*i)->source; + v.push_back(e); + } + } + + void torrent::get_peer_info(std::vector& v) + { + v.clear(); + for (peer_iterator i = begin(); + i != end(); ++i) + { + peer_connection* peer = *i; + TORRENT_ASSERT(peer->m_in_use == 1337); + + // incoming peers that haven't finished the handshake should + // not be included in this list + if (peer->associated_torrent().expired()) continue; + + v.push_back(peer_info()); + peer_info& p = v.back(); + + peer->get_peer_info(p); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + if (resolving_countries()) + resolve_peer_country(intrusive_ptr(peer)); +#endif + } + } + + void torrent::get_download_queue(std::vector* queue) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + queue->clear(); + std::vector& blk = m_ses.m_block_info_storage; + blk.clear(); + + if (!valid_metadata() || !has_picker()) return; + piece_picker const& p = picker(); + std::vector const& q + = p.get_download_queue(); + + const int blocks_per_piece = m_picker->blocks_in_piece(0); + blk.resize(q.size() * blocks_per_piece); + // for some weird reason valgrind claims these are uninitialized + // unless it's zeroed out here (block_info has a construct that's + // supposed to initialize it) + if (!blk.empty()) + memset(&blk[0], 0, sizeof(blk[0]) * blk.size()); + + int counter = 0; + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i, ++counter) + { + partial_piece_info pi; + pi.piece_state = (partial_piece_info::state_t)i->state; + pi.blocks_in_piece = p.blocks_in_piece(i->index); + pi.finished = (int)i->finished; + pi.writing = (int)i->writing; + pi.requested = (int)i->requested; + TORRENT_ASSERT(counter * blocks_per_piece + pi.blocks_in_piece <= int(blk.size())); + pi.blocks = &blk[counter * blocks_per_piece]; + int piece_size = int(torrent_file().piece_size(i->index)); + for (int j = 0; j < pi.blocks_in_piece; ++j) + { + block_info& bi = pi.blocks[j]; + bi.state = i->info[j].state; + bi.block_size = j < pi.blocks_in_piece - 1 ? block_size() + : piece_size - (j * block_size()); + bool complete = bi.state == block_info::writing + || bi.state == block_info::finished; + if (i->info[j].peer == 0) + { + bi.set_peer(tcp::endpoint()); + bi.bytes_progress = complete ? bi.block_size : 0; + } + else + { + policy::peer* p = static_cast(i->info[j].peer); + if (p->connection) + { + bi.set_peer(p->connection->remote()); + if (bi.state == block_info::requested) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == j) + { + bi.bytes_progress = pbp->bytes_downloaded; + TORRENT_ASSERT(bi.bytes_progress <= bi.block_size); + } + else + { + bi.bytes_progress = 0; + } + } + else + { + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + else + { + bi.set_peer(p->ip()); + bi.bytes_progress = complete ? bi.block_size : 0; + } + } + + pi.blocks[j].num_peers = i->info[j].num_peers; + } + pi.piece_index = i->index; + queue->push_back(pi); + } + + } + + bool torrent::connect_to_peer(policy::peer* peerinfo, bool ignore_limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + TORRENT_ASSERT(peerinfo); + TORRENT_ASSERT(peerinfo->connection == 0); + + peerinfo->last_connected = m_ses.session_time(); +#ifdef TORRENT_DEBUG + if (!settings().allow_multiple_connections_per_ip) + { + // this asserts that we don't have duplicates in the policy's peer list + peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == peerinfo->ip()); +#if TORRENT_USE_I2P + TORRENT_ASSERT(i_ == m_connections.end() + || (*i_)->type() != peer_connection::bittorrent_connection + || peerinfo->is_i2p_addr); +#else + TORRENT_ASSERT(i_ == m_connections.end() + || (*i_)->type() != peer_connection::bittorrent_connection); +#endif + } +#endif + + // extend connect timeout by this many seconds + int timeout_extend = 0; + + TORRENT_ASSERT(want_more_peers() || ignore_limit); + TORRENT_ASSERT(m_ses.num_connections() < m_ses.settings().connections_limit || ignore_limit); + + tcp::endpoint a(peerinfo->ip()); + TORRENT_ASSERT(!m_apply_ip_filter + || (m_ses.m_ip_filter.access(peerinfo->address()) & ip_filter::blocked) == 0); + + boost::shared_ptr s(new socket_type(m_ses.m_io_service)); + +#if TORRENT_USE_I2P + bool i2p = peerinfo->is_i2p_addr; + if (i2p) + { + if (m_ses.i2p_proxy().hostname.empty()) + { + // we have an i2p torrent, but we're not connected to an i2p + // SAM proxy. + if (alerts().should_post()) + alerts().post_alert(i2p_alert(error_code(errors::no_i2p_router + , get_libtorrent_category()))); + return false; + } + + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.i2p_proxy(), *s); + (void)ret; + TORRENT_ASSERT(ret); + s->get()->set_destination(static_cast(peerinfo)->destination); + s->get()->set_command(i2p_stream::cmd_connect); + s->get()->set_session_id(m_ses.m_i2p_conn.session_id()); + // i2p setups are slow + timeout_extend = 20; + } + else +#endif + { + // this is where we determine if we open a regular TCP connection + // or a uTP connection. If the m_utp_socket_manager pointer is not passed in + // we'll instantiate a TCP connection + utp_socket_manager* sm = 0; + + if (m_ses.m_settings.enable_outgoing_utp + && (!m_ses.m_settings.enable_outgoing_tcp + || peerinfo->supports_utp + || peerinfo->confirmed_supports_utp)) + sm = &m_ses.m_utp_socket_manager; + + // don't make a TCP connection if it's disabled + if (sm == 0 && !m_ses.m_settings.enable_outgoing_tcp) return false; + + void* userdata = 0; +#ifdef TORRENT_USE_OPENSSL + if (is_ssl_torrent() && m_ses.settings().ssl_listen != 0) + { + userdata = m_ssl_ctx.get(); + // SSL handshakes are slow + timeout_extend = 10; + } +#endif + + bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, sm, true); + (void)ret; + TORRENT_ASSERT(ret); + +#if defined TORRENT_USE_OPENSSL && BOOST_VERSION >= 104700 + if (is_ssl_torrent()) + { + // for ssl sockets, set the hostname + std::string host_name = to_hex(m_torrent_file->info_hash().to_string()); + +#define CASE(t) case socket_type_int_impl >::value: \ + s->get >()->set_host_name(host_name); break; + + switch (s->type()) + { + CASE(stream_socket) + CASE(socks5_stream) + CASE(http_stream) + CASE(utp_stream) + default: break; + }; + } +#undef CASE +#endif + } + + m_ses.setup_socket_buffers(*s); + + boost::intrusive_ptr c(new bt_peer_connection( + m_ses, s, a, peerinfo, m_ses.get_peer_id(), shared_from_this(), true)); + +#if TORRENT_USE_ASSERTS + c->m_in_constructor = false; +#endif + + c->add_stat(size_type(peerinfo->prev_amount_download) << 10 + , size_type(peerinfo->prev_amount_upload) << 10); + peerinfo->prev_amount_download = 0; + peerinfo->prev_amount_upload = 0; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + boost::shared_ptr pp((*i)->new_connection(c.get())); + if (pp) c->add_extension(pp); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + // add the newly connected peer to this torrent's peer list + m_connections.insert(boost::get_pointer(c)); + m_ses.m_connections.insert(c); + m_policy.set_connection(peerinfo, c.get()); + c->start(); + + int timeout = settings().peer_connect_timeout; + if (peerinfo) timeout += 3 * peerinfo->failcount; + timeout += timeout_extend; + + TORRENT_TRY + { + m_ses.m_half_open.enqueue( + boost::bind(&peer_connection::on_connect, c, _1) + , boost::bind(&peer_connection::on_timeout, c) + , seconds(timeout)); + } + TORRENT_CATCH (std::exception&) + { + std::set::iterator i + = m_connections.find(boost::get_pointer(c)); + if (i != m_connections.end()) m_connections.erase(i); + c->disconnect(errors::no_error, 1); + return false; + } + + if (m_share_mode) + recalc_share_mode(); + + return peerinfo->connection; + } + + bool torrent::set_metadata(char const* metadata_buf, int metadata_size) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_torrent_file->is_valid()) return false; + + hasher h; + h.update(metadata_buf, metadata_size); + sha1_hash info_hash = h.final(); + + if (info_hash != m_torrent_file->info_hash()) + { + if (alerts().should_post()) + { + alerts().post_alert(metadata_failed_alert(get_handle() + , error_code(errors::mismatching_info_hash, get_libtorrent_category()))); + } + return false; + } + + lazy_entry metadata; + error_code ec; + int ret = lazy_bdecode(metadata_buf, metadata_buf + metadata_size, metadata, ec); + if (ret != 0 || !m_torrent_file->parse_info_section(metadata, ec, 0)) + { + // this means the metadata is correct, since we + // verified it against the info-hash, but we + // failed to parse it. Pause the torrent + if (alerts().should_post()) + { + alerts().post_alert(metadata_failed_alert(get_handle(), ec)); + } + set_error(errors::invalid_swarm_metadata, ""); + pause(); + return false; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle())); + } + + // this makes the resume data "paused" and + // "auto_managed" fields be ignored. If the paused + // field is not ignored, the invariant check will fail + // since we will be paused but without having disconnected + // any of the peers. + m_override_resume_data = true; + + // we have to initialize the torrent before we start + // disconnecting redundant peers, otherwise we'll think + // we're a seed, because we have all 0 pieces + init(); + + // disconnect redundant peers + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + std::set::iterator p = i++; + (*p)->disconnect_if_redundant(); + } + + m_need_save_resume_data = true; + + return true; + } + + bool connecting_time_compare(peer_connection const* lhs, peer_connection const* rhs) + { + bool lhs_connecting = lhs->is_connecting() && !lhs->is_disconnecting(); + bool rhs_connecting = rhs->is_connecting() && !rhs->is_disconnecting(); + if (lhs_connecting > rhs_connecting) return false; + if (lhs_connecting < rhs_connecting) return true; + + // a lower value of connected_time means it's been waiting + // longer. This is a less-than comparison, so if lhs has + // waited longer than rhs, we should return false. + return lhs->connected_time() > rhs->connected_time(); + } + + bool torrent::attach_peer(peer_connection* p) + { +// INVARIANT_CHECK; + +#ifdef TORRENT_USE_OPENSSL +#if BOOST_VERSION >= 104700 + if (is_ssl_torrent()) + { + // if this is an SSL torrent, don't allow non SSL peers on it + boost::shared_ptr s = p->get_socket(); + + // +#define SSL(t) socket_type_int_impl >::value: \ + ssl_conn = s->get >()->native_handle(); \ + break; + + SSL* ssl_conn = 0; + + switch (s->type()) + { + case SSL(stream_socket) + case SSL(socks5_stream) + case SSL(http_stream) + case SSL(utp_stream) + }; + +#undef SSL + + if (ssl_conn == 0) + { + // don't allow non SSL peers on SSL torrents + p->disconnect(errors::requires_ssl_connection); + return false; + } + + if (!m_ssl_ctx) + { + // we don't have a valid cert, don't accept any connection! + p->disconnect(errors::invalid_ssl_cert); + return false; + } + + if (SSL_get_SSL_CTX(ssl_conn) != m_ssl_ctx->native_handle()) + { + // if the SSL_CTX associated with this connection is + // not the one belonging to this torrent, the SSL handshake + // connected to one torrent, and the BitTorrent protocol + // to a different one. This is probably an attempt to circumvent + // access control. Don't allow it. + p->disconnect(errors::invalid_ssl_cert); + return false; + } + } +#else // BOOST_VERSION + if (is_ssl_torrent()) + { + p->disconnect(asio::error::operation_not_supported); + return false; + } +#endif +#else // TORRENT_USE_OPENSSL + if (is_ssl_torrent()) + { + // Don't accidentally allow seeding of SSL torrents, just + // because libtorrent wasn't built with SSL support + p->disconnect(errors::requires_ssl_connection); + return false; + } +#endif // TORRENT_USE_OPENSSL + + TORRENT_ASSERT(p != 0); + TORRENT_ASSERT(!p->is_outgoing()); + + m_has_incoming = true; + + if (m_apply_ip_filter + && m_ses.m_ip_filter.access(p->remote().address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle() + , p->remote().address(), peer_blocked_alert::ip_filter)); + p->disconnect(errors::banned_by_ip_filter); + return false; + } + + if ((m_state == torrent_status::queued_for_checking + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) + && valid_metadata()) + { + p->disconnect(errors::torrent_not_ready); + return false; + } + + if (m_ses.m_connections.find(p) == m_ses.m_connections.end()) + { + p->disconnect(errors::peer_not_constructed); + return false; + } + + if (m_ses.is_aborted()) + { + p->disconnect(errors::session_closing); + return false; + } + + bool maybe_replace_peer = false; + + if (m_connections.size() >= m_max_connections) + { + // if more than 10% of the connections are outgoing + // connection attempts that haven't completed yet, + // disconnect one of them and let this incoming + // connection through. + if (m_num_connecting > m_max_connections / 10) + { + // find one of the connecting peers and disconnect it + // find any peer that's connecting (i.e. a half-open TCP connection) + // that's also not disconnecting + + // disconnect the peer that's been wating to establish a connection + // the longest + std::set::iterator i = std::max_element(begin(), end() + , &connecting_time_compare); + + if (i == end() || !(*i)->is_connecting() || (*i)->is_disconnecting()) + { + // this seems odd, but we might as well handle it + p->disconnect(errors::too_many_connections); + return false; + } + (*i)->disconnect(errors::too_many_connections); + + // if this peer was let in via connections slack, + // it has done its duty of causing the disconnection + // of another peer + p->peer_disconnected_other(); + } + else + { + maybe_replace_peer = true; + } + } + + TORRENT_TRY + { +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr pp((*i)->new_connection(p)); + if (pp) p->add_extension(pp); + } +#endif + if (!m_policy.new_connection(*p, m_ses.session_time())) + { +#if defined TORRENT_LOGGING + debug_log("CLOSING CONNECTION \"%s\" peer list full" + , print_endpoint(p->remote()).c_str()); +#endif + p->disconnect(errors::too_many_connections); + return false; + } + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_LOGGING + debug_log("CLOSING CONNECTION \"%s\" caught exception: %s" + , print_endpoint(p->remote()).c_str(), e.what()); +#endif + p->disconnect(errors::no_error); + return false; + } + TORRENT_ASSERT(m_connections.find(p) == m_connections.end()); + m_connections.insert(p); +#ifdef TORRENT_DEBUG + error_code ec; + TORRENT_ASSERT(p->remote() == p->get_socket()->remote_endpoint(ec) || ec); +#endif + + TORRENT_ASSERT(p->peer_info_struct() != NULL); + + // we need to do this after we've added the peer to the policy + // since that's when the peer is assigned its peer_info object, + // which holds the rank + if (maybe_replace_peer) + { + // now, find the lowest rank peer and disconnect that + // if it's lower rank than the incoming connection + peer_connection* peer = find_lowest_ranking_peer(); + + // TODO: 3 if peer is a really good peer, maybe we shouldn't disconnect it + if (peer && peer->peer_rank() < p->peer_rank()) + { + peer->disconnect(errors::too_many_connections); + p->peer_disconnected_other(); + } + else + { + p->disconnect(errors::too_many_connections); + // we have to do this here because from the peer's point of + // it wasn't really attached to the torrent, but we do need + // to let policy know we're removing it + remove_peer(p); + return false; + } + } + +#if TORRENT_USE_INVARIANT_CHECKS + m_policy.check_invariant(); +#endif + + if (m_share_mode) + recalc_share_mode(); + + return true; + } + + bool torrent::want_more_peers() const + { + return m_connections.size() < m_max_connections + && !is_paused() + && ((m_state != torrent_status::checking_files + && m_state != torrent_status::checking_resume_data + && m_state != torrent_status::queued_for_checking) + || !valid_metadata()) + && m_policy.num_connect_candidates() > 0 + && !m_abort + && (m_ses.settings().seeding_outgoing_connections + || (m_state != torrent_status::seeding + && m_state != torrent_status::finished)); + } + + void torrent::disconnect_all(error_code const& ec) + { +// doesn't work with the !m_allow_peers -> m_num_peers == 0 condition +// INVARIANT_CHECK; + + while (!m_connections.empty()) + { + peer_connection* p = *m_connections.begin(); + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CLOSING CONNECTION \"%s\"", ec.message().c_str()); +#endif +#if TORRENT_USE_ASSERTS + std::size_t size = m_connections.size(); +#endif + if (p->is_disconnecting()) + m_connections.erase(m_connections.begin()); + else + p->disconnect(ec); + TORRENT_ASSERT(m_connections.size() <= size); + } + } + + // this returns true if lhs is a better disconnect candidate than rhs + bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs) + { + // prefer to disconnect peers that are already disconnecting + if (lhs->is_disconnecting() != rhs->is_disconnecting()) + return lhs->is_disconnecting(); + + // prefer to disconnect peers we're not interested in + if (lhs->is_interesting() != rhs->is_interesting()) + return rhs->is_interesting(); + + // prefer to disconnect peers that are not seeds + if (lhs->is_seed() != rhs->is_seed()) + return rhs->is_seed(); + + // prefer to disconnect peers that are on parole + if (lhs->on_parole() != rhs->on_parole()) + return lhs->on_parole(); + + // prefer to disconnect peers that send data at a lower rate + size_type lhs_transferred = lhs->statistics().total_payload_download(); + size_type rhs_transferred = rhs->statistics().total_payload_download(); + + ptime now = time_now(); + size_type lhs_time_connected = total_seconds(now - lhs->connected_time()); + size_type rhs_time_connected = total_seconds(now - rhs->connected_time()); + + lhs_transferred /= lhs_time_connected + 1; + rhs_transferred /= (rhs_time_connected + 1); + if (lhs_transferred != rhs_transferred) + return lhs_transferred < rhs_transferred; + + // prefer to disconnect peers that chokes us + if (lhs->is_choked() != rhs->is_choked()) + return lhs->is_choked(); + + return lhs->last_received() < rhs->last_received(); + } + + int torrent::disconnect_peers(int num, error_code const& ec) + { + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + // make sure this peer is not a dangling pointer + TORRENT_ASSERT(m_ses.has_peer(*i)); + } +#endif + int ret = 0; + while (ret < num && !m_connections.empty()) + { + std::set::iterator i = std::min_element( + m_connections.begin(), m_connections.end(), compare_disconnect_peer); + + peer_connection* p = *i; + ++ret; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); +#if TORRENT_USE_ASSERTS + int num_conns = m_connections.size(); +#endif + p->disconnect(ec); + TORRENT_ASSERT(int(m_connections.size()) == num_conns - 1); + } + + return ret; + } + + int torrent::bandwidth_throttle(int channel) const + { + return m_bandwidth_channel[channel].throttle(); + } + + // called when torrent is finished (all interesting + // pieces have been downloaded) + void torrent::finished() + { + INVARIANT_CHECK; + + TORRENT_ASSERT(is_finished()); + TORRENT_ASSERT(m_state != torrent_status::finished && m_state != torrent_status::seeding); + + set_state(torrent_status::finished); + set_queue_position(-1); + + // we have to call completed() before we start + // disconnecting peers, since there's an assert + // to make sure we're cleared the piece picker + if (is_seed()) completed(); + + send_upload_only(); + + state_updated(); + + if (m_completed_time == 0) + m_completed_time = time(0); + + // disconnect all seeds + if (settings().close_redundant_connections) + { + // TODO: 1 should disconnect all peers that have the pieces we have + // not just seeds. It would be pretty expensive to check all pieces + // for all peers though + std::vector seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = *i; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + if (p->upload_only()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** SEED, CLOSING CONNECTION"); +#endif + seeds.push_back(p); + } + } + std::for_each(seeds.begin(), seeds.end() + , boost::bind(&peer_connection::disconnect, _1, errors::torrent_finished, 0)); + } + + if (m_abort) return; + + m_policy.recalculate_connect_candidates(); + + TORRENT_ASSERT(m_storage); + // we need to keep the object alive during this operation + m_storage->async_release_files( + boost::bind(&torrent::on_files_released, shared_from_this(), _1, _2)); + + // this torrent just completed downloads, which means it will fall + // under a different limit with the auto-manager. Make sure we + // update auto-manage torrents in that case + if (m_auto_managed) + m_ses.trigger_auto_manage(); + } + + // this is called when we were finished, but some files were + // marked for downloading, and we are no longer finished + void torrent::resume_download() + { + INVARIANT_CHECK; + + if (m_state == torrent_status::checking_resume_data + || m_state == torrent_status::checking_files + || m_state == torrent_status::allocating) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** RESUME_DOWNLOAD [ skipping, state: %d ]" + , int(m_state)); +#endif + return; + } + + TORRENT_ASSERT(!is_finished()); + set_state(torrent_status::downloading); + set_queue_position((std::numeric_limits::max)()); + m_policy.recalculate_connect_candidates(); + + m_completed_time = 0; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("*** RESUME_DOWNLOAD"); +#endif + send_upload_only(); + } + + // called when torrent is complete (all pieces downloaded) + void torrent::completed() + { + m_picker.reset(); + + set_state(torrent_status::seeding); + if (!m_announcing) return; + + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->complete_sent) continue; + i->next_announce = now; + i->min_announce = now; + } + announce_with_tracker(); + } + + // this will move the tracker with the given index + // to a prioritized position in the list (move it towards + // the begining) and return the new index to the tracker. + int torrent::prioritize_tracker(int index) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_trackers.size())); + if (index >= (int)m_trackers.size()) return -1; + + while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) + { + using std::swap; + swap(m_trackers[index], m_trackers[index-1]); + if (m_last_working_tracker == index) --m_last_working_tracker; + else if (m_last_working_tracker == index - 1) ++m_last_working_tracker; + --index; + } + return index; + } + + int torrent::deprioritize_tracker(int index) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < int(m_trackers.size())); + if (index >= (int)m_trackers.size()) return -1; + + while (index < int(m_trackers.size()) - 1 && m_trackers[index].tier == m_trackers[index + 1].tier) + { + using std::swap; + swap(m_trackers[index], m_trackers[index + 1]); + if (m_last_working_tracker == index) ++m_last_working_tracker; + else if (m_last_working_tracker == index + 1) --m_last_working_tracker; + ++index; + } + return index; + } + + void torrent::files_checked() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(m_torrent_file->is_valid()); + + if (m_abort) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("files_checked(), paused"); +#endif + return; + } + + // we might be finished already, in which case we should + // not switch to downloading mode. If all files are + // filtered, we're finished when we start. + if (m_state != torrent_status::finished + && m_state != torrent_status::seeding) + set_state(torrent_status::downloading); + + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(torrent_checked_alert( + get_handle())); + } + + // calling pause will also trigger the auto managed + // recalculation + // if we just got here by downloading the metadata, + // just keep going, no need to disconnect all peers just + // to restart the torrent in a second + if (m_auto_managed) + { + // if this is an auto managed torrent, force a recalculation + // of which torrents to have active + m_ses.trigger_auto_manage(); + } + + if (!is_seed()) + { + // turn off super seeding if we're not a seed + if (m_super_seeding) m_super_seeding = false; + + // if we just finished checking and we're not a seed, we are + // likely to be unpaused + m_ses.trigger_auto_manage(); + + if (is_finished() && m_state != torrent_status::finished) + finished(); + } + else + { + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->complete_sent = true; + + if (m_state != torrent_status::finished + && m_state != torrent_status::seeding) + finished(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_files_checked(); + } TORRENT_CATCH (std::exception&) {} + } +#endif + + m_connections_initialized = true; + m_files_checked = true; + + for (torrent::peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* pc = *i; + ++i; + + // all peer connections have to initialize themselves now that the metadata + // is available + if (!m_connections_initialized) + { + if (pc->is_disconnecting()) continue; + pc->on_metadata_impl(); + if (pc->is_disconnecting()) continue; + pc->init(); + } + +#ifdef TORRENT_VERBOSE_LOGGING + pc->peer_log("*** ON_FILES_CHECKED"); +#endif + if (pc->is_interesting() && !pc->has_peer_choked()) + { + request_a_block(*this, *pc); + pc->send_block_requests(); + } + } + + + start_announcing(); + + maybe_connect_web_seeds(); + } + + alert_manager& torrent::alerts() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return m_ses.m_alerts; + } + + std::string torrent::save_path() const + { + return m_save_path; + } + + bool torrent::rename_file(int index, std::string const& name) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_torrent_file->num_files()); + + if (!m_owning_storage.get()) return false; + + m_owning_storage->async_rename_file(index, name + , boost::bind(&torrent::on_file_renamed, shared_from_this(), _1, _2)); + return true; + } + + void torrent::move_storage(std::string const& save_path, int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_abort) + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_failed_alert(get_handle(), boost::asio::error::operation_aborted)); + return; + } + + // storage may be NULL during shutdown + if (m_owning_storage.get()) + { +#if TORRENT_USE_UNC_PATHS + std::string path = canonicalize_path(save_path); +#else + std::string const& path = save_path; +#endif + m_owning_storage->async_move_storage(path, flags + , boost::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2)); + m_moving_storage = true; + } + else + { +#if TORRENT_USE_UNC_PATHS + m_save_path = canonicalize_path(save_path); +#else + + m_save_path = save_path; +#endif + m_need_save_resume_data = true; + + if (alerts().should_post()) + { + alerts().post_alert(storage_moved_alert(get_handle(), m_save_path)); + } + } + } + + void torrent::on_storage_moved(int ret, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + m_moving_storage = false; + + if (ret == piece_manager::no_error || ret == piece_manager::need_full_check) + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_alert(get_handle(), j.str)); + m_save_path = j.str; + m_need_save_resume_data = true; + if (ret == piece_manager::need_full_check) + force_recheck(); + } + else + { + if (alerts().should_post()) + alerts().post_alert(storage_moved_failed_alert(get_handle(), j.error)); + } + } + + piece_manager& torrent::filesystem() + { + TORRENT_ASSERT(m_owning_storage.get()); + TORRENT_ASSERT(m_storage); + return *m_storage; + } + + + torrent_handle torrent::get_handle() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return torrent_handle(shared_from_this()); + } + + session_settings const& torrent::settings() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return m_ses.settings(); + } + +#if TORRENT_USE_INVARIANT_CHECKS + void torrent::check_invariant() const + { + for (std::deque::const_iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { + TORRENT_ASSERT(!is_seed()); + TORRENT_ASSERT(!has_picker() || !m_picker->have_piece(i->piece)); + } + + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode); + + if (!should_check_files()) + TORRENT_ASSERT(m_state != torrent_status::checking_files); + else + TORRENT_ASSERT(m_queued_for_checking); + + if (m_torrent_file) + { + TORRENT_ASSERT(m_info_hash == m_torrent_file->info_hash()); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + if (!m_ses.m_queued_for_checking.empty()) + { + // if there are torrents waiting to be checked + // assert that there's a torrent that is being + // processed right now + int found = 0; + int found_active = 0; + for (aux::session_impl::torrent_map::iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + if (i->second->m_state == torrent_status::checking_files) + { + ++found; + if (i->second->should_check_files()) ++found_active; + } + + // if the session is paused, there might still be some torrents + // in the checking_files state that haven't been dequeued yet + if (m_ses.is_paused()) + { + TORRENT_ASSERT(found_active == 0); + } + else + { + // the case of 2 is in the special case where one switches over from + // checking to complete. + TORRENT_ASSERT(found_active >= 1); + TORRENT_ASSERT(found_active <= 2); + TORRENT_ASSERT(found >= 1); + } + } +#endif + + TORRENT_ASSERT(m_resume_entry.type() == lazy_entry::dict_t + || m_resume_entry.type() == lazy_entry::none_t); + + int num_uploads = 0; + std::map num_requests; + for (const_peer_iterator i = this->begin(); i != this->end(); ++i) + { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // make sure this peer is not a dangling pointer + TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif + peer_connection const& p = *(*i); + for (std::vector::const_iterator i = p.request_queue().begin() + , end(p.request_queue().end()); i != end; ++i) + ++num_requests[i->block]; + for (std::vector::const_iterator i = p.download_queue().begin() + , end(p.download_queue().end()); i != end; ++i) + if (!i->not_wanted && !i->timed_out) ++num_requests[i->block]; + if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads; + torrent* associated_torrent = p.associated_torrent().lock().get(); + if (associated_torrent != this && associated_torrent != 0) + TORRENT_ASSERT(false); + } + TORRENT_ASSERT(num_uploads == int(m_num_uploads)); + + if (has_picker()) + { + for (std::map::iterator i = num_requests.begin() + , end(num_requests.end()); i != end; ++i) + { + piece_block b = i->first; + int count = i->second; + int picker_count = m_picker->num_peers(b); + // if we're no longer downloading the piece + // (for instance, it may be fully downloaded and waiting + // for the hash check to return), the piece picker always + // returns 0 requests, regardless of how many peers may still + // have the block in their queue + if (!m_picker->is_downloaded(b) && m_picker->is_downloading(b.piece_index)) + TORRENT_ASSERT(picker_count == count); + } + TORRENT_ASSERT(num_have() >= m_picker->num_have_filtered()); + } + + if (valid_metadata()) + { + TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == m_torrent_file->num_pieces()); + } + else + { + TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == 0); + } + +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS + // make sure we haven't modified the peer object + // in a way that breaks the sort order + if (m_policy.begin_peer() != m_policy.end_peer()) + { + policy::const_iterator i = m_policy.begin_peer(); + policy::const_iterator prev = i++; + policy::const_iterator end(m_policy.end_peer()); + policy::peer_address_compare cmp; + for (; i != end; ++i, ++prev) + { + TORRENT_ASSERT(!cmp(*i, *prev)); + } + } +#endif + + size_type total_done = quantized_bytes_done(); + if (m_torrent_file->is_valid()) + { + if (is_seed()) + TORRENT_ASSERT(total_done == m_torrent_file->total_size()); + else + TORRENT_ASSERT(total_done != m_torrent_file->total_size() || !m_files_checked); + + TORRENT_ASSERT(block_size() <= m_torrent_file->piece_length()); + } + else + { + TORRENT_ASSERT(total_done == 0); + } +/* + if (m_picker && !m_abort) + { + // make sure that pieces that have completed the download + // of all their blocks are in the disk io thread's queue + // to be checked. + const std::vector& dl_queue + = m_picker->get_download_queue(); + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + const int blocks_per_piece = m_picker->blocks_in_piece(i->index); + + bool complete = true; + for (int j = 0; j < blocks_per_piece; ++j) + { + if (i->info[j].state == piece_picker::block_info::state_finished) + continue; + complete = false; + break; + } + TORRENT_ASSERT(complete); + } + } +*/ + if (m_files_checked && valid_metadata()) + { + TORRENT_ASSERT(block_size() > 0); + } + + + for (std::vector::const_iterator i = m_file_progress.begin() + , end(m_file_progress.end()); i != end; ++i) + { + int index = i - m_file_progress.begin(); + TORRENT_ASSERT(*i <= m_torrent_file->files().file_size(index)); + } + } +#endif + + void torrent::set_sequential_download(bool sd) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_sequential_download == sd) return; + m_sequential_download = sd; + + m_need_save_resume_data = true; + + state_updated(); + } + + void torrent::queue_up() + { + set_queue_position(queue_position() == 0 + ? queue_position() : queue_position() - 1); + } + + void torrent::queue_down() + { + set_queue_position(queue_position() + 1); + } + + void torrent::set_queue_position(int p) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT((p == -1) == is_finished() + || (!m_auto_managed && p == -1) + || (m_abort && p == -1)); + if (is_finished() && p != -1) return; + if (p == m_sequence_number) return; + + state_updated(); + + session_impl::torrent_map& torrents = m_ses.m_torrents; + if (p >= 0 && m_sequence_number == -1) + { + int max_seq = -1; + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t->m_sequence_number > max_seq) max_seq = t->m_sequence_number; + if (t->m_sequence_number >= p) + { + ++t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = (std::min)(max_seq + 1, p); + } + else if (p < 0) + { + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t == this) continue; + if (t->m_sequence_number >= m_sequence_number + && t->m_sequence_number != -1) + { + --t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = p; + } + else if (p < m_sequence_number) + { + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + if (t == this) continue; + if (t->m_sequence_number >= p + && t->m_sequence_number < m_sequence_number + && t->m_sequence_number != -1) + { + ++t->m_sequence_number; + t->state_updated(); + } + } + m_sequence_number = p; + } + else if (p > m_sequence_number) + { + int max_seq = 0; + for (session_impl::torrent_map::iterator i = torrents.begin() + , end(torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + int pos = t->m_sequence_number; + if (pos > max_seq) max_seq = pos; + if (t == this) continue; + + if (pos <= p + && pos > m_sequence_number + && pos != -1) + { + --t->m_sequence_number; + t->state_updated(); + } + + } + m_sequence_number = (std::min)(max_seq, p); + } + + m_ses.m_auto_manage_time_scaler = 2; + } + + void torrent::set_max_uploads(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = (1<<24)-1; + if (m_max_uploads != limit && state_update) state_updated(); + m_max_uploads = limit; + + m_need_save_resume_data = true; + } + + void torrent::set_max_connections(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = (1<<24)-1; + if (m_max_connections != limit && state_update) state_updated(); + m_max_connections = limit; + + if (num_peers() > int(m_max_connections)) + { + disconnect_peers(num_peers() - m_max_connections + , error_code(errors::too_many_connections, get_libtorrent_category())); + } + + m_need_save_resume_data = true; + } + + int torrent::get_peer_upload_limit(tcp::endpoint ip) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + const_peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return -1; + return (*i)->get_upload_limit(); + } + + int torrent::get_peer_download_limit(tcp::endpoint ip) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + const_peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return -1; + return (*i)->get_download_limit(); + } + + void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return; + (*i)->set_upload_limit(limit); + } + + void torrent::set_peer_download_limit(tcp::endpoint ip, int limit) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + peer_iterator i = std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&peer_connection::remote, _1) == ip); + if (i == m_connections.end()) return; + (*i)->set_download_limit(limit); + } + + void torrent::set_upload_limit(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::upload_channel].throttle() != limit + && state_update) + state_updated(); + m_bandwidth_channel[peer_connection::upload_channel].throttle(limit); + + m_need_save_resume_data = true; + } + + int torrent::upload_limit() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + int limit = m_bandwidth_channel[peer_connection::upload_channel].throttle(); + if (limit == (std::numeric_limits::max)()) limit = -1; + return limit; + } + + void torrent::set_download_limit(int limit, bool state_update) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(limit >= -1); + if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::download_channel].throttle() != limit + && state_update) + state_updated(); + m_bandwidth_channel[peer_connection::download_channel].throttle(limit); + + m_need_save_resume_data = true; + } + + int torrent::download_limit() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + int limit = m_bandwidth_channel[peer_connection::download_channel].throttle(); + if (limit == (std::numeric_limits::max)()) limit = -1; + return limit; + } + + bool torrent::delete_files() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("DELETING FILES IN TORRENT"); +#endif + + disconnect_all(errors::torrent_removed); + stop_announcing(); + + // storage may be NULL during shutdown + if (m_owning_storage.get()) + { + TORRENT_ASSERT(m_storage); + m_storage->async_delete_files( + boost::bind(&torrent::on_files_deleted, shared_from_this(), _1, _2)); + m_deleted = true; + return true; + } + return false; + } + + void torrent::clear_error() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_error) return; + bool checking_files = should_check_files(); + m_ses.trigger_auto_manage(); + m_error = error_code(); + m_error_file.clear(); + + state_updated(); + + // if we haven't downloaded the metadata from m_url, try again + if (!m_url.empty() && !m_torrent_file->is_valid()) + { + start_download_url(); + return; + } + // if the error happened during initialization, try again now + if (!m_storage) init(); + if (!checking_files && should_check_files()) + queue_torrent_check(); + } + + void torrent::set_error(error_code const& ec, std::string const& error_file) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + bool checking_files = should_check_files(); + m_error = ec; + m_error_file = error_file; + + if (alerts().should_post()) + alerts().post_alert(torrent_error_alert(get_handle(), ec)); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + if (ec) + { + char buf[1024]; + snprintf(buf, sizeof(buf), "TORRENT ERROR: %s: %s", ec.message().c_str(), error_file.c_str()); + log_to_all_peers(buf); + } +#endif + + if (checking_files && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + } + + state_updated(); + } + + void torrent::auto_managed(bool a) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_auto_managed == a) return; + bool checking_files = should_check_files(); + m_auto_managed = a; + + state_updated(); + + // we need to save this new state as well + m_need_save_resume_data = true; + + // recalculate which torrents should be + // paused + m_ses.trigger_auto_manage(); + + if (!checking_files && should_check_files()) + { + queue_torrent_check(); + } + else if (checking_files && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + } + + // if this torrent is running and just became auto-managed + // we might want to pause it in favor of some other torrent + if (m_auto_managed && !is_paused()) + m_ses.m_auto_manage_time_scaler = 2; + } + + // the higher seed rank, the more important to seed + int torrent::seed_rank(session_settings const& s) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + enum flags + { + seed_ratio_not_met = 0x40000000, + no_seeds = 0x20000000, + recently_started = 0x10000000, + prio_mask = 0x0fffffff + }; + + if (!is_finished()) return 0; + + int scale = 1000; + if (!is_seed()) scale = 500; + + int ret = 0; + + ptime now = time_now(); + + int finished_time = m_finished_time; + int download_time = int(m_active_time) - finished_time; + + // if we haven't yet met the seed limits, set the seed_ratio_not_met + // flag. That will make this seed prioritized + // downloaded may be 0 if the torrent is 0-sized + size_type downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size()); + if (finished_time < s.seed_time_limit + && (download_time > 1 && finished_time / float(download_time) < s.seed_time_ratio_limit) + && downloaded > 0 + && m_total_uploaded / float(downloaded) < s.share_ratio_limit) + ret |= seed_ratio_not_met; + + // if this torrent is running, and it was started less + // than 30 minutes ago, give it priority, to avoid oscillation + if (!is_paused() && now - m_started < minutes(30)) + ret |= recently_started; + + // if we have any scrape data, use it to calculate + // seed rank + int seeds = 0; + int downloaders = 0; + + if (m_complete != 0xffffff) seeds = m_complete; + else seeds = m_policy.num_seeds(); + + if (m_incomplete != 0xffffff) downloaders = m_incomplete; + else downloaders = m_policy.num_peers() - m_policy.num_seeds(); + + if (seeds == 0) + { + ret |= no_seeds; + ret |= downloaders & prio_mask; + } + else + { + ret |= ((1 + downloaders) * scale / seeds) & prio_mask; + } + + return ret; + } + + // this is an async operation triggered by the client + void torrent::save_resume_data(int flags) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (!valid_metadata()) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle() + , errors::no_metadata)); + return; + } + + // storage may be NULL during shutdown + if (!m_owning_storage.get() || !m_storage) + { + alerts().post_alert(save_resume_data_failed_alert(get_handle() + , errors::destructing_torrent)); + return; + } + + m_need_save_resume_data = false; + m_last_saved_resume = time(0); + m_save_resume_flags = boost::uint8_t(flags); + state_updated(); + + TORRENT_ASSERT(m_storage); + if (m_state == torrent_status::queued_for_checking + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) + { + boost::shared_ptr rd(new entry); + write_resume_data(*rd); + alerts().post_alert(save_resume_data_alert(rd + , get_handle())); + return; + } + + if ((flags & torrent_handle::flush_disk_cache)) + m_storage->async_release_files(); + + m_storage->async_save_resume_data( + boost::bind(&torrent::on_save_resume_data, shared_from_this(), _1, _2)); + } + + bool torrent::should_check_files() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // #error should m_allow_peers really affect checking? + return (m_state == torrent_status::checking_files + || m_state == torrent_status::queued_for_checking) + && (m_allow_peers || m_auto_managed) + && !has_error() + && !m_abort + && !m_graceful_pause_mode + && !m_ses.is_paused(); + } + + void torrent::flush_cache() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + // storage may be NULL during shutdown + if (!m_owning_storage) + { + TORRENT_ASSERT(m_abort); + return; + } + m_storage->async_release_files( + boost::bind(&torrent::on_cache_flushed, shared_from_this(), _1, _2)); + } + + void torrent::on_cache_flushed(int /* ret */, disk_io_job const& j) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (m_ses.is_aborted()) return; + + if (alerts().should_post()) + alerts().post_alert(cache_flushed_alert(get_handle())); + } + + bool torrent::is_paused() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + return !m_allow_peers || m_ses.is_paused() || m_graceful_pause_mode; + } + + void torrent::pause(bool graceful) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (!m_allow_peers) return; + if (!graceful) set_allow_peers(false); + m_announce_to_dht = false; + m_announce_to_trackers = false; + m_announce_to_lsd = false; + + // we need to save this new state + m_need_save_resume_data = true; + state_updated(); + + bool prev_graceful = m_graceful_pause_mode; + m_graceful_pause_mode = graceful; + + if (!m_ses.is_paused() || (prev_graceful && !m_graceful_pause_mode)) + { + do_pause(); + // if this torrent was just paused + // we might have to resume some other auto-managed torrent + m_ses.trigger_auto_manage(); + } + } + + void torrent::do_pause() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!is_paused()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + if ((*i)->on_pause()) return; + } TORRENT_CATCH (std::exception&) {} + } +#endif + + m_inactive = false; + + state_updated(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + log_to_all_peers("PAUSING TORRENT"); +#endif + + // this will make the storage close all + // files and flush all cached data + if (m_owning_storage.get()) + { + TORRENT_ASSERT(m_storage); + m_storage->async_release_files( + boost::bind(&torrent::on_torrent_paused, shared_from_this(), _1, _2)); + m_storage->async_clear_read_cache(); + } + else + { + if (alerts().should_post()) + alerts().post_alert(torrent_paused_alert(get_handle())); + } + + if (!m_graceful_pause_mode) + { + disconnect_all(errors::torrent_paused); + } + else + { + // disconnect all peers with no outstanding data to receive + // and choke all remaining peers to prevent responding to new + // requests + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + std::set::iterator j = i++; + peer_connection* p = *j; + TORRENT_ASSERT(p->associated_torrent().lock().get() == this); + + if (p->is_disconnecting()) + { + m_connections.erase(j); + continue; + } + + if (p->outstanding_bytes() > 0) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CHOKING PEER: torrent graceful paused"); +#endif + // remove any un-sent requests from the queue + p->clear_request_queue(); + // don't accept new requests from the peer + if (!p->is_choked()) m_ses.choke_peer(*p); + continue; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** CLOSING CONNECTION: torrent_paused"); +#endif + p->disconnect(errors::torrent_paused); + } + } + + stop_announcing(); + + if (m_queued_for_checking && !should_check_files()) + { + // stop checking + m_storage->abort_disk_io(); + dequeue_torrent_check(); + set_state(torrent_status::queued_for_checking); + TORRENT_ASSERT(!m_queued_for_checking); + } + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING + void torrent::log_to_all_peers(char const* message) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + (*i)->peer_log("*** %s", message); + } +#endif + + debug_log("%s", message); + } +#endif + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type) + { + web_seed_entry ent(url, type); + // don't add duplicates + if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; + m_web_seeds.push_back(ent); + } + + void torrent::add_web_seed(std::string const& url, web_seed_entry::type_t type + , std::string const& auth, web_seed_entry::headers_t const& extra_headers) + { + web_seed_entry ent(url, type, auth, extra_headers); + // don't add duplicates + if (std::find(m_web_seeds.begin(), m_web_seeds.end(), ent) != m_web_seeds.end()) return; + m_web_seeds.push_back(ent); + } + + void torrent::set_allow_peers(bool b, bool graceful) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + if (m_allow_peers == b + && m_graceful_pause_mode == graceful) return; + + m_allow_peers = b; + if (!m_ses.is_paused()) + m_graceful_pause_mode = graceful; + + if (!b) + { + m_announce_to_dht = false; + m_announce_to_trackers = false; + m_announce_to_lsd = false; + do_pause(); + } + else + { + do_resume(); + } + + update_guage(); + } + + void torrent::resume() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + if (m_allow_peers + && m_announce_to_dht + && m_announce_to_trackers + && m_announce_to_lsd) return; + m_announce_to_dht = true; + m_announce_to_trackers = true; + m_announce_to_lsd = true; + // this call will trigger a tracker announce, that's + // why it's important to set announce_to_trackers to + // true first + set_allow_peers(true); + if (!m_ses.is_paused()) m_graceful_pause_mode = false; + + // we need to save this new state + m_need_save_resume_data = true; + + do_resume(); + } + + void torrent::do_resume() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + if ((*i)->on_resume()) return; + } TORRENT_CATCH (std::exception&) {} + } +#endif + + if (alerts().should_post()) + alerts().post_alert(torrent_resumed_alert(get_handle())); + + state_updated(); + + m_started = time_now(); + clear_error(); + start_announcing(); + if (!m_queued_for_checking && should_check_files()) + queue_torrent_check(); + } + + void torrent::update_tracker_timer(ptime now) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_announcing) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** update tracker timer: not announcing"); +#endif + return; + } + + ptime next_announce = max_time(); + int tier = INT_MAX; + + bool found_working = false; + + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + char msg[1000]; + snprintf(msg, sizeof(msg), "*** update tracker timer: considering \"%s\" " + "[ announce_to_all_tiers: %d announce_to_all_trackers: %d" + " found_working: %d i->tier: %d tier: %d " + " is_working: %d fails: %d fail_limit: %d updating: %d ]" + , i->url.c_str(), settings().announce_to_all_tiers + , settings().announce_to_all_trackers, found_working + , i->tier, tier, i->is_working(), i->fails, i->fail_limit + , i->updating); + debug_log(msg); +#endif + if (settings().announce_to_all_tiers + && found_working + && i->tier <= tier + && tier != INT_MAX) + continue; + + if (i->tier > tier && !settings().announce_to_all_tiers) break; + if (i->is_working()) { tier = i->tier; found_working = false; } + if (i->fails >= i->fail_limit && i->fail_limit != 0) continue; + if (i->updating) + { + found_working = true; + } + else + { + ptime next_tracker_announce = (std::max)(i->next_announce, i->min_announce); + if (next_tracker_announce < next_announce + && (!found_working || i->is_working())) + next_announce = next_tracker_announce; + } + if (i->is_working()) found_working = true; + if (found_working + && !settings().announce_to_all_trackers + && !settings().announce_to_all_tiers) break; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + char msg[200]; + snprintf(msg, sizeof(msg), "*** update tracker timer: next_announce < now %d" + " m_waiting_tracker: %d next_announce_in: %d" + , next_announce <= now, m_waiting_tracker, total_seconds(now - next_announce)); + debug_log(msg); +#endif + if (next_announce <= now) next_announce = now; + + // don't re-issue the timer if it's the same expiration time as last time + // if m_waiting_tracker is false, expires_at() is undefined + if (m_waiting_tracker && m_tracker_timer.expires_at() == next_announce) return; + + m_waiting_tracker = true; + error_code ec; + boost::weak_ptr self(shared_from_this()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("tracker::on_tracker_announce_disp"); +#endif + m_tracker_timer.expires_at(next_announce, ec); + m_tracker_timer.async_wait(boost::bind(&torrent::on_tracker_announce_disp, self, _1)); + } + + void torrent::start_announcing() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (is_paused()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), paused"); +#endif + return; + } + // if we don't have metadata, we need to announce + // before checking files, to get peers to + // request the metadata from + if (!m_files_checked && valid_metadata()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), files not checked (with valid metadata)"); +#endif + return; + } + if (!m_torrent_file->is_valid() && !m_url.empty()) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("start_announcing(), downloading URL"); +#endif + return; + } + if (m_announcing) return; + + m_announcing = true; + +#ifndef TORRENT_DISABLE_DHT + if (m_policy.num_peers() < 50 && m_ses.m_dht) + { + // we don't have any peers, prioritize + // announcing this torrent with the DHT + m_ses.prioritize_dht(shared_from_this()); + } +#endif + + if (!m_trackers.empty()) + { + // tell the tracker that we're back + std::for_each(m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::reset, _1)); + } + + // reset the stats, since from the tracker's + // point of view, this is a new session + m_total_failed_bytes = 0; + m_total_redundant_bytes = 0; + m_stat.clear(); + + announce_with_tracker(); + + // private torrents are never announced on LSD + // or on DHT, we don't need this timer. + if (!m_torrent_file->is_valid() + || (!m_torrent_file->priv() + && (!m_torrent_file->is_i2p() + || settings().allow_i2p_mixed))) + { + if (m_ses.m_lsd) lsd_announce(); + } + } + + void torrent::stop_announcing() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!m_announcing) return; + + error_code ec; + m_tracker_timer.cancel(ec); + + m_announcing = false; + + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + i->next_announce = now; + i->min_announce = now; + } + announce_with_tracker(tracker_request::stopped); + } + + void torrent::second_tick(stat& accumulator, int tick_interval_ms) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + boost::weak_ptr self(shared_from_this()); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->tick(); + } TORRENT_CATCH (std::exception&) {} + } + + if (m_abort) return; +#endif + + m_time_scaler--; + if (m_time_scaler <= 0) + { + m_time_scaler = 10; + + if (settings().max_sparse_regions > 0 + && m_picker + && m_picker->sparse_regions() > settings().max_sparse_regions) + { + // we have too many sparse regions. Prioritize pieces + // that won't introduce new sparse regions + // prioritize pieces that will reduce the number of sparse + // regions even higher + int start = m_picker->cursor(); + int end = m_picker->reverse_cursor(); + for (int i = start; i < end; ++i) + update_sparse_piece_prio(i, start, end); + } + } + + // if we're in upload only mode and we're auto-managed + // leave upload mode every 10 minutes hoping that the error + // condition has been fixed + if (m_upload_mode && m_auto_managed && int(m_upload_mode_time) + >= settings().optimistic_disk_retry) + { + set_upload_mode(false); + } + + if (is_paused() && !m_graceful_pause_mode) + { + // let the stats fade out to 0 + accumulator += m_stat; + m_stat.second_tick(tick_interval_ms); + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); + return; + } + + if (settings().rate_limit_ip_overhead) + { + int up_limit = m_bandwidth_channel[peer_connection::upload_channel].throttle(); + int down_limit = m_bandwidth_channel[peer_connection::download_channel].throttle(); + + if (down_limit > 0 + && m_stat.download_ip_overhead() >= down_limit + && alerts().should_post()) + { + alerts().post_alert(performance_alert(get_handle() + , performance_alert::download_limit_too_low)); + } + + if (up_limit > 0 + && m_stat.upload_ip_overhead() >= up_limit + && alerts().should_post()) + { + alerts().post_alert(performance_alert(get_handle() + , performance_alert::upload_limit_too_low)); + } + } + + int seconds_since_last_tick = 1; + if (m_ses.m_tick_residual >= 1000) ++seconds_since_last_tick; + + if (is_seed()) m_seeding_time += seconds_since_last_tick; + if (is_finished()) m_finished_time += seconds_since_last_tick; + if (m_upload_mode) m_upload_mode_time += seconds_since_last_tick; + m_last_scrape += seconds_since_last_tick; + m_active_time += seconds_since_last_tick; + m_last_download += seconds_since_last_tick; + m_last_upload += seconds_since_last_tick; + + // ---- TIME CRITICAL PIECES ---- + +#if TORRENT_DEBUG_STREAMING > 0 + std::vector queue; + get_download_queue(&queue); + + std::vector peer_list; + get_peer_info(peer_list); + + std::sort(queue.begin(), queue.end(), boost::bind(&partial_piece_info::piece_index, _1) + < boost::bind(&partial_piece_info::piece_index, _2)); + + printf("average piece download time: %.2f s (+/- %.2f s)\n" + , m_average_piece_time / 1000.f + , m_piece_time_deviation / 1000.f); + for (std::vector::iterator i = queue.begin() + , end(queue.end()); i != end; ++i) + { + extern void print_piece(libtorrent::partial_piece_info* pp + , std::vector const& peers + , std::vector const& time_critical); + + print_piece(&*i, peer_list, m_time_critical_pieces); + } +#endif // TORRENT_DEBUG_STREAMING + + if (!m_time_critical_pieces.empty() && !upload_mode()) + { + request_time_critical_pieces(); + } + + // ---- WEB SEEDS ---- + + maybe_connect_web_seeds(); + + m_swarm_last_seen_complete = m_last_seen_complete; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = *i; + ++i; + + // look for the peer that saw a seed most recently + m_swarm_last_seen_complete = (std::max)(p->last_seen_complete(), m_swarm_last_seen_complete); + + if (!p->ignore_stats()) + m_stat += p->statistics(); + + // updates the peer connection's ul/dl bandwidth + // resource requests + TORRENT_TRY { + p->second_tick(tick_interval_ms); + } + TORRENT_CATCH (std::exception& e) + { + TORRENT_DECLARE_DUMMY(std::exception, e); + (void)e; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + p->peer_log("*** ERROR %s", e.what()); +#endif + p->disconnect(errors::no_error, 1); + } + } + if (m_ses.m_alerts.should_post()) + m_ses.m_alerts.post_alert(stats_alert(get_handle(), tick_interval_ms, m_stat)); + + accumulator += m_stat; + m_total_uploaded += m_stat.last_payload_uploaded(); + m_total_downloaded += m_stat.last_payload_downloaded(); + m_stat.second_tick(tick_interval_ms); + + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); + + // this section determines whether the torrent is active or not. When it + // changes state, it may also trigger the auto-manage logic to reconsider + // which torrents should be queued and started. There is a low pass + // filter in order to avoid flapping (auto_manage_startup). + bool is_inactive = false; + if (is_finished()) + is_inactive = m_stat.upload_payload_rate() < m_ses.m_settings.inactive_up_rate; + else + is_inactive = m_stat.download_payload_rate() < m_ses.m_settings.inactive_down_rate; + + if (is_inactive) + { + if (m_inactive_counter < 0) m_inactive_counter = 0; + if (m_inactive_counter < (std::numeric_limits::max)()) + { + ++m_inactive_counter; + + // if this torrent was just considered inactive, we may want + // to dequeue some other torrent + if (m_inactive == false + && m_inactive_counter >= m_ses.m_settings.auto_manage_startup) + { + m_inactive = true; + if (m_ses.m_settings.dont_count_slow_torrents) + m_ses.trigger_auto_manage(); + } + } + } + else + { + if (m_inactive_counter > 0) m_inactive_counter = 0; + if (m_inactive_counter > (std::numeric_limits::min)()) + { + --m_inactive_counter; + + // if this torrent was just considered active, we may want + // to queue some other torrent + if (m_inactive == true + && m_inactive_counter <= -m_ses.m_settings.auto_manage_startup) + { + m_inactive = false; + if (m_ses.m_settings.dont_count_slow_torrents) + m_ses.trigger_auto_manage(); + } + } + } + } + + void torrent::maybe_connect_web_seeds() + { + if (m_abort) return; + + // if we have everything we want we don't need to connect to any web-seed + if (!is_finished() && !m_web_seeds.empty() && m_files_checked + && int(m_connections.size()) < m_max_connections + && m_ses.num_connections() < m_ses.settings().connections_limit) + { + // keep trying web-seeds if there are any + // first find out which web seeds we are connected to + for (std::list::iterator i = m_web_seeds.begin(); + i != m_web_seeds.end();) + { + std::list::iterator w = i++; + if (w->peer_info.connection) continue; + if (w->retry > time_now()) continue; + if (w->resolving) continue; + + connect_to_url_seed(w); + } + } + } + + void torrent::recalc_share_mode() + { + TORRENT_ASSERT(share_mode()); + if (is_seed()) return; + + int pieces_in_torrent = m_torrent_file->num_pieces(); + int num_seeds = 0; + int num_peers = 0; + int num_downloaders = 0; + int missing_pieces = 0; + int num_interested = 0; + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = *i; + if (p->is_connecting()) continue; + ++num_peers; + if (p->is_seed()) + { + ++num_seeds; + continue; + } + + if (p->share_mode()) continue; + + if ((*i)->is_peer_interested()) ++num_interested; + ++num_downloaders; + missing_pieces += pieces_in_torrent - p->num_have_pieces(); + } + + if (num_peers == 0) return; + + if (num_seeds * 100 / num_peers > 50 + && (num_peers * 100 / m_max_connections > 90 + || num_peers > 20)) + { + // we are connected to more than 90% seeds (and we're beyond + // 90% of the max number of connections). That will + // limit our ability to upload. We need more downloaders. + // disconnect some seeds so that we don't have more than 50% + int to_disconnect = num_seeds - num_peers / 2; + std::vector seeds; + seeds.reserve(num_seeds); + for (std::set::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = *i; + if (p->is_seed()) seeds.push_back(p); + } + + std::random_shuffle(seeds.begin(), seeds.end()); + TORRENT_ASSERT(to_disconnect <= int(seeds.size())); + for (int i = 0; i < to_disconnect; ++i) + seeds[i]->disconnect(errors::upload_upload_connection); + } + + if (num_downloaders == 0) return; + + // assume that the seeds are about as fast as us. During the time + // we can download one piece, and upload one piece, each seed + // can upload two pieces. + missing_pieces -= 2 * num_seeds; + + if (missing_pieces <= 0) return; + + // missing_pieces represents our opportunity to download pieces + // and share them more than once each + + // now, download at least one piece, otherwise download one more + // piece if our downloaded (and downloading) pieces is less than 50% + // of the uploaded bytes + int num_downloaded_pieces = (std::max)(m_picker->num_have() + , pieces_in_torrent - m_picker->num_filtered()); + + if (boost::int64_t(num_downloaded_pieces) * m_torrent_file->piece_length() + * settings().share_mode_target > m_total_uploaded + && num_downloaded_pieces > 0) + return; + + // don't have more pieces downloading in parallel than 5% of the total + // number of pieces we have downloaded + if (int(m_picker->get_download_queue().size()) > num_downloaded_pieces / 20) + return; + + // one more important property is that there are enough pieces + // that more than one peer wants to download + // make sure that there are enough downloaders for the rarest + // piece. Go through all pieces, figure out which one is the rarest + // and how many peers that has that piece + + std::vector rarest_pieces; + + int num_pieces = m_torrent_file->num_pieces(); + int rarest_rarity = INT_MAX; + bool prio_updated = false; + for (int i = 0; i < num_pieces; ++i) + { + piece_picker::piece_pos const& pp = m_picker->piece_stats(i); + if (pp.peer_count == 0) continue; + if (pp.filtered() && (pp.have() || pp.downloading)) + { + m_picker->set_piece_priority(i, 1); + prio_updated = true; + continue; + } + // don't count pieces we already have or are downloading + if (!pp.filtered() || pp.have()) continue; + if (int(pp.peer_count) > rarest_rarity) continue; + if (int(pp.peer_count) == rarest_rarity) + { + rarest_pieces.push_back(i); + continue; + } + + rarest_pieces.clear(); + rarest_rarity = pp.peer_count; + rarest_pieces.push_back(i); + } + + if (prio_updated) + m_policy.recalculate_connect_candidates(); + + // now, rarest_pieces is a list of all pieces that are the rarest ones. + // and rarest_rarity is the number of peers that have the rarest pieces + + // if there's only a single peer that doesn't have the rarest piece + // it's impossible for us to download one piece and upload it + // twice. i.e. we cannot get a positive share ratio + if (num_peers - rarest_rarity < settings().share_mode_target) return; + + // we might be able to do better than a share ratio of 2 if there are + // enough downloaders of the pieces we already have. + // TODO: go through the pieces we have and count the total number + // of downloaders we have. Only count peers that are interested in us + // since some peers might not send have messages for pieces we have + // it num_interested == 0, we need to pick a new piece + + // now, pick one of the rarest pieces to download + int pick = random() % rarest_pieces.size(); + bool was_finished = is_finished(); + m_picker->set_piece_priority(rarest_pieces[pick], 1); + update_peer_interest(was_finished); + + m_policy.recalculate_connect_candidates(); + } + + void torrent::refresh_explicit_cache(int cache_size) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (!ready_for_connections()) return; + + if (m_abort) return; + TORRENT_ASSERT(m_storage); + + // rotate the cached pieces + + // add blocks_per_piece / 2 in order to round to closest whole piece + int blocks_per_piece = m_torrent_file->piece_length() / block_size(); + int num_cache_pieces = (cache_size + blocks_per_piece / 2) / blocks_per_piece; + if (num_cache_pieces > m_torrent_file->num_pieces()) + num_cache_pieces = m_torrent_file->num_pieces(); + + std::vector avail_vec; + if (has_picker()) + { + m_picker->get_availability(avail_vec); + } + else + { + // we don't keep track of availability, do it the expensive way + // do a linear search from the first piece + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + int availability = 0; + if (!have_piece(i)) + { + avail_vec.push_back(INT_MAX); + continue; + } + + for (const_peer_iterator j = this->begin(); j != this->end(); ++j) + if ((*j)->has_piece(i)) ++availability; + avail_vec.push_back(availability); + } + } + + // now pick the num_cache_pieces rarest pieces from avail_vec + std::vector > pieces(m_torrent_file->num_pieces()); + for (int i = 0; i < m_torrent_file->num_pieces(); ++i) + { + pieces[i].second = i; + if (!have_piece(i)) pieces[i].first = INT_MAX; + else pieces[i].first = avail_vec[i]; + } + + // decrease the availability of the pieces that are + // already in the read cache, to move them closer to + // the beginning of the pieces list, and more likely + // to be included in this round of cache pieces + std::vector ret; + m_ses.m_disk_thread.get_cache_info(info_hash(), ret); + // remove write cache entries + ret.erase(std::remove_if(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) + , ret.end()); + for (std::vector::iterator i = ret.begin() + , end(ret.end()); i != end; ++i) + { + --pieces[i->piece].first; + } + + std::random_shuffle(pieces.begin(), pieces.end()); + std::stable_sort(pieces.begin(), pieces.end() + , boost::bind(&std::pair::first, _1) < + boost::bind(&std::pair::first, _2)); + avail_vec.clear(); + for (int i = 0; i < num_cache_pieces; ++i) + { + if (pieces[i].first == INT_MAX) break; + avail_vec.push_back(pieces[i].second); + } + + if (!avail_vec.empty()) + { + // the number of pieces to cache for this torrent is proportional + // the number of peers it has, divided by the total number of peers. + // Each peer gets an equal share of the cache + + avail_vec.resize((std::min)(num_cache_pieces, int(avail_vec.size()))); + + for (std::vector::iterator i = avail_vec.begin() + , end(avail_vec.end()); i != end; ++i) + filesystem().async_cache(*i, boost::bind(&torrent::on_disk_cache_complete + , shared_from_this(), _1, _2)); + } + } + + void torrent::get_suggested_pieces(std::vector& s) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + if (settings().suggest_mode == session_settings::no_piece_suggestions) + { + s.clear(); + return; + } + + std::vector ret; + m_ses.m_disk_thread.get_cache_info(info_hash(), ret); + + // remove write cache entries + ret.erase(std::remove_if(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::kind, _1) == cached_piece_info::write_cache) + , ret.end()); + + // sort by how new the cached entry is, new pieces first + std::sort(ret.begin(), ret.end() + , boost::bind(&cached_piece_info::last_use, _1) + < boost::bind(&cached_piece_info::last_use, _2)); + + // cut off the oldest pieces that we don't want to suggest + // if we have an explicit cache, it's much more likely to + // stick around, so we should suggest all pieces + int num_pieces_to_suggest = int(ret.size()); + if (num_pieces_to_suggest == 0) return; + + if (!settings().explicit_read_cache) + num_pieces_to_suggest = (std::max)(1, int(ret.size() / 2)); + ret.resize(num_pieces_to_suggest); + + std::transform(ret.begin(), ret.end(), std::back_inserter(s) + , boost::bind(&cached_piece_info::piece, _1)); + } + + void torrent::add_stats(stat const& s) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + // these stats are propagated to the session + // stats the next time second_tick is called + m_stat += s; + } + +#if TORRENT_DEBUG_STREAMING > 0 + char const* esc(char const* code) + { + // this is a silly optimization + // to avoid copying of strings + enum { num_strings = 200 }; + static char buf[num_strings][20]; + static int round_robin = 0; + char* ret = buf[round_robin]; + ++round_robin; + if (round_robin >= num_strings) round_robin = 0; + ret[0] = '\033'; + ret[1] = '['; + int i = 2; + int j = 0; + while (code[j]) ret[i++] = code[j++]; + ret[i++] = 'm'; + ret[i++] = 0; + return ret; + } + + int peer_index(libtorrent::tcp::endpoint addr + , std::vector const& peers) + { + using namespace libtorrent; + std::vector::const_iterator i = std::find_if(peers.begin() + , peers.end(), boost::bind(&peer_info::ip, _1) == addr); + if (i == peers.end()) return -1; + + return i - peers.begin(); + } + + void print_piece(libtorrent::partial_piece_info* pp + , std::vector const& peers + , std::vector const& time_critical) + { + using namespace libtorrent; + + ptime now = time_now_hires(); + + float deadline = 0.f; + float last_request = 0.f; + int timed_out = -1; + + int piece = pp->piece_index; + std::vector::const_iterator i + = std::find_if(time_critical.begin(), time_critical.end() + , boost::bind(&time_critical_piece::piece, _1) == piece); + if (i != time_critical.end()) + { + deadline = total_milliseconds(i->deadline - now) / 1000.f; + last_request = total_milliseconds(now - i->last_requested) / 1000.f; + timed_out = i->timed_out; + } + + int num_blocks = pp->blocks_in_piece; + + printf("%5d: [", piece); + for (int j = 0; j < num_blocks; ++j) + { + int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1; + char chr = '+'; + if (index >= 0) + chr = (index < 10)?'0' + index:'A' + index - 10; + + char const* color = ""; + char const* multi_req = ""; + + if (pp->blocks[j].num_peers > 1) + multi_req = esc("1"); + + if (pp->blocks[j].bytes_progress > 0 + && pp->blocks[j].state == block_info::requested) + { + color = esc("33;7"); + chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size); + } + else if (pp->blocks[j].state == block_info::finished) color = esc("32;7"); + else if (pp->blocks[j].state == block_info::writing) color = esc("36;7"); + else if (pp->blocks[j].state == block_info::requested) color = esc("0"); + else { color = esc("0"); chr = ' '; } + + printf("%s%s%c%s", color, multi_req, chr, esc("0")); + } + printf("%s]", esc("0")); + if (deadline != 0.f) + printf(" deadline: %f last-req: %f timed_out: %d\n" + , deadline, last_request, timed_out); + else + printf("\n"); + } +#endif // TORRENT_DEBUG_STREAMING + + struct busy_block_t + { + int peers; + int index; + bool operator<(busy_block_t rhs) const { return peers < rhs.peers; } + }; + + void pick_busy_blocks(int piece, int blocks_in_piece + , int timed_out + , std::vector& interesting_blocks + , piece_picker::downloading_piece const& pi) + { + // if there aren't any free blocks in the piece, and the piece is + // old enough, we may switch into busy mode for this piece. In this + // case busy_blocks and busy_count are set to contain the eligible + // busy blocks we may pick + // first, figure out which blocks are eligible for picking + // in "busy-mode" + busy_block_t* busy_blocks + = TORRENT_ALLOCA(busy_block_t, blocks_in_piece); + int busy_count = 0; + + // pick busy blocks from the piece + for (int k = 0; k < blocks_in_piece; ++k) + { + // only consider blocks that have been requested + // and we're still waiting for them + if (pi.info[k].state != piece_picker::block_info::state_requested) + continue; + + piece_block b(piece, k); + + // only allow a single additional request per block, in order + // to spread it out evenly across all stalled blocks + if (pi.info[k].num_peers > timed_out) + continue; + + busy_blocks[busy_count].peers = pi.info[k].num_peers; + busy_blocks[busy_count].index = k; + ++busy_count; + +#if TORRENT_DEBUG_STREAMING > 1 + printf(" [%d (%d)]", b.block_index, pi.info[k].num_peers); +#endif + } +#if TORRENT_DEBUG_STREAMING > 1 + printf("\n"); +#endif + + // then sort blocks by the number of peers with requests + // to the blocks (request the blocks with the fewest peers + // first) + std::sort(busy_blocks, busy_blocks + busy_count); + + // then insert them into the interesting_blocks vector + for (int k = 0; k < busy_count; ++k) + { + interesting_blocks.push_back( + piece_block(piece, busy_blocks[k].index)); + } + } + + void pick_time_critical_block(std::vector& peers + , std::vector& ignore_peers + , std::set& peers_with_requests + , piece_picker::downloading_piece const& pi + , time_critical_piece* i + , piece_picker* picker + , int blocks_in_piece + , int timed_out) + { + std::vector interesting_blocks; + std::vector backup1; + std::vector backup2; + std::vector ignore; + + ptime now = time_now(); + + // loop until every block has been requested from this piece (i->piece) + do + { + // if this peer's download time exceeds 2 seconds, we're done. + // We don't want to build unreasonably long request queues + if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("queue time: %d ms, done\n" + , int(total_milliseconds(peers[0]->download_queue_time()))); +#endif + break; + } + + // pick the peer with the lowest download_queue_time that has i->piece + std::vector::iterator p = std::find_if(peers.begin(), peers.end() + , boost::bind(&peer_connection::has_piece, _1, i->piece)); + + // obviously we'll have to skip it if we don't have a peer that has + // this piece + if (p == peers.end()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("out of peers, done\n"); +#endif + break; + } + peer_connection& c = **p; + + interesting_blocks.clear(); + backup1.clear(); + backup2.clear(); + + // specifically request blocks with no affinity towards fast or slow + // pieces. If we would, the picked block might end up in one of + // the backup lists + picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks + , backup1, backup2, blocks_in_piece, 0, c.peer_info_struct() + , ignore, piece_picker::none, 0); + + interesting_blocks.insert(interesting_blocks.end() + , backup1.begin(), backup1.end()); + interesting_blocks.insert(interesting_blocks.end() + , backup2.begin(), backup2.end()); + + bool busy_mode = false; + + if (interesting_blocks.empty()) + { + busy_mode = true; + +#if TORRENT_DEBUG_STREAMING > 1 + printf("interesting_blocks.empty()\n"); +#endif + + // there aren't any free blocks to pick, and the piece isn't + // old enough to pick busy blocks yet. break to continue to + // the next piece. + if (timed_out == 0) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("not timed out, moving on to next piece\n"); +#endif + break; + } + +#if TORRENT_DEBUG_STREAMING > 1 + printf("pick busy blocks\n"); +#endif + + pick_busy_blocks(i->piece, blocks_in_piece, timed_out + , interesting_blocks, pi); + } + + // we can't pick anything from this piece, we're done with it. + // move on to the next one + if (interesting_blocks.empty()) break; + + piece_block b = interesting_blocks.front(); + + // in busy mode we need to make sure we don't do silly + // things like requesting the same block twice from the + // same peer + std::vector const& dq = c.download_queue(); + + bool already_requested = std::find_if(dq.begin(), dq.end() + , has_block(b)) != dq.end(); + + if (already_requested) + { + // if the piece is stalled, we may end up picking a block + // that we've already requested from this peer. If so, we should + // simply disregard this peer from this piece, since this peer + // is likely to be causing the stall. We should request it + // from the next peer in the list + // the peer will be put back in the set for the next piece + ignore_peers.push_back(*p); + peers.erase(p); +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already requested by peer, try next peer\n"); +#endif + // try next peer + continue; + } + + std::vector const& rq = c.request_queue(); + + bool already_in_queue = std::find_if(rq.begin(), rq.end() + , has_block(b)) != rq.end(); + + if (already_in_queue) + { + if (!c.make_time_critical(b)) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already time-critical and in queue for peer, trying next peer\n"); +#endif + ignore_peers.push_back(*p); + peers.erase(p); + continue; + } + i->last_requested = now; + +#if TORRENT_DEBUG_STREAMING > 1 + printf("piece already in queue for peer, making time-critical\n"); +#endif + + // we inserted a new block in the request queue, this + // makes us actually send it later + peers_with_requests.insert(peers_with_requests.begin(), &c); + } + else + { + if (!c.add_request(b, peer_connection::req_time_critical + | (busy_mode ? peer_connection::req_busy : 0))) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("failed to request block [%d, %d]\n" + , b.piece_index, b.block_index); +#endif + ignore_peers.push_back(*p); + peers.erase(p); + continue; + } + +#if TORRENT_DEBUG_STREAMING > 1 + printf("requested block [%d, %d]\n" + , b.piece_index, b.block_index); +#endif + peers_with_requests.insert(peers_with_requests.begin(), &c); + } + + if (!busy_mode) i->last_requested = now; + + if (i->first_requested == min_time()) i->first_requested = now; + + if (!c.can_request_time_critical()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("peer cannot pick time critical pieces\n"); +#endif + peers.erase(p); + // try next peer + continue; + } + + // resort p, since it will have a higher download_queue_time now + while (p != peers.end()-1 && (*p)->download_queue_time() + > (*(p+1))->download_queue_time()) + { + std::iter_swap(p, p+1); + ++p; + } + } while (!interesting_blocks.empty()); + } + + void torrent::request_time_critical_pieces() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(!upload_mode()); + + // build a list of peers and sort it by download_queue_time + // we use this sorted list to determine which peer we should + // request a block from. The earlier a peer is in the list, + // the sooner we will fully download the block we request. + std::vector peers; + peers.reserve(m_connections.size()); + + // some peers are marked as not being able to request time critical + // blocks from. For instance, peers that have choked us, peers that are + // on parole (i.e. they are believed to have sent us bad data), peers + // that are being disconnected, in upload mode etc. + std::remove_copy_if(m_connections.begin(), m_connections.end() + , std::back_inserter(peers), !boost::bind(&peer_connection::can_request_time_critical, _1)); + + // sort by the time we believe it will take this peer to send us all + // blocks we've requested from it. The shorter time, the better candidate + // it is to request a time critical block from. + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) + < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); + + // remove the bottom 10% of peers from the candidate set. + // this is just to remove outliers that might stall downloads + int new_size = (peers.size() * 9 + 9) / 10; + TORRENT_ASSERT(new_size <= int(peers.size())); + peers.resize(new_size); + + // remember all the peers we issued requests to, so we can commit them + // at the end of this function. Instead of sending the requests right + // away, we batch them up and send them in a single write to the TCP + // socket, increasing the chance that they will all be sent in the same + // packet. + std::set peers_with_requests; + + // peers that should be temporarily ignored for a specific piece + // in order to give priority to other peers. They should be used for + // subsequent pieces, so they are stored in this vector until the + // piece is done + std::vector ignore_peers; + + ptime now = time_now_hires(); + + // now, iterate over all time critical pieces, in order of importance, and + // request them from the peers, in order of responsiveness. i.e. request + // the most time critical pieces from the fastest peers. + for (std::deque::iterator i = m_time_critical_pieces.begin() + , end(m_time_critical_pieces.end()); i != end; ++i) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("considering %d\n", i->piece); +#endif + + if (peers.empty()) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("out of peers, done\n"); +#endif + break; + } + + // the +1000 is to compensate for the fact that we only call this + // function once per second, so if we need to request it 500 ms from + // now, we should request it right away + if (i != m_time_critical_pieces.begin() && i->deadline > now + + milliseconds(m_average_piece_time + m_piece_time_deviation * 4 + 1000)) + { + // don't request pieces whose deadline is too far in the future + // this is one of the termination conditions. We don't want to + // send requests for all pieces in the torrent right away +#if TORRENT_DEBUG_STREAMING > 0 + printf("reached deadline horizon [%f + %f * 4 + 1]\n" + , m_average_piece_time / 1000.f + , m_piece_time_deviation / 1000.f); +#endif + break; + } + + piece_picker::downloading_piece pi; + m_picker->piece_info(i->piece, pi); + + // the number of "times" this piece has timed out. + int timed_out = 0; + + int blocks_in_piece = m_picker->blocks_in_piece(i->piece); + +#if TORRENT_DEBUG_STREAMING > 0 + i->timed_out = timed_out; +#endif + int free_to_request = blocks_in_piece + - pi.finished - pi.writing - pi.requested; + + if (free_to_request == 0) + { + if (i->last_requested == min_time()) + i->last_requested = now; + + // if it's been more than half of the typical download time + // of a piece since we requested the last block, allow + // one more request per block + if (m_average_piece_time > 0) + timed_out = total_milliseconds(now - i->last_requested) + / (std::max)(int(m_average_piece_time + m_piece_time_deviation / 2), 1); + +#if TORRENT_DEBUG_STREAMING > 0 + i->timed_out = timed_out; +#endif + // every block in this piece is already requested + // there's no need to consider this piece, unless it + // appears to be stalled. + if (pi.requested == 0 || timed_out == 0) + { +#if TORRENT_DEBUG_STREAMING > 1 + printf("skipping %d (full) [req: %d timed_out: %d ]\n" + , i->piece, pi.requested + , timed_out); +#endif + + // if requested is 0, it meants all blocks have been received, and + // we're just waiting for it to flush them to disk. + // if last_requested is recent enough, we should give it some + // more time + // skip to the next piece + continue; + } + + // it's been too long since we requested the last block from + // this piece. Allow re-requesting blocks from this piece +#if TORRENT_DEBUG_STREAMING > 1 + printf("timed out [average-piece-time: %d ms ]\n" + , m_average_piece_time); +#endif + } + + // pick all blocks for this piece. the peers list is kept up to date + // and sorted. when we issue a request to a peer, its download queue + // time will increase and it may need to be bumped in the peers list, + // since it's ordered by download queue time + pick_time_critical_block(peers, ignore_peers + , peers_with_requests + , pi, &*i, m_picker.get() + , blocks_in_piece, timed_out); + + // put back the peers we ignored into the peer list for the next piece + if (!ignore_peers.empty()) + { + peers.insert(peers.begin(), ignore_peers.begin(), ignore_peers.end()); + ignore_peers.clear(); + + // TODO: instead of resorting the whole list, insert the peers + // directly into the right place + std::sort(peers.begin(), peers.end() + , boost::bind(&peer_connection::download_queue_time, _1, 16*1024) + < boost::bind(&peer_connection::download_queue_time, _2, 16*1024)); + } + + // if this peer's download time exceeds 2 seconds, we're done. + // We don't want to build unreasonably long request queues + if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) + break; + } + + // commit all the time critical requests + for (std::set::iterator i = peers_with_requests.begin() + , end(peers_with_requests.end()); i != end; ++i) + { + (*i)->send_block_requests(); + } + } + + std::set torrent::web_seeds(web_seed_entry::type_t type) const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + std::set ret; + for (std::list::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->peer_info.banned) continue; + if (i->type != type) continue; + ret.insert(i->url); + } + return ret; + } + + void torrent::remove_web_seed(std::string const& url, web_seed_entry::type_t type) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&web_seed_entry::url, _1) + == url && boost::bind(&web_seed_entry::type, _1) == type)); + if (i != m_web_seeds.end()) remove_web_seed(i); + } + + void torrent::disconnect_web_seed(peer_connection* p) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + // this happens if the web server responded with a redirect + // or with something incorrect, so that we removed the web seed + // immediately, before we disconnected + if (i == m_web_seeds.end()) return; + + TORRENT_ASSERT(i->resolving == false); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("disconnect web seed: \"%s\"", i->url.c_str()); +#endif + TORRENT_ASSERT(i->peer_info.connection); + i->peer_info.connection = 0; + } + + void torrent::remove_web_seed(peer_connection* p) + { + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + TORRENT_ASSERT(i != m_web_seeds.end()); + if (i == m_web_seeds.end()) return; + p->set_peer_info(0); + if (has_picker()) picker().clear_peer(&i->peer_info); + m_web_seeds.erase(i); + } + + void torrent::retry_web_seed(peer_connection* p, int retry) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() + , (boost::bind(&policy::peer::connection, boost::bind(&web_seed_entry::peer_info, _1)) == p)); + + TORRENT_ASSERT(i != m_web_seeds.end()); + if (i == m_web_seeds.end()) return; + if (retry == 0) retry = m_ses.settings().urlseed_wait_retry; + i->retry = time_now() + seconds(retry); + } + + bool torrent::try_connect_peer() + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(want_more_peers()); + bool ret = m_policy.connect_one_peer(m_ses.session_time()); + return ret; + } + + void torrent::add_peer(tcp::endpoint const& adr, int source) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + peer_id id(0); + m_policy.add_peer(adr, id, source, 0); + + state_updated(); + } + + void torrent::async_verify_piece(int piece_index, boost::function const& f) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +// INVARIANT_CHECK; + + TORRENT_ASSERT(m_storage); + TORRENT_ASSERT(m_storage->refcount() > 0); + TORRENT_ASSERT(piece_index >= 0); + TORRENT_ASSERT(piece_index < m_torrent_file->num_pieces()); + TORRENT_ASSERT(piece_index < (int)m_picker->num_pieces()); + TORRENT_ASSERT(!m_picker || !m_picker->have_piece(piece_index)); +#ifdef TORRENT_DEBUG + if (m_picker) + { + int blocks_in_piece = m_picker->blocks_in_piece(piece_index); + for (int i = 0; i < blocks_in_piece; ++i) + { + TORRENT_ASSERT(m_picker->num_peers(piece_block(piece_index, i)) == 0); + } + } +#endif + + m_storage->async_hash(piece_index, boost::bind(&torrent::on_piece_verified + , shared_from_this(), _1, _2, f)); +#if TORRENT_USE_INVARIANT_CHECKS + check_invariant(); +#endif + } + + void torrent::on_piece_verified(int ret, disk_io_job const& j + , boost::function f) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + // return value: + // 0: success, piece passed hash check + // -1: disk failure + // -2: hash check failed + + state_updated(); + + if (ret == -1) handle_disk_error(j); + f(ret); + } + + tcp::endpoint torrent::current_tracker() const + { + return m_tracker_address; + } + + announce_entry* torrent::find_tracker(tracker_request const& r) + { + std::vector::iterator i = std::find_if( + m_trackers.begin(), m_trackers.end() + , boost::bind(&announce_entry::url, _1) == r.url); + if (i == m_trackers.end()) return 0; + return &*i; + } + +#if !TORRENT_NO_FPU + void torrent::file_progress(std::vector& fp) const + { + fp.clear(); + if (!valid_metadata()) return; + + fp.resize(m_torrent_file->num_files(), 1.f); + if (is_seed()) return; + + std::vector progress; + file_progress(progress); + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + file_entry const& f = m_torrent_file->file_at(i); + if (f.size == 0) fp[i] = 1.f; + else fp[i] = float(progress[i]) / f.size; + } + } +#endif + + void torrent::file_progress(std::vector& fp, int flags) const + { + if (!valid_metadata()) + { + fp.clear(); + return; + } + + fp.resize(m_torrent_file->num_files(), 0); + + // if we're a seed, just fill in the full file sizes as a shortcut + if (is_seed()) + { + for (int i = 0; i < m_torrent_file->num_files(); ++i) + fp[i] = m_torrent_file->files().file_size(i); + return; + } + + // we're not a seed and we don't have a picker, that means we donn't + // have any piece yet. + if (!has_picker()) + return; + + if (flags & torrent_handle::piece_granularity) + { + std::copy(m_file_progress.begin(), m_file_progress.end(), fp.begin()); + return; + } + + TORRENT_ASSERT(has_picker()); + + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + peer_request ret = m_torrent_file->files().map_file(i, 0, 0); + size_type size = m_torrent_file->files().file_size(i); + TORRENT_ASSERT(ret.piece >= 0); + TORRENT_ASSERT(ret.piece < m_picker->num_pieces()); + if (ret.piece < 0 || ret.piece >= m_picker->num_pieces()) + { + // this is not supposed to happen. + fp[i] = 0; + continue; + } + + size_type done = 0; + while (size > 0) + { + TORRENT_ASSERT(ret.piece < m_picker->num_pieces()); + TORRENT_ASSERT(ret.piece >= 0); + + size_type bytes_step = (std::min)(size_type(m_torrent_file->piece_size(ret.piece) + - ret.start), size); + + if (m_picker->have_piece(ret.piece)) done += bytes_step; + ++ret.piece; + ret.start = 0; + size -= bytes_step; + } + TORRENT_ASSERT(size == 0); + + fp[i] = done; + } + + const std::vector& q + = m_picker->get_download_queue(); + + file_storage const& fs = m_torrent_file->files(); + for (std::vector::const_iterator + i = q.begin(), end(q.end()); i != end; ++i) + { + size_type offset = size_type(i->index) * m_torrent_file->piece_length(); + int file = fs.file_index_at_offset(offset); + int num_blocks = m_picker->blocks_in_piece(i->index); + piece_picker::block_info const* info = i->info; + for (int k = 0; k < num_blocks; ++k) + { + TORRENT_ASSERT(file < fs.num_files()); + TORRENT_ASSERT(offset == size_type(i->index) * m_torrent_file->piece_length() + + k * block_size()); + TORRENT_ASSERT(offset < m_torrent_file->total_size()); + while (offset >= fs.file_offset(file) + fs.file_size(file)) + { + ++file; + } + TORRENT_ASSERT(file < fs.num_files()); + + size_type block = block_size(); + + if (info[k].state == piece_picker::block_info::state_none) + { + offset += block; + continue; + } + + if (info[k].state == piece_picker::block_info::state_requested) + { + block = 0; + policy::peer* p = static_cast(info[k].peer); + if (p && p->connection) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == k) + block = pbp->bytes_downloaded; + TORRENT_ASSERT(block <= block_size()); + } + + if (block == 0) + { + offset += block_size(); + continue; + } + } + + if (offset + block > fs.file_offset(file) + fs.file_size(file)) + { + int left_over = int(block_size() - block); + // split the block on multiple files + while (block > 0) + { + TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); + size_type slice = (std::min)(fs.file_offset(file) + fs.file_size(file) - offset + , block); + fp[file] += slice; + offset += slice; + block -= slice; + TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); + if (offset == fs.file_offset(file) + fs.file_size(file)) + { + ++file; + if (file == fs.num_files()) + { + offset += block; + break; + } + } + } + offset += left_over; + TORRENT_ASSERT(offset == size_type(i->index) * m_torrent_file->piece_length() + + (k+1) * block_size()); + } + else + { + fp[file] += block; + offset += block_size(); + } + TORRENT_ASSERT(file <= m_torrent_file->num_files()); + } + } + } + + void torrent::new_external_ip() + { + m_policy.clear_peer_prio(); + } + + void torrent::set_state(torrent_status::state_t s) + { + TORRENT_ASSERT(m_ses.is_network_thread()); +#if TORRENT_USE_ASSERTS + if (s != torrent_status::checking_files + && s != torrent_status::queued_for_checking) + { + // the only valid transition away from queued_for_checking + // is to checking_files. One exception is to finished + // in case all the files are marked with priority 0 + if (m_queued_for_checking) + { + std::vector pieces; + m_picker->piece_priorities(pieces); + // make sure all pieces have priority 0 + TORRENT_ASSERT(std::accumulate(pieces.begin(), pieces.end(), 0) == 0); + } + } + if (s == torrent_status::seeding) + TORRENT_ASSERT(is_seed()); + + if (s == torrent_status::seeding) + TORRENT_ASSERT(is_seed()); + if (s == torrent_status::finished) + TORRENT_ASSERT(is_finished()); + if (s == torrent_status::downloading && m_state == torrent_status::finished) + TORRENT_ASSERT(!is_finished()); +#endif + + if (int(m_state) == s) return; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(state_changed_alert(get_handle() + , s, (torrent_status::state_t)m_state)); + } + + if (s == torrent_status::finished + && m_ses.m_alerts.should_post()) + { + alerts().post_alert(torrent_finished_alert( + get_handle())); + } + + + m_state = s; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + debug_log("set_state() %d", m_state); +#endif + + update_guage(); + + state_updated(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_state(m_state); + } TORRENT_CATCH (std::exception&) {} + } +#endif + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void torrent::notify_extension_add_peer(tcp::endpoint const& ip + , int src, int flags) + { + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + TORRENT_TRY { + (*i)->on_add_peer(ip, src, flags); + } TORRENT_CATCH (std::exception&) {} + } + } +#endif + + void torrent::state_updated() + { + // if this fails, this function is probably called + // from within the torrent constructor, which it + // shouldn't be. Whichever function ends up calling + // this should probably be moved to torrent::start() + TORRENT_ASSERT(shared_from_this()); + + // we can't call state_updated() while the session + // is building the status update alert + TORRENT_ASSERT(!m_ses.m_posting_torrent_updates); + + // we're either not subscribing to this torrent, or + // it has already been updated this round, no need to + // add it to the list twice + if (!m_state_subscription) return; + if (m_in_state_updates) + { +#ifndef TORRENT_DISABLE_INVARIANT_CHECKS + TORRENT_ASSERT(m_ses.in_state_updates(shared_from_this())); +#endif + return; + } + + m_ses.add_to_update_queue(shared_from_this()); + m_in_state_updates = true; + } + + void torrent::status(torrent_status* st, boost::uint32_t flags) + { + INVARIANT_CHECK; + + ptime now = time_now(); + + st->handle = get_handle(); + st->info_hash = info_hash(); + + if (flags & torrent_handle::query_name) + st->name = name(); + + if (flags & torrent_handle::query_save_path) + st->save_path = save_path(); + + if (flags & torrent_handle::query_torrent_file) + st->torrent_file = m_torrent_file; + + st->has_incoming = m_has_incoming; + if (m_error) st->error = convert_from_native(m_error.message()) + ": " + m_error_file; + st->seed_mode = m_seed_mode; + st->moving_storage = m_moving_storage; + + st->added_time = m_added_time; + st->completed_time = m_completed_time; + + st->last_scrape = m_last_scrape; + st->share_mode = m_share_mode; + st->upload_mode = m_upload_mode; + st->up_bandwidth_queue = 0; + st->down_bandwidth_queue = 0; + st->priority = m_priority; + + st->num_peers = int(m_connections.size()) - m_num_connecting; + + st->list_peers = m_policy.num_peers(); + st->list_seeds = m_policy.num_seeds(); + st->connect_candidates = m_policy.num_connect_candidates(); + st->seed_rank = seed_rank(settings()); + + st->all_time_upload = m_total_uploaded; + st->all_time_download = m_total_downloaded; + + // activity time + st->finished_time = m_finished_time; + st->active_time = m_active_time; + st->seeding_time = m_seeding_time; + st->time_since_upload = m_last_upload; + st->time_since_download = m_last_download; + + st->storage_mode = (storage_mode_t)m_storage_mode; + + st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete; + st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; + st->paused = is_torrent_paused(); + st->auto_managed = m_auto_managed; + st->sequential_download = m_sequential_download; + st->is_seeding = is_seed(); + st->is_finished = is_finished(); + st->super_seeding = m_super_seeding; + st->has_metadata = valid_metadata(); + bytes_done(*st, flags & torrent_handle::query_accurate_download_counters); + TORRENT_ASSERT(st->total_wanted_done >= 0); + TORRENT_ASSERT(st->total_done >= st->total_wanted_done); + + // payload transfer + st->total_payload_download = m_stat.total_payload_download(); + st->total_payload_upload = m_stat.total_payload_upload(); + + // total transfer + st->total_download = m_stat.total_payload_download() + + m_stat.total_protocol_download(); + st->total_upload = m_stat.total_payload_upload() + + m_stat.total_protocol_upload(); + + // failed bytes + st->total_failed_bytes = m_total_failed_bytes; + st->total_redundant_bytes = m_total_redundant_bytes; + + // transfer rate + st->download_rate = m_stat.download_rate(); + st->upload_rate = m_stat.upload_rate(); + st->download_payload_rate = m_stat.download_payload_rate(); + st->upload_payload_rate = m_stat.upload_payload_rate(); + + if (m_waiting_tracker && !is_paused()) + st->next_announce = boost::posix_time::seconds( + total_seconds(next_announce() - now)); + else + st->next_announce = boost::posix_time::seconds(0); + + if (st->next_announce.is_negative()) + st->next_announce = boost::posix_time::seconds(0); + + st->announce_interval = boost::posix_time::seconds(0); + + st->current_tracker.clear(); + if (m_last_working_tracker >= 0) + { + TORRENT_ASSERT(m_last_working_tracker < int(m_trackers.size())); + st->current_tracker = m_trackers[m_last_working_tracker].url; + } + else + { + std::vector::const_iterator i; + for (i = m_trackers.begin(); i != m_trackers.end(); ++i) + { + if (!i->updating) continue; + st->current_tracker = i->url; + break; + } + } + + if ((flags & torrent_handle::query_verified_pieces)) + { + st->verified_pieces = m_verified; + } + + st->num_uploads = m_num_uploads; + st->uploads_limit = m_max_uploads == (1<<24)-1 ? -1 : m_max_uploads; + st->num_connections = int(m_connections.size()); + st->connections_limit = m_max_connections == (1<<24)-1 ? -1 : m_max_connections; + // if we don't have any metadata, stop here + + st->queue_position = queue_position(); + st->need_save_resume = need_save_resume_data(); + st->ip_filter_applies = m_apply_ip_filter; + + st->state = (torrent_status::state_t)m_state; + + if (!valid_metadata()) + { + st->state = torrent_status::downloading_metadata; + st->progress_ppm = m_progress_ppm; +#if !TORRENT_NO_FPU + st->progress = m_progress_ppm / 1000000.f; +#endif + st->block_size = 0; + return; + } + + st->block_size = block_size(); + + if (m_state == torrent_status::checking_files) + { + st->progress_ppm = m_progress_ppm; +#if !TORRENT_NO_FPU + st->progress = m_progress_ppm / 1000000.f; +#endif + } + else if (st->total_wanted == 0) + { + st->progress_ppm = 1000000; + st->progress = 1.f; + } + else + { + st->progress_ppm = st->total_wanted_done * 1000000 + / st->total_wanted; +#if !TORRENT_NO_FPU + st->progress = st->progress_ppm / 1000000.f; +#endif + } + + if (has_picker() && (flags & torrent_handle::query_pieces)) + { + st->sparse_regions = m_picker->sparse_regions(); + int num_pieces = m_picker->num_pieces(); + st->pieces.resize(num_pieces, false); + for (int i = 0; i < num_pieces; ++i) + if (m_picker->have_piece(i)) st->pieces.set_bit(i); + } + else if (is_seed()) + { + int num_pieces = m_torrent_file->num_pieces(); + st->pieces.resize(num_pieces, true); + } + st->num_pieces = num_have(); + st->num_seeds = num_seeds(); + if ((flags & torrent_handle::query_distributed_copies) && m_picker.get()) + { + boost::tie(st->distributed_full_copies, st->distributed_fraction) = + m_picker->distributed_copies(); +#if TORRENT_NO_FPU + st->distributed_copies = -1.f; +#else + st->distributed_copies = st->distributed_full_copies + + float(st->distributed_fraction) / 1000; +#endif + } + else + { + st->distributed_full_copies = -1; + st->distributed_fraction = -1; + st->distributed_copies = -1.f; + } + + st->last_seen_complete = m_swarm_last_seen_complete; + } + + void torrent::add_redundant_bytes(int b, torrent::wasted_reason_t reason) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + m_ses.add_redundant_bytes(b, reason); +// TORRENT_ASSERT(m_total_redundant_bytes + m_total_failed_bytes +// <= m_stat.total_payload_download()); + } + + void torrent::add_failed_bytes(int b) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + m_ses.add_failed_bytes(b); +// TORRENT_ASSERT(m_total_redundant_bytes + m_total_failed_bytes +// <= m_stat.total_payload_download()); + } + + int torrent::num_seeds() const + { + TORRENT_ASSERT(m_ses.is_network_thread()); + INVARIANT_CHECK; + + int ret = 0; + for (std::set::const_iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + if ((*i)->is_seed()) ++ret; + return ret; + } + + void torrent::tracker_request_error(tracker_request const& r + , int response_code, error_code const& ec, std::string const& msg + , int retry_interval) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + + INVARIANT_CHECK; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** tracker error: (%d) %s %s", ec.value() + , ec.message().c_str(), msg.c_str()); +#endif + if (r.kind == tracker_request::announce_request) + { + announce_entry* ae = find_tracker(r); + if (ae) + { + ae->failed(settings(), retry_interval); + ae->last_error = ec; + ae->message = msg; + int tracker_index = ae - &m_trackers[0]; +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + debug_log("*** increment tracker fail count [%d]", ae->fails); +#endif + // never talk to this tracker again + if (response_code == 410) ae->fail_limit = 1; + + deprioritize_tracker(tracker_index); + } + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() + , ae?ae->fails:0, response_code, r.url, ec, msg)); + } + } + else if (r.kind == tracker_request::scrape_request) + { + if (response_code == 410) + { + // never talk to this tracker again + announce_entry* ae = find_tracker(r); + if (ae) ae->fail_limit = 1; + } + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), r.url, ec)); + } + } + // announce to the next working tracker + if ((!m_abort && !is_paused()) || r.event == tracker_request::stopped) + announce_with_tracker(r.event); + update_tracker_timer(time_now()); + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + void torrent::debug_log(const char* fmt, ...) const + { + if (!m_ses.m_logger) return; + + va_list v; + va_start(v, fmt); + + char usr[1024]; + vsnprintf(usr, sizeof(usr), fmt, v); + va_end(v); + char buf[1280]; + snprintf(buf, sizeof(buf), "%s: %s: %s\n", time_now_string() + , to_hex(info_hash().to_string()).substr(0, 6).c_str(), usr); + (*m_ses.m_logger) << buf; + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp b/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp new file mode 100644 index 0000000000..4e13755100 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent_handle.cpp @@ -0,0 +1,914 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/thread.hpp" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std +{ + using ::srand; + using ::isalnum; +}; +#endif + +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + torrent_status::torrent_status() + : total_download(0) + , total_upload(0) + , total_payload_download(0) + , total_payload_upload(0) + , total_failed_bytes(0) + , total_redundant_bytes(0) + , total_done(0) + , total_wanted_done(0) + , total_wanted(0) + , all_time_upload(0) + , all_time_download(0) + , added_time(0) + , completed_time(0) + , last_seen_complete(0) + , storage_mode(storage_mode_sparse) + , progress(0.f) + , progress_ppm(0) + , queue_position(0) + , download_rate(0) + , upload_rate(0) + , download_payload_rate(0) + , upload_payload_rate(0) + , num_seeds(0) + , num_peers(0) + , num_complete(-1) + , num_incomplete(-1) + , list_seeds(0) + , list_peers(0) + , connect_candidates(0) + , num_pieces(0) + , distributed_full_copies(0) + , distributed_fraction(0) + , distributed_copies(0.f) + , block_size(0) + , num_uploads(0) + , num_connections(0) + , uploads_limit(0) + , connections_limit(0) + , up_bandwidth_queue(0) + , down_bandwidth_queue(0) + , time_since_upload(0) + , time_since_download(0) + , active_time(0) + , finished_time(0) + , seeding_time(0) + , seed_rank(0) + , last_scrape(0) + , sparse_regions(0) + , priority(0) + , state(checking_resume_data) + , need_save_resume(false) + , ip_filter_applies(true) + , upload_mode(false) + , share_mode(false) + , super_seeding(false) + , paused(false) + , auto_managed(false) + , sequential_download(false) + , is_seeding(false) + , is_finished(false) + , has_metadata(false) + , has_incoming(false) + , seed_mode(false) + , moving_storage(false) + , info_hash(0) + {} + + torrent_status::~torrent_status() {} + + template + void fun_ret(R* ret, bool* done, condition_variable* e, mutex* m, boost::function f) + { + *ret = f(); + mutex::scoped_lock l(*m); + *done = true; + e->notify_all(); + } + + // defined in session.cpp + void fun_wrap(bool* done, condition_variable* e, mutex* m, boost::function f); + +#define TORRENT_ASYNC_CALL(x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t)) + +#define TORRENT_ASYNC_CALL1(x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1)) + +#define TORRENT_ASYNC_CALL2(x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2)) + +#define TORRENT_ASYNC_CALL3(x, a1, a2, a3) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2, a3)) + +#define TORRENT_ASYNC_CALL4(x, a1, a2, a3, a4) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + session_impl& ses = t->session(); \ + ses.m_io_service.dispatch(boost::bind(&torrent:: x, t, a1, a2, a3, a4)) + +#define TORRENT_SYNC_CALL(x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return; \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL1(x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL2(x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL3(x, a1, a2, a3) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (t) { \ + bool done = false; \ + session_impl& ses = t->session(); \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2, a3)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); }; } + +#define TORRENT_SYNC_CALL_RET(type, def, x) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL_RET1(type, def, x, a1) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#define TORRENT_SYNC_CALL_RET2(type, def, x, a1, a2) \ + boost::shared_ptr t = m_torrent.lock(); \ + if (!t) return def; \ + bool done = false; \ + session_impl& ses = t->session(); \ + type r; \ + mutex::scoped_lock l(ses.mut); \ + ses.m_io_service.dispatch(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ + t.reset(); \ + while (!done) { ses.cond.wait(l); } + +#ifndef BOOST_NO_EXCEPTIONS + void throw_invalid_handle() + { + throw libtorrent_exception(errors::invalid_torrent_handle); + } +#endif + + sha1_hash torrent_handle::info_hash() const + { + boost::shared_ptr t = m_torrent.lock(); + const static sha1_hash empty; + if (!t) return empty; + return t->info_hash(); + } + + int torrent_handle::max_uploads() const + { + TORRENT_SYNC_CALL_RET(int, 0, max_uploads); + return r; + } + + void torrent_handle::set_max_uploads(int max_uploads) const + { + TORRENT_ASSERT_PRECOND(max_uploads >= 2 || max_uploads == -1); + TORRENT_ASYNC_CALL2(set_max_uploads, max_uploads, true); + } + + int torrent_handle::max_connections() const + { + TORRENT_SYNC_CALL_RET(int, 0, max_connections); + return r; + } + + void torrent_handle::set_max_connections(int max_connections) const + { + TORRENT_ASSERT_PRECOND(max_connections >= 2 || max_connections == -1); + TORRENT_ASYNC_CALL2(set_max_connections, max_connections, true); + } + + void torrent_handle::set_upload_limit(int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_upload_limit, limit, true); + } + + int torrent_handle::upload_limit() const + { + TORRENT_SYNC_CALL_RET(int, 0, upload_limit); + return r; + } + + void torrent_handle::set_download_limit(int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_download_limit, limit, true); + } + + int torrent_handle::download_limit() const + { + TORRENT_SYNC_CALL_RET(int, 0, download_limit); + return r; + } + + void torrent_handle::move_storage( + std::string const& save_path, int flags) const + { + TORRENT_ASYNC_CALL2(move_storage, save_path, flags); + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + void torrent_handle::move_storage( + std::wstring const& save_path, int flags) const + { + std::string utf8; + wchar_utf8(save_path, utf8); + TORRENT_ASYNC_CALL2(move_storage, utf8, flags); + } + + void torrent_handle::rename_file(int index, std::wstring const& new_name) const + { + std::string utf8; + wchar_utf8(new_name, utf8); + TORRENT_ASYNC_CALL2(rename_file, index, utf8); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + void torrent_handle::rename_file(int index, std::string const& new_name) const + { + TORRENT_ASYNC_CALL2(rename_file, index, new_name); + } + + void torrent_handle::add_extension( + boost::function(torrent*, void*)> const& ext + , void* userdata) + { +#ifndef TORRENT_DISABLE_EXTENSIONS + TORRENT_ASYNC_CALL2(add_extension, ext, userdata); +#endif + } + + bool torrent_handle::set_metadata(char const* metadata, int size) const + { + TORRENT_SYNC_CALL_RET2(bool, false, set_metadata, metadata, size); + return r; + } + + void torrent_handle::pause(int flags) const + { + TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause)); + } + + void torrent_handle::apply_ip_filter(bool b) const + { + TORRENT_ASYNC_CALL1(set_apply_ip_filter, b); + } + + void torrent_handle::set_share_mode(bool b) const + { + TORRENT_ASYNC_CALL1(set_share_mode, b); + } + + void torrent_handle::set_upload_mode(bool b) const + { + TORRENT_ASYNC_CALL1(set_upload_mode, b); + } + + void torrent_handle::flush_cache() const + { + TORRENT_ASYNC_CALL(flush_cache); + } + + void torrent_handle::set_ssl_certificate( + std::string const& certificate + , std::string const& private_key + , std::string const& dh_params + , std::string const& passphrase) + { +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASYNC_CALL4(set_ssl_cert, certificate, private_key, dh_params, passphrase); +#endif + } + + void torrent_handle::set_ssl_certificate_buffer( + std::string const& certificate + , std::string const& private_key + , std::string const& dh_params) + { +#ifdef TORRENT_USE_OPENSSL + TORRENT_ASYNC_CALL3(set_ssl_cert_buffer, certificate, private_key, dh_params); +#endif + } + + void torrent_handle::save_resume_data(int f) const + { + TORRENT_ASYNC_CALL1(save_resume_data, f); + } + + bool torrent_handle::need_save_resume_data() const + { + TORRENT_SYNC_CALL_RET(bool, false, need_save_resume_data); + return r; + } + + void torrent_handle::force_recheck() const + { + TORRENT_ASYNC_CALL(force_recheck); + } + + void torrent_handle::resume() const + { + TORRENT_ASYNC_CALL(resume); + } + + void torrent_handle::auto_managed(bool m) const + { + TORRENT_ASYNC_CALL1(auto_managed, m); + } + + void torrent_handle::set_priority(int p) const + { + TORRENT_ASYNC_CALL1(set_priority, p); + } + + int torrent_handle::queue_position() const + { + TORRENT_SYNC_CALL_RET(int, -1, queue_position); + return r; + } + + void torrent_handle::queue_position_up() const + { + TORRENT_ASYNC_CALL(queue_up); + } + + void torrent_handle::queue_position_down() const + { + TORRENT_ASYNC_CALL(queue_down); + } + + void torrent_handle::queue_position_top() const + { + TORRENT_ASYNC_CALL1(set_queue_position, 0); + } + + void torrent_handle::queue_position_bottom() const + { + TORRENT_ASYNC_CALL1(set_queue_position, INT_MAX); + } + + void torrent_handle::clear_error() const + { + TORRENT_ASYNC_CALL(clear_error); + } + + void torrent_handle::set_tracker_login(std::string const& name + , std::string const& password) const + { + TORRENT_ASYNC_CALL2(set_tracker_login, name, password); + } + + void torrent_handle::file_progress(std::vector& progress, int flags) const + { + TORRENT_SYNC_CALL2(file_progress, boost::ref(progress), flags); + } + + torrent_status torrent_handle::status(boost::uint32_t flags) const + { + torrent_status st; + TORRENT_SYNC_CALL2(status, &st, flags); + return st; + } + + void torrent_handle::set_sequential_download(bool sd) const + { + TORRENT_ASYNC_CALL1(set_sequential_download, sd); + } + + void torrent_handle::piece_availability(std::vector& avail) const + { + TORRENT_SYNC_CALL1(piece_availability, boost::ref(avail)); + } + + void torrent_handle::piece_priority(int index, int priority) const + { + TORRENT_ASYNC_CALL2(set_piece_priority, index, priority); + } + + int torrent_handle::piece_priority(int index) const + { + TORRENT_SYNC_CALL_RET1(int, 0, piece_priority, index); + return r; + } + + void torrent_handle::prioritize_pieces(std::vector const& pieces) const + { + TORRENT_ASYNC_CALL1(prioritize_pieces, pieces); + } + + std::vector torrent_handle::piece_priorities() const + { + std::vector ret; + TORRENT_SYNC_CALL1(piece_priorities, &ret); + return ret; + } + + void torrent_handle::file_priority(int index, int priority) const + { + TORRENT_ASYNC_CALL2(set_file_priority, index, priority); + } + + int torrent_handle::file_priority(int index) const + { + TORRENT_SYNC_CALL_RET1(int, 0, file_priority, index); + return r; + } + + void torrent_handle::prioritize_files(std::vector const& files) const + { + TORRENT_ASYNC_CALL1(prioritize_files, files); + } + + std::vector torrent_handle::file_priorities() const + { + std::vector ret; + TORRENT_SYNC_CALL1(file_priorities, &ret); + return ret; + } + + void torrent_handle::use_interface(const char* net_interface) const + { + TORRENT_ASYNC_CALL1(use_interface, std::string(net_interface)); + } + +#ifndef TORRENT_NO_DEPRECATE +// ============ start deprecation =============== + +#if !TORRENT_NO_FPU + void torrent_handle::file_progress(std::vector& progress) const + { + TORRENT_SYNC_CALL1(file_progress, boost::ref(progress)); + } +#endif + + int torrent_handle::get_peer_upload_limit(tcp::endpoint ip) const + { + TORRENT_SYNC_CALL_RET1(int, -1, get_peer_upload_limit, ip); + return r; + } + + int torrent_handle::get_peer_download_limit(tcp::endpoint ip) const + { + TORRENT_SYNC_CALL_RET1(int, -1, get_peer_download_limit, ip); + return r; + } + + void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_peer_upload_limit, ip, limit); + } + + void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const + { + TORRENT_ASSERT_PRECOND(limit >= -1); + TORRENT_ASYNC_CALL2(set_peer_download_limit, ip, limit); + } + + void torrent_handle::set_ratio(float ratio) const + { + TORRENT_ASSERT_PRECOND(ratio >= 0.f); + } + + bool torrent_handle::is_seed() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_seed); + return r; + } + + bool torrent_handle::is_finished() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_finished); + return r; + } + + bool torrent_handle::is_paused() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_torrent_paused); + return r; + } + + bool torrent_handle::is_sequential_download() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_sequential_download); + return r; + } + + bool torrent_handle::is_auto_managed() const + { + TORRENT_SYNC_CALL_RET(bool, false, is_auto_managed); + return r; + } + + bool torrent_handle::has_metadata() const + { + TORRENT_SYNC_CALL_RET(bool, false, valid_metadata); + return r; + } + + void torrent_handle::filter_piece(int index, bool filter) const + { + TORRENT_ASYNC_CALL2(filter_piece, index, filter); + } + + void torrent_handle::filter_pieces(std::vector const& pieces) const + { + TORRENT_ASYNC_CALL1(filter_pieces, pieces); + } + + bool torrent_handle::is_piece_filtered(int index) const + { + TORRENT_SYNC_CALL_RET1(bool, false, is_piece_filtered, index); + return r; + } + + std::vector torrent_handle::filtered_pieces() const + { + std::vector ret; + TORRENT_SYNC_CALL1(filtered_pieces, ret); + return ret; + } + + void torrent_handle::filter_files(std::vector const& files) const + { + TORRENT_ASYNC_CALL1(filter_files, files); + } + + bool torrent_handle::super_seeding() const + { + TORRENT_SYNC_CALL_RET(bool, false, super_seeding); + return r; + } + +// ============ end deprecation =============== +#endif + + std::vector torrent_handle::trackers() const + { + const static std::vector empty; + TORRENT_SYNC_CALL_RET(std::vector, empty, trackers); + return r; + } + + void torrent_handle::add_url_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::url_seed); + } + + void torrent_handle::remove_url_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::url_seed); + } + + std::set torrent_handle::url_seeds() const + { + const static std::set empty; + TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::url_seed); + return r; + } + + void torrent_handle::add_http_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::http_seed); + } + + void torrent_handle::remove_http_seed(std::string const& url) const + { + TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::http_seed); + } + + std::set torrent_handle::http_seeds() const + { + const static std::set empty; + TORRENT_SYNC_CALL_RET1(std::set, empty, web_seeds, web_seed_entry::http_seed); + return r; + } + + void torrent_handle::replace_trackers( + std::vector const& urls) const + { + TORRENT_ASYNC_CALL1(replace_trackers, urls); + } + + void torrent_handle::add_tracker(announce_entry const& url) const + { + TORRENT_ASYNC_CALL1(add_tracker, url); + } + + void torrent_handle::add_piece(int piece, char const* data, int flags) const + { + TORRENT_SYNC_CALL3(add_piece, piece, data, flags); + } + + void torrent_handle::read_piece(int piece) const + { + TORRENT_ASYNC_CALL1(read_piece, piece); + } + + bool torrent_handle::have_piece(int piece) const + { + TORRENT_SYNC_CALL_RET1(bool, false, have_piece, piece); + return r; + } + + storage_interface* torrent_handle::get_storage_impl() const + { + TORRENT_SYNC_CALL_RET(storage_interface*, 0, get_storage); + return r; + } + + bool torrent_handle::is_valid() const + { + return !m_torrent.expired(); + } + + boost::intrusive_ptr torrent_handle::torrent_file() const + { + TORRENT_SYNC_CALL_RET(boost::intrusive_ptr + , boost::intrusive_ptr(), get_torrent_copy); + return r; + } + +#ifndef TORRENT_NO_DEPRECATE + // this function should either be removed, or return + // reference counted handle to the torrent_info which + // forces the torrent to stay loaded while the client holds it + torrent_info const& torrent_handle::get_torrent_info() const + { +#ifdef BOOST_NO_EXCEPTIONS + const static torrent_info empty(sha1_hash(0)); +#endif + boost::shared_ptr t = m_torrent.lock(); + if (!t) +#ifdef BOOST_NO_EXCEPTIONS + return empty; +#else + throw_invalid_handle(); +#endif + if (!t->valid_metadata()) +#ifdef BOOST_NO_EXCEPTIONS + return empty; +#else + throw_invalid_handle(); +#endif + return t->torrent_file(); + } + + entry torrent_handle::write_resume_data() const + { + entry ret(entry::dictionary_t); + TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret)); + t = m_torrent.lock(); + if (t) + { + bool done = false; + session_impl& ses = t->session(); + mutex::scoped_lock l(ses.mut); + ses.m_io_service.dispatch(boost::bind(&fun_wrap, &done, &ses.cond + , &ses.mut, boost::function(boost::bind( + &piece_manager::write_resume_data, &t->filesystem(), boost::ref(ret))))); + t.reset(); + while (!done) { ses.cond.wait(l); } + } + + return ret; + } + + std::string torrent_handle::save_path() const + { + TORRENT_SYNC_CALL_RET(std::string, "", save_path); + return r; + } + + std::string torrent_handle::name() const + { + TORRENT_SYNC_CALL_RET(std::string, "", name); + return r; + } + +#endif + + void torrent_handle::connect_peer(tcp::endpoint const& adr, int source) const + { + TORRENT_ASYNC_CALL2(add_peer, adr, source); + } + +#ifndef TORRENT_NO_DEPRECATE + void torrent_handle::force_reannounce( + boost::posix_time::time_duration duration) const + { + TORRENT_ASYNC_CALL2(force_tracker_request, time_now() + + seconds(duration.total_seconds()), -1); + } +#endif + + void torrent_handle::force_dht_announce() const + { +#ifndef TORRENT_DISABLE_DHT + TORRENT_ASYNC_CALL(dht_announce); +#endif + } + + void torrent_handle::force_reannounce(int s, int idx) const + { + TORRENT_ASYNC_CALL2(force_tracker_request, time_now() + seconds(s), idx); + } + + void torrent_handle::scrape_tracker() const + { + TORRENT_ASYNC_CALL(scrape_tracker); + } + + void torrent_handle::super_seeding(bool on) const + { + TORRENT_ASYNC_CALL1(super_seeding, on); + } + + void torrent_handle::resolve_countries(bool r) + { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_ASYNC_CALL1(resolve_countries, r); +#endif + } + + bool torrent_handle::resolve_countries() const + { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + TORRENT_SYNC_CALL_RET(bool, false, resolving_countries); + return r; +#else + return false; +#endif + } + + void torrent_handle::get_full_peer_list(std::vector& v) const + { + TORRENT_SYNC_CALL1(get_full_peer_list, boost::ref(v)); + } + + void torrent_handle::get_peer_info(std::vector& v) const + { + TORRENT_SYNC_CALL1(get_peer_info, boost::ref(v)); + } + + void torrent_handle::get_download_queue(std::vector& queue) const + { + TORRENT_SYNC_CALL1(get_download_queue, &queue); + } + + void torrent_handle::set_piece_deadline(int index, int deadline, int flags) const + { + TORRENT_ASYNC_CALL3(set_piece_deadline, index, deadline, flags); + } + + void torrent_handle::reset_piece_deadline(int index) const + { + TORRENT_ASYNC_CALL1(reset_piece_deadline, index); + } + + void torrent_handle::clear_piece_deadlines() const + { + TORRENT_ASYNC_CALL(clear_time_critical); + } + + boost::shared_ptr torrent_handle::native_handle() const + { + return m_torrent.lock(); + } + + std::size_t hash_value(torrent_status const& ts) + { + return hash_value(ts.handle); + } + + std::size_t hash_value(torrent_handle const& th) + { + // using the locked shared_ptr value as hash doesn't work + // for expired weak_ptrs. So, we're left with a hack + return std::size_t(*reinterpret_cast(&th.m_torrent)); + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/torrent_info.cpp b/apps/Launcher/ext/libtorrent/src/torrent_info.cpp new file mode 100644 index 0000000000..a14e8ddbc7 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/torrent_info.cpp @@ -0,0 +1,1473 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM +#include +#include +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/config.hpp" +#include "libtorrent/ConvertUTF.h" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/escape_string.hpp" // is_space +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/magnet_uri.hpp" + +#if TORRENT_USE_I2P +#include "libtorrent/parse_url.hpp" +#endif + +namespace libtorrent +{ + + bool valid_path_character(char c) + { +#ifdef TORRENT_WINDOWS + static const char invalid_chars[] = "?<>\"|\b*:"; +#else + static const char invalid_chars[] = ""; +#endif + if (c >= 0 && c < 32) return false; + return std::strchr(invalid_chars, c) == 0; + } + + // fixes invalid UTF-8 sequences and + // replaces characters that are invalid + // in paths + TORRENT_EXTRA_EXPORT bool verify_encoding(std::string& target, bool fix_paths = false) + { + if (target.empty()) return true; + + std::string tmp_path; + bool valid_encoding = true; + + UTF8 const* ptr = (UTF8 const*)&target[0]; + UTF8 const* end = (UTF8 const*)&target[0] + target.size(); + while (ptr < end) + { + UTF32 codepoint; + UTF32* cp = &codepoint; + + // decode a single utf-8 character + ConversionResult res = ConvertUTF8toUTF32(&ptr, end, &cp, cp + 1 + , lenientConversion); + + // this was the last character, and nothing was + // written to the destination buffer (i.e. the source character was + // truncated) + if (res == sourceExhausted + || res == sourceIllegal) + { + if (cp == &codepoint) + { + if (res == sourceExhausted) + ptr = end; + else + ++ptr; + + codepoint = '_'; + valid_encoding = false; + } + } + else if ((res != conversionOK && res != targetExhausted) + || codepoint == UNI_REPLACEMENT_CHAR) + { + // we expect the conversion to fail with targetExhausted, since we + // only pass in a single destination character slot. The last + // character will succeed though. Also, if the character was replaced, + // use our own replacement symbol (underscore). + codepoint = '_'; + valid_encoding = false; + } + + // if fix paths is true, also replace characters that are invalid + // in filenames + if (fix_paths && codepoint < 0x7f && !valid_path_character(codepoint)) + { + codepoint = '_'; + valid_encoding = false; + } + + // encode codepoint into utf-8 + cp = &codepoint; + UTF8 sequence[5]; + UTF8* start = sequence; + res = ConvertUTF32toUTF8((const UTF32**)&cp, cp + 1, &start, start + 5, lenientConversion); + TORRENT_ASSERT(res == conversionOK); + + for (int i = 0; i < start - sequence; ++i) + tmp_path += (char)sequence[i]; + } + + // the encoding was not valid utf-8 + // save the original encoding and replace the + // commonly used path with the correctly + // encoded string + if (!valid_encoding) target = tmp_path; + return valid_encoding; + } + + // TODO: 1 we might save constructing a std::string if this would take a char const* instead + bool valid_path_element(std::string const& element) + { + if (element.empty() + || element == "." || element == ".." + || element[0] == '/' || element[0] == '\\' + || element[element.size()-1] == ':') + return false; + return true; + } + + TORRENT_EXTRA_EXPORT void trim_path_element(std::string& element) + { + const int max_path_len = TORRENT_MAX_PATH; + + // on windows, the max path is expressed in + // unicode characters, not bytes +#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING + std::wstring path_element; + utf8_wchar(element, path_element); + if (path_element.size() > max_path_len) + { + // truncate filenames that are too long. But keep extensions! + std::wstring ext; + wchar_t const* ext1 = wcsrchr(path_element.c_str(), '.'); + if (ext1 != NULL) ext = ext1; + + if (ext.size() > 15) + { + path_element.resize(max_path_len); + } + else + { + path_element.resize(max_path_len - ext.size()); + path_element += ext; + } + } + // remove trailing spaces and dots. These aren't allowed in filenames on windows + for (int i = path_element.size() - 1; i >= 0; --i) + { + if (path_element[i] != L' ' && path_element[i] != L'.') break; + path_element.resize(i); + } + if (path_element.empty()) path_element = L"_"; + wchar_utf8(path_element, element); +#else + std::string& path_element = element; + if (int(path_element.size()) > max_path_len) + { + + // truncate filenames that are too long. But keep extensions! + std::string ext = extension(path_element); + if (ext.size() > 15) + { + path_element.resize(max_path_len); + } + else + { + path_element.resize(max_path_len - ext.size()); + path_element += ext; + } + } + + // remove trailing spaces and dots. These aren't allowed in filenames on windows + // apply rules consistently across platforms though + for (int i = path_element.size() - 1; i >= 0; --i) + { + if (path_element[i] != ' ' && path_element[i] != '.') break; + path_element.resize(i); + } + + if (path_element.empty()) path_element = "_"; +#endif + } + + TORRENT_EXTRA_EXPORT std::string sanitize_path(std::string const& p) + { + std::string new_path; + std::string split = split_path(p); + for (char const* e = split.c_str(); e != 0; e = next_path_element(e)) + { + std::string pe = e; +#if !TORRENT_USE_UNC_PATHS && defined TORRENT_WINDOWS + // if we're not using UNC paths on windows, there + // are certain filenames we're not allowed to use + const static char const* reserved_names[] = + { + "con", "prn", "aux", "clock$", "nul", + "com0", "com1", "com2", "com3", "com4", + "com5", "com6", "com7", "com8", "com9", + "lpt0", "lpt1", "lpt2", "lpt3", "lpt4", + "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" + }; + int num_names = sizeof(reserved_names)/sizeof(reserved_names[0]); + + char const* file_end = strrchr(pe.c_str(), '.'); + std::string name; + if (file_end) name.assign(pe.c_str(), file_end); + else name = pe; + std::transform(name.begin(), name.end(), name.begin(), &to_lower); + char const** str = std::find(reserved_names, reserved_names + num_names, name); + if (str != reserved_names + num_names) + { + pe += "_"; + } +#endif + if (!valid_path_element(pe)) continue; + trim_path_element(pe); + new_path = combine_path(new_path, pe); + } + return new_path; + } + + bool extract_single_file(lazy_entry const& dict, file_entry& target + , std::string const& root_dir, lazy_entry const** filehash + , lazy_entry const** filename, time_t* mtime) + { + if (dict.type() != lazy_entry::dict_t) return false; + lazy_entry const* length = dict.dict_find("length"); + if (length == 0 || length->type() != lazy_entry::int_t) + return false; + target.size = length->int_value(); + if (target.size < 0) + return false; + + size_type ts = dict.dict_find_int_value("mtime", -1); + if (ts > 0) *mtime = std::time_t(ts); + + // prefer the name.utf-8 + // because if it exists, it is more + // likely to be correctly encoded + + lazy_entry const* p = dict.dict_find("path.utf-8"); + if (p == 0 || p->type() != lazy_entry::list_t) + p = dict.dict_find("path"); + if (p == 0 || p->type() != lazy_entry::list_t) + return false; + + std::string path = root_dir; + for (int i = 0, end(p->list_size()); i < end; ++i) + { + if (p->list_at(i)->type() != lazy_entry::string_t) + return false; + std::string path_element = p->list_at(i)->string_value(); + if (path_element.empty()) + path_element = "_"; + if (!valid_path_element(path_element)) continue; + if (i == end - 1) *filename = p->list_at(i); + trim_path_element(path_element); + path = combine_path(path, path_element); + } + path = sanitize_path(path); + verify_encoding(path, true); + + // bitcomet pad file + if (path.find("_____padding_file_") != std::string::npos) + target.pad_file = true; + + target.path = path; + + lazy_entry const* attr = dict.dict_find_string("attr"); + if (attr) + { + for (int i = 0; i < attr->string_length(); ++i) + { + switch (attr->string_ptr()[i]) + { + case 'l': target.symlink_attribute = true; target.size = 0; break; + case 'x': target.executable_attribute = true; break; + case 'h': target.hidden_attribute = true; break; + case 'p': target.pad_file = true; break; + } + } + } + + lazy_entry const* fh = dict.dict_find_string("sha1"); + if (fh && fh->string_length() == 20 && filehash) + *filehash = fh; + + lazy_entry const* s_p = dict.dict_find("symlink path"); + if (s_p != 0 && s_p->type() == lazy_entry::list_t && target.symlink_attribute) + { + for (int i = 0, end(s_p->list_size()); i < end; ++i) + { + std::string path_element = s_p->list_at(i)->string_value(); + trim_path_element(path_element); + target.symlink_path = combine_path(target.symlink_path, path_element); + } + } + else + { + target.symlink_attribute = false; + } + + return true; + } + + struct string_less_no_case + { + bool operator()(std::string const& lhs, std::string const& rhs) + { + char c1, c2; + char const* s1 = lhs.c_str(); + char const* s2 = rhs.c_str(); + + while (*s1 != 0 || *s2 != 0) + { + c1 = to_lower(*s1); + c2 = to_lower(*s2); + if (c1 < c2) return true; + if (c1 > c2) return false; + ++s1; + ++s2; + } + return false; + } + }; + + bool extract_files(lazy_entry const& list, file_storage& target + , std::string const& root_dir, ptrdiff_t info_ptr_diff) + { + if (list.type() != lazy_entry::list_t) return false; + target.reserve(list.list_size()); + + // TODO: 1 this logic should be a separate step + // done once the torrent is loaded, and the original + // filenames should be preserved! + std::set files; + + for (int i = 0, end(list.list_size()); i < end; ++i) + { + lazy_entry const* file_hash = 0; + time_t mtime = 0; + file_entry e; + lazy_entry const* fee = 0; + if (!extract_single_file(*list.list_at(i), e, root_dir + , &file_hash, &fee, &mtime)) + return false; + + // as long as this file already exists + // increase the counter + int cnt = 0; + if (!files.insert(e.path).second) + { + std::string base = remove_extension(e.path); + std::string ext = extension(e.path); + do + { + ++cnt; + char new_ext[50]; + snprintf(new_ext, sizeof(new_ext), ".%d%s", cnt, ext.c_str()); + e.path = base + new_ext; + } while (!files.insert(e.path).second); + } + target.add_file(e, file_hash ? file_hash->string_ptr() + info_ptr_diff : 0); + + // This is a memory optimization! Instead of having + // each entry keep a string for its filename, make it + // simply point into the info-section buffer + int last_index = target.num_files() - 1; + // TODO: 1 once the filename renaming is removed from here + // this check can be removed as well + if (fee && target.file_name(last_index) == fee->string_value()) + { + // this string pointer does not necessarily point into + // the m_info_section buffer. + char const* str_ptr = fee->string_ptr() + info_ptr_diff; + target.rename_file_borrow(last_index, str_ptr, fee->string_length()); + } + } + return true; + } + + int merkle_get_parent(int tree_node) + { + // node 0 doesn't have a parent + TORRENT_ASSERT(tree_node > 0); + return (tree_node - 1) / 2; + } + + int merkle_get_sibling(int tree_node) + { + // node 0 doesn't have a sibling + TORRENT_ASSERT(tree_node > 0); + // even numbers have their sibling to the left + // odd numbers have their sibling to the right + return tree_node + (tree_node&1?1:-1); + } + + int merkle_num_nodes(int leafs) + { + TORRENT_ASSERT(leafs > 0); + return (leafs << 1) - 1; + } + + int merkle_num_leafs(int pieces) + { + TORRENT_ASSERT(pieces > 0); + // round up to nearest 2 exponent + int ret = 1; + while (pieces > ret) ret <<= 1; + return ret; + } + + int load_file(std::string const& filename, std::vector& v, error_code& ec, int limit = 8000000) + { + ec.clear(); + file f; + if (!f.open(filename, file::read_only, ec)) return -1; + size_type s = f.get_size(ec); + if (ec) return -1; + if (s > limit) + { + ec = error_code(errors::metadata_too_large, get_libtorrent_category()); + return -2; + } + v.resize((unsigned int)s); + if (s == 0) return 0; + file::iovec_t b = {&v[0], size_t(s) }; + size_type read = f.readv(0, &b, 1, ec); + if (read != s) return -3; + if (ec) return -3; + return 0; + } + + announce_entry::announce_entry(std::string const& u) + : url(u) + , next_announce(min_time()) + , min_announce(min_time()) + , scrape_incomplete(-1) + , scrape_complete(-1) + , scrape_downloaded(-1) + , tier(0) + , fail_limit(0) + , fails(0) + , updating(false) + , source(0) + , verified(false) + , start_sent(false) + , complete_sent(false) + , send_stats(true) + {} + + announce_entry::announce_entry() + : next_announce(min_time()) + , min_announce(min_time()) + , scrape_incomplete(-1) + , scrape_complete(-1) + , scrape_downloaded(-1) + , tier(0) + , fail_limit(0) + , fails(0) + , updating(false) + , source(0) + , verified(false) + , start_sent(false) + , complete_sent(false) + , send_stats(true) + {} + + announce_entry::~announce_entry() {} + + int announce_entry::next_announce_in() const + { return total_seconds(next_announce - time_now()); } + + int announce_entry::min_announce_in() const + { return total_seconds(min_announce - time_now()); } + + void announce_entry::failed(session_settings const& sett, int retry_interval) + { + ++fails; + // the exponential back-off ends up being: + // 7, 15, 27, 45, 95, 127, 165, ... seconds + // with the default tracker_backoff of 250 + int delay = (std::min)(tracker_retry_delay_min + int(fails) * int(fails) + * tracker_retry_delay_min * sett.tracker_backoff / 100 + , int(tracker_retry_delay_max)); + delay = (std::max)(delay, retry_interval); + next_announce = time_now() + seconds(delay); + updating = false; + } + + bool announce_entry::can_announce(ptime now, bool is_seed) const + { + // if we're a seed and we haven't sent a completed + // event, we need to let this announce through + bool need_send_complete = is_seed && !complete_sent; + + return now >= next_announce + && (now >= min_announce || need_send_complete) + && (fails < fail_limit || fail_limit == 0) + && !updating; + } + + void announce_entry::trim() + { + while (!url.empty() && is_space(url[0])) + url.erase(url.begin()); + } + + web_seed_entry::web_seed_entry(std::string const& url_, type_t type_ + , std::string const& auth_ + , headers_t const& extra_headers_) + : url(url_), type(type_) + , auth(auth_), extra_headers(extra_headers_) + , retry(time_now()) + , supports_keepalive(true) + , resolving(false), removed(false) + , peer_info(tcp::endpoint(), true, 0) + { + peer_info.web_seed = true; + restart_request.piece = -1; + } + + torrent_info::torrent_info(torrent_info const& t, int flags) + : m_merkle_first_leaf(t.m_merkle_first_leaf) + , m_files(t.m_files) + , m_orig_files(t.m_orig_files) + , m_urls(t.m_urls) + , m_web_seeds(t.m_web_seeds) + , m_nodes(t.m_nodes) + , m_merkle_tree(t.m_merkle_tree) + , m_piece_hashes(t.m_piece_hashes) + , m_comment(t.m_comment) + , m_created_by(t.m_created_by) + , m_creation_date(t.m_creation_date) + , m_info_hash(t.m_info_hash) + , m_info_section_size(t.m_info_section_size) + , m_multifile(t.m_multifile) + , m_private(t.m_private) + , m_i2p(t.m_i2p) + { +#if TORRENT_USE_INVARIANT_CHECKS + t.check_invariant(); +#endif + if (m_info_section_size > 0) + { + error_code ec; + m_info_section.reset(new char[m_info_section_size]); + memcpy(m_info_section.get(), t.m_info_section.get(), m_info_section_size); +#if TORRENT_USE_ASSERTS || !defined BOOST_NO_EXCEPTIONS + int ret = +#endif + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); +#ifndef BOOST_NO_EXCEPTIONS + if (ret != 0) throw libtorrent_exception(ec); +#endif + TORRENT_ASSERT(ret == 0); + + ptrdiff_t offset = m_info_section.get() - t.m_info_section.get(); + + m_piece_hashes += offset; + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + INVARIANT_CHECK; + } + + void torrent_info::remap_files(file_storage const& f) + { + INVARIANT_CHECK; + + // the new specified file storage must have the exact + // same size as the current file storage + TORRENT_ASSERT(m_files.total_size() == f.total_size()); + + if (m_files.total_size() != f.total_size()) return; + copy_on_write(); + m_files = f; + m_files.set_num_pieces(m_orig_files->num_pieces()); + m_files.set_piece_length(m_orig_files->piece_length()); + } + +#ifndef TORRENT_NO_DEPRECATE + // standard constructor that parses a torrent file + torrent_info::torrent_info(entry const& torrent_file) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector tmp; + std::back_insert_iterator > out(tmp); + bencode(out, torrent_file); + + lazy_entry e; + error_code ec; + if (tmp.size() == 0 || lazy_bdecode(&tmp[0], &tmp[0] + tmp.size(), e, ec) != 0) + { +#ifndef BOOST_NO_EXCEPTIONS + throw invalid_torrent_file(ec); +#endif + return; + } +#ifndef BOOST_NO_EXCEPTIONS + if (!parse_torrent_file(e, ec, 0)) + throw invalid_torrent_file(ec); +#else + parse_torrent_file(e, ec, 0); +#endif + INVARIANT_CHECK; + } +#endif + +#ifndef BOOST_NO_EXCEPTIONS + torrent_info::torrent_info(lazy_entry const& torrent_file, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + error_code ec; + if (!parse_torrent_file(torrent_file, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(char const* buffer, int size, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + error_code ec; + lazy_entry e; + if (lazy_bdecode(buffer, buffer + size, e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(std::string const& filename, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + error_code ec; + int ret = load_file(filename, buf, ec); + if (ret < 0) throw invalid_torrent_file(ec); + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + torrent_info::torrent_info(std::wstring const& filename, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + std::string utf8; + wchar_utf8(filename, utf8); + error_code ec; + int ret = load_file(utf8, buf, ec); + if (ret < 0) throw invalid_torrent_file(ec); + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + throw invalid_torrent_file(ec); + + if (!parse_torrent_file(e, ec, flags)) + throw invalid_torrent_file(ec); + + INVARIANT_CHECK; + } + + void torrent_info::rename_file(int index, std::wstring const& new_filename) + { + copy_on_write(); + m_files.rename_file_deprecated(index, new_filename); + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING +#endif + + torrent_info::torrent_info(lazy_entry const& torrent_file, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + parse_torrent_file(torrent_file, ec, flags); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(char const* buffer, int size, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + lazy_entry e; + if (lazy_bdecode(buffer, buffer + size, e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } + + torrent_info::torrent_info(std::string const& filename, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + int ret = load_file(filename, buf, ec); + if (ret < 0) return; + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } + +#if TORRENT_USE_WSTRING +#ifndef TORRENT_NO_DEPRECATE + torrent_info::torrent_info(std::wstring const& filename, error_code& ec, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(0) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + { + std::vector buf; + std::string utf8; + wchar_utf8(filename, utf8); + int ret = load_file(utf8, buf, ec); + if (ret < 0) return; + + lazy_entry e; + if (buf.size() == 0 || lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0) + return; + parse_torrent_file(e, ec, flags); + + INVARIANT_CHECK; + } +#endif // TORRENT_NO_DEPRECATE +#endif // TORRENT_USE_WSTRING + + // constructor used for creating new torrents + // will not contain any hashes, comments, creation date + // just the necessary to use it with piece manager + // used for torrents with no metadata + torrent_info::torrent_info(sha1_hash const& info_hash, int flags) + : m_merkle_first_leaf(0) + , m_piece_hashes(0) + , m_creation_date(time(0)) + , m_info_hash(info_hash) + , m_info_section_size(0) + , m_multifile(false) + , m_private(false) + , m_i2p(false) + {} + + torrent_info::~torrent_info() + {} + + void torrent_info::copy_on_write() + { + INVARIANT_CHECK; + + if (m_orig_files) return; + m_orig_files.reset(new file_storage(m_files)); + } + +#define SWAP(a, b) \ + tmp = a; \ + a = b; \ + b = tmp; + + void torrent_info::swap(torrent_info& ti) + { + INVARIANT_CHECK; + + using std::swap; + m_urls.swap(ti.m_urls); + m_web_seeds.swap(ti.m_web_seeds); + m_files.swap(ti.m_files); + m_orig_files.swap(ti.m_orig_files); + m_nodes.swap(ti.m_nodes); + swap(m_info_hash, ti.m_info_hash); + swap(m_creation_date, ti.m_creation_date); + m_comment.swap(ti.m_comment); + m_created_by.swap(ti.m_created_by); + boost::uint32_t tmp; + SWAP(m_multifile, ti.m_multifile); + SWAP(m_private, ti.m_private); + SWAP(m_i2p, ti.m_i2p); + swap(m_info_section, ti.m_info_section); + SWAP(m_info_section_size, ti.m_info_section_size); + swap(m_piece_hashes, ti.m_piece_hashes); + m_info_dict.swap(ti.m_info_dict); + swap(m_merkle_tree, ti.m_merkle_tree); + SWAP(m_merkle_first_leaf, ti.m_merkle_first_leaf); + } + +#undef SWAP + + std::string torrent_info::ssl_cert() const + { + // this is parsed lazily + if (m_info_dict.type() == lazy_entry::none_t) + { + error_code ec; + lazy_bdecode(m_info_section.get(), m_info_section.get() + + m_info_section_size, m_info_dict, ec); + if (ec) return ""; + } + if (m_info_dict.type() != lazy_entry::dict_t) return ""; + return m_info_dict.dict_find_string_value("ssl-cert"); + } + + bool torrent_info::parse_info_section(lazy_entry const& info, error_code& ec, int flags) + { + if (info.type() != lazy_entry::dict_t) + { + ec = errors::torrent_info_no_dict; + return false; + } + + // hash the info-field to calculate info-hash + hasher h; + std::pair section = info.data_section(); + h.update(section.first, section.second); + m_info_hash = h.final(); + + // copy the info section + m_info_section_size = section.second; + m_info_section.reset(new char[m_info_section_size]); + std::memcpy(m_info_section.get(), section.first, m_info_section_size); + TORRENT_ASSERT(section.first[0] == 'd'); + TORRENT_ASSERT(section.first[m_info_section_size-1] == 'e'); + + // when translating a pointer that points into the 'info' tree's + // backing buffer, into a pointer to our copy of the info section, + // this is the pointer offset to use. + ptrdiff_t info_ptr_diff = m_info_section.get() - section.first; + + // extract piece length + int piece_length = info.dict_find_int_value("piece length", -1); + if (piece_length <= 0) + { + ec = errors::torrent_missing_piece_length; + return false; + } + m_files.set_piece_length(piece_length); + + // extract file name (or the directory name if it's a multifile libtorrent) + lazy_entry const* name_ent = info.dict_find_string("name.utf-8"); + if (name_ent == 0) name_ent = info.dict_find_string("name"); + if (name_ent == 0) + { + ec = errors::torrent_missing_name; + return false; + } + + std::string name = name_ent->string_value(); + if (name.empty()) name = to_hex(m_info_hash.to_string()); + name = sanitize_path(name); + + if (!valid_path_element(name)) + { + ec = errors::torrent_invalid_name; + return false; + } + + // correct utf-8 encoding errors + verify_encoding(name, true); + + // extract file list + lazy_entry const* i = info.dict_find_list("files"); + if (i == 0) + { + // if there's no list of files, there has to be a length + // field. + file_entry e; + e.path = name; + e.offset = 0; + e.size = info.dict_find_int_value("length", -1); + if (e.size < 0) + { + ec = errors::torrent_invalid_length; + return false; + } + e.mtime = info.dict_find_int_value("mtime", 0); + lazy_entry const* attr = info.dict_find_string("attr"); + if (attr) + { + for (int i = 0; i < attr->string_length(); ++i) + { + switch (attr->string_ptr()[i]) + { + case 'l': e.symlink_attribute = true; e.size = 0; break; + case 'x': e.executable_attribute = true; break; + case 'h': e.hidden_attribute = true; break; + case 'p': e.pad_file = true; break; + } + } + } + + lazy_entry const* s_p = info.dict_find("symlink path"); + if (s_p != 0 && s_p->type() == lazy_entry::list_t) + { + for (int i = 0, end(s_p->list_size()); i < end; ++i) + { + std::string path_element = s_p->list_at(i)->string_value(); + trim_path_element(path_element); + e.symlink_path = combine_path(e.symlink_path, path_element); + } + } + else + { + e.symlink_attribute = false; + } + + lazy_entry const* fh = info.dict_find_string("sha1"); + if (fh && fh->string_length() != 20) fh = 0; + + // bitcomet pad file + if (e.path.find("_____padding_file_") != std::string::npos) + e.pad_file = true; + m_files.add_file(e, fh ? fh->string_ptr() + info_ptr_diff : 0); + m_multifile = false; + } + else + { + if (!extract_files(*i, m_files, name, info_ptr_diff)) + { + ec = errors::torrent_file_parse_failed; + return false; + } + m_multifile = true; + } + TORRENT_ASSERT(!m_files.name().empty()); + + // extract sha-1 hashes for all pieces + // we want this division to round upwards, that's why we have the + // extra addition + + m_files.set_num_pieces(int((m_files.total_size() + m_files.piece_length() - 1) + / m_files.piece_length())); + + lazy_entry const* pieces = info.dict_find_string("pieces"); + lazy_entry const* root_hash = info.dict_find_string("root hash"); + if (pieces == 0 && root_hash == 0) + { + ec = errors::torrent_missing_pieces; + return false; + } + + if (pieces) + { + if (pieces->string_length() != m_files.num_pieces() * 20) + { + ec = errors::torrent_invalid_hashes; + return false; + } + + m_piece_hashes = pieces->string_ptr() + info_ptr_diff; + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + else + { + TORRENT_ASSERT(root_hash); + if (root_hash->string_length() != 20) + { + ec = errors::torrent_invalid_hashes; + return false; + } + int num_leafs = merkle_num_leafs(m_files.num_pieces()); + int num_nodes = merkle_num_nodes(num_leafs); + m_merkle_first_leaf = num_nodes - num_leafs; + m_merkle_tree.resize(num_nodes); + std::memset(&m_merkle_tree[0], 0, num_nodes * 20); + m_merkle_tree[0].assign(root_hash->string_ptr()); + } + + m_private = info.dict_find_int_value("private", 0); + + return true; + } + + bool torrent_info::add_merkle_nodes(std::map const& subtree + , int piece) + { + INVARIANT_CHECK; + + int n = m_merkle_first_leaf + piece; + typedef std::map::const_iterator iter; + iter i = subtree.find(n); + if (i == subtree.end()) return false; + sha1_hash h = i->second; + + // if the verification passes, these are the + // nodes to add to our tree + std::map to_add; + + while (n > 0) + { + int sibling = merkle_get_sibling(n); + int parent = merkle_get_parent(n); + iter sibling_hash = subtree.find(sibling); + if (sibling_hash == subtree.end()) + return false; + to_add[n] = h; + to_add[sibling] = sibling_hash->second; + hasher hs; + if (sibling < n) + { + hs.update((char const*)&sibling_hash->second[0], 20); + hs.update((char const*)&h[0], 20); + } + else + { + hs.update((char const*)&h[0], 20); + hs.update((char const*)&sibling_hash->second[0], 20); + } + h = hs.final(); + n = parent; + } + if (h != m_merkle_tree[0]) return false; + + // the nodes and piece hash matched the root-hash + // insert them into our tree + + for (std::map::iterator i = to_add.begin() + , end(to_add.end()); i != end; ++i) + { + m_merkle_tree[i->first] = i->second; + } + return true; + } + + // builds a list of nodes that are required to verify + // the given piece + std::map torrent_info::build_merkle_list(int piece) const + { + INVARIANT_CHECK; + + std::map ret; + int n = m_merkle_first_leaf + piece; + ret[n] = m_merkle_tree[n]; + ret[0] = m_merkle_tree[0]; + while (n > 0) + { + int sibling = merkle_get_sibling(n); + int parent = merkle_get_parent(n); + ret[sibling] = m_merkle_tree[sibling]; + // we cannot build the tree path if one + // of the nodes in the tree is missing + TORRENT_ASSERT(m_merkle_tree[sibling] != sha1_hash(0)); + n = parent; + } + return ret; + } + +#if TORRENT_USE_I2P + bool is_i2p_url(std::string const& url) + { + using boost::tuples::ignore; + std::string hostname; + error_code ec; + boost::tie(ignore, ignore, hostname, ignore, ignore) + = parse_url_components(url, ec); + char const* top_domain = strrchr(hostname.c_str(), '.'); + return top_domain && strcmp(top_domain, ".i2p") == 0; + } +#endif + + bool torrent_info::parse_torrent_file(lazy_entry const& torrent_file, error_code& ec, int flags) + { + if (torrent_file.type() != lazy_entry::dict_t) + { + ec = errors::torrent_is_no_dict; + return false; + } + + lazy_entry const* info = torrent_file.dict_find_dict("info"); + if (info == 0) + { + lazy_entry const* link = torrent_file.dict_find_string("magnet-uri"); + if (link) + { + std::string uri = link->string_value(); + + add_torrent_params p; + parse_magnet_uri(uri, p, ec); + if (ec) return false; + + m_info_hash = p.info_hash; + for (std::vector::iterator i = p.trackers.begin() + , end(p.trackers.end()); i != end; ++i) + m_urls.push_back(*i); + + return true; + } + + ec = errors::torrent_missing_info; + return false; + } + if (!parse_info_section(*info, ec, flags)) return false; + + // extract the url of the tracker + lazy_entry const* i = torrent_file.dict_find_list("announce-list"); + if (i) + { + m_urls.reserve(i->list_size()); + for (int j = 0, end(i->list_size()); j < end; ++j) + { + lazy_entry const* tier = i->list_at(j); + if (tier->type() != lazy_entry::list_t) continue; + for (int k = 0, end(tier->list_size()); k < end; ++k) + { + announce_entry e(tier->list_string_value_at(k)); + e.trim(); + if (e.url.empty()) continue; + e.tier = j; + e.fail_limit = 0; + e.source = announce_entry::source_torrent; +#if TORRENT_USE_I2P + if (is_i2p_url(e.url)) m_i2p = true; +#endif + m_urls.push_back(e); + } + } + + if (!m_urls.empty()) + { + // shuffle each tier + std::vector::iterator start = m_urls.begin(); + std::vector::iterator stop; + int current_tier = m_urls.front().tier; + for (stop = m_urls.begin(); stop != m_urls.end(); ++stop) + { + if (stop->tier != current_tier) + { + std::random_shuffle(start, stop); + start = stop; + current_tier = stop->tier; + } + } + std::random_shuffle(start, stop); + } + } + + + if (m_urls.empty()) + { + announce_entry e(torrent_file.dict_find_string_value("announce")); + e.fail_limit = 0; + e.source = announce_entry::source_torrent; + e.trim(); +#if TORRENT_USE_I2P + if (is_i2p_url(e.url)) m_i2p = true; +#endif + if (!e.url.empty()) m_urls.push_back(e); + } + + lazy_entry const* nodes = torrent_file.dict_find_list("nodes"); + if (nodes) + { + for (int i = 0, end(nodes->list_size()); i < end; ++i) + { + lazy_entry const* n = nodes->list_at(i); + if (n->type() != lazy_entry::list_t + || n->list_size() < 2 + || n->list_at(0)->type() != lazy_entry::string_t + || n->list_at(1)->type() != lazy_entry::int_t) + continue; + m_nodes.push_back(std::make_pair( + n->list_at(0)->string_value() + , int(n->list_at(1)->int_value()))); + } + } + + // extract creation date + size_type cd = torrent_file.dict_find_int_value("creation date", -1); + if (cd >= 0) + { + m_creation_date = long(cd); + } + + // if there are any url-seeds, extract them + lazy_entry const* url_seeds = torrent_file.dict_find("url-list"); + if (url_seeds && url_seeds->type() == lazy_entry::string_t && url_seeds->string_length() > 0) + { + web_seed_entry ent(maybe_url_encode(url_seeds->string_value()) + , web_seed_entry::url_seed); + if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; + m_web_seeds.push_back(ent); + } + else if (url_seeds && url_seeds->type() == lazy_entry::list_t) + { + // only add a URL once + std::set unique; + for (int i = 0, end(url_seeds->list_size()); i < end; ++i) + { + lazy_entry const* url = url_seeds->list_at(i); + if (url->type() != lazy_entry::string_t) continue; + if (url->string_length() == 0) continue; + web_seed_entry ent(maybe_url_encode(url->string_value()) + , web_seed_entry::url_seed); + if (m_multifile && ent.url[ent.url.size()-1] != '/') ent.url += '/'; + if (unique.count(ent.url)) continue; + unique.insert(ent.url); + m_web_seeds.push_back(ent); + } + } + + // if there are any http-seeds, extract them + lazy_entry const* http_seeds = torrent_file.dict_find("httpseeds"); + if (http_seeds && http_seeds->type() == lazy_entry::string_t && http_seeds->string_length() > 0) + { + m_web_seeds.push_back(web_seed_entry(maybe_url_encode(http_seeds->string_value()) + , web_seed_entry::http_seed)); + } + else if (http_seeds && http_seeds->type() == lazy_entry::list_t) + { + // only add a URL once + std::set unique; + for (int i = 0, end(http_seeds->list_size()); i < end; ++i) + { + lazy_entry const* url = http_seeds->list_at(i); + if (url->type() != lazy_entry::string_t || url->string_length() == 0) continue; + std::string u = maybe_url_encode(url->string_value()); + if (unique.count(u)) continue; + unique.insert(u); + m_web_seeds.push_back(web_seed_entry(u, web_seed_entry::http_seed)); + } + } + + m_comment = torrent_file.dict_find_string_value("comment.utf-8"); + if (m_comment.empty()) m_comment = torrent_file.dict_find_string_value("comment"); + verify_encoding(m_comment); + + m_created_by = torrent_file.dict_find_string_value("created by.utf-8"); + if (m_created_by.empty()) m_created_by = torrent_file.dict_find_string_value("created by"); + verify_encoding(m_created_by); + + return true; + } + + boost::optional + torrent_info::creation_date() const + { + if (m_creation_date != 0) + { + return boost::optional(m_creation_date); + } + return boost::optional(); + } + + void torrent_info::add_tracker(std::string const& url, int tier) + { + announce_entry e(url); + e.tier = tier; + e.source = announce_entry::source_client; + m_urls.push_back(e); + + std::sort(m_urls.begin(), m_urls.end(), boost::bind(&announce_entry::tier, _1) + < boost::bind(&announce_entry::tier, _2)); + } + +#ifndef TORRENT_NO_DEPRECATE + namespace + { + struct filter_web_seed_type + { + filter_web_seed_type(web_seed_entry::type_t t_) : t(t_) {} + void operator() (web_seed_entry const& w) + { if (w.type == t) urls.push_back(w.url); } + std::vector urls; + web_seed_entry::type_t t; + }; + } + + std::vector torrent_info::url_seeds() const + { + return std::for_each(m_web_seeds.begin(), m_web_seeds.end() + , filter_web_seed_type(web_seed_entry::url_seed)).urls; + } + + std::vector torrent_info::http_seeds() const + { + return std::for_each(m_web_seeds.begin(), m_web_seeds.end() + , filter_web_seed_type(web_seed_entry::http_seed)).urls; + } + +#endif // TORRENT_NO_DEPRECATE + + void torrent_info::add_url_seed(std::string const& url + , std::string const& ext_auth + , web_seed_entry::headers_t const& ext_headers) + { + m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::url_seed + , ext_auth, ext_headers)); + } + + void torrent_info::add_http_seed(std::string const& url + , std::string const& auth + , web_seed_entry::headers_t const& extra_headers) + { + m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::http_seed + , auth, extra_headers)); + } + + +#if !defined TORRENT_NO_DEPRECATE && TORRENT_USE_IOSTREAM +// ------- start deprecation ------- + + void torrent_info::print(std::ostream& os) const + { + INVARIANT_CHECK; + + os << "trackers:\n"; + for (std::vector::const_iterator i = trackers().begin(); + i != trackers().end(); ++i) + { + os << i->tier << ": " << i->url << "\n"; + } + if (!m_comment.empty()) + os << "comment: " << m_comment << "\n"; + os << "private: " << (m_private?"yes":"no") << "\n"; + os << "number of pieces: " << num_pieces() << "\n"; + os << "piece length: " << piece_length() << "\n"; + os << "files:\n"; + for (int i = 0; i < m_files.num_files(); ++i) + os << " " << std::setw(11) << m_files.file_size(i) + << " " << m_files.file_path(i) << "\n"; + } + +// ------- end deprecation ------- +#endif + +#if TORRENT_USE_INVARIANT_CHECKS + void torrent_info::check_invariant() const + { + for (int i = 0; i < m_files.num_files(); ++i) + { + TORRENT_ASSERT(m_files.file_name_ptr(i) != 0); + if (m_files.file_name_len(i) != -1) + { + // name needs to point into the allocated info section buffer + TORRENT_ASSERT(m_files.file_name_ptr(i) >= m_info_section.get()); + TORRENT_ASSERT(m_files.file_name_ptr(i) < m_info_section.get() + m_info_section_size); + } + else + { + // name must be a valid string + TORRENT_ASSERT(strlen(m_files.file_name_ptr(i)) < 2048); + } + } + + if (m_piece_hashes != 0) + { + TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); + TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); + } + } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp b/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp new file mode 100644 index 0000000000..b556325bb5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/tracker_manager.cpp @@ -0,0 +1,357 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#include + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +using boost::tuples::make_tuple; +using boost::tuples::tuple; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + +} + +namespace libtorrent +{ + timeout_handler::timeout_handler(io_service& ios) + : m_start_time(time_now_hires()) + , m_read_time(m_start_time) + , m_timeout(ios) + , m_completion_timeout(0) + , m_read_timeout(0) + , m_abort(false) + {} + + void timeout_handler::set_timeout(int completion_timeout, int read_timeout) + { + m_completion_timeout = completion_timeout; + m_read_timeout = read_timeout; + m_start_time = m_read_time = time_now_hires(); + + TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0); + + if (m_abort) return; + + int timeout = 0; + if (m_read_timeout > 0) timeout = m_read_timeout; + if (m_completion_timeout > 0) + { + timeout = timeout == 0 + ? m_completion_timeout + : (std::min)(m_completion_timeout, timeout); + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("timeout_handler::timeout_callback"); +#endif + error_code ec; + m_timeout.expires_at(m_read_time + seconds(timeout), ec); + m_timeout.async_wait(boost::bind( + &timeout_handler::timeout_callback, self(), _1)); + } + + void timeout_handler::restart_read_timeout() + { + m_read_time = time_now_hires(); + } + + void timeout_handler::cancel() + { + m_abort = true; + m_completion_timeout = 0; + error_code ec; + m_timeout.cancel(ec); + } + + void timeout_handler::timeout_callback(error_code const& error) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("timeout_handler::timeout_callback"); +#endif + if (m_abort) return; + + ptime now = time_now_hires(); + time_duration receive_timeout = now - m_read_time; + time_duration completion_timeout = now - m_start_time; + + if ((m_read_timeout + && m_read_timeout <= total_seconds(receive_timeout)) + || (m_completion_timeout + && m_completion_timeout <= total_seconds(completion_timeout)) + || error) + { + on_timeout(error); + return; + } + + int timeout = 0; + if (m_read_timeout > 0) timeout = m_read_timeout; + if (m_completion_timeout > 0) + { + timeout = timeout == 0 + ? int(m_completion_timeout - total_seconds(m_read_time - m_start_time)) + : (std::min)(int(m_completion_timeout - total_seconds(m_read_time - m_start_time)), timeout); + } +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("timeout_handler::timeout_callback"); +#endif + error_code ec; + m_timeout.expires_at(m_read_time + seconds(timeout), ec); + m_timeout.async_wait( + boost::bind(&timeout_handler::timeout_callback, self(), _1)); + } + + tracker_connection::tracker_connection( + tracker_manager& man + , tracker_request const& req + , io_service& ios + , boost::weak_ptr r) + : timeout_handler(ios) + , m_requester(r) + , m_man(man) + , m_req(req) + {} + + boost::shared_ptr tracker_connection::requester() const + { + return m_requester.lock(); + } + + void tracker_connection::fail(error_code const& ec, int code + , char const* msg, int interval, int min_interval) + { + // we need to post the error to avoid deadlock + get_io_service().post(boost::bind(&tracker_connection::fail_impl + , self(), ec, code, std::string(msg), interval, min_interval)); + } + + void tracker_connection::fail_impl(error_code const& ec, int code + , std::string msg, int interval, int min_interval) + { + boost::shared_ptr cb = requester(); + if (cb) cb->tracker_request_error(m_req, code, ec, msg.c_str() + , interval == 0 ? min_interval : interval); + close(); + } + + void tracker_connection::sent_bytes(int bytes) + { + m_man.sent_bytes(bytes); + } + + void tracker_connection::received_bytes(int bytes) + { + m_man.received_bytes(bytes); + } + + void tracker_connection::close() + { + cancel(); + m_man.remove_request(this); + } + + tracker_manager::~tracker_manager() + { + TORRENT_ASSERT(m_abort); + abort_all_requests(true); + } + + void tracker_manager::sent_bytes(int bytes) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_ses.m_stat.sent_tracker_bytes(bytes); + } + + void tracker_manager::received_bytes(int bytes) + { + TORRENT_ASSERT(m_ses.is_network_thread()); + m_ses.m_stat.received_tracker_bytes(bytes); + } + + void tracker_manager::remove_request(tracker_connection const* c) + { + mutex_t::scoped_lock l(m_mutex); + + tracker_connections_t::iterator i = std::find(m_connections.begin() + , m_connections.end(), boost::intrusive_ptr(c)); + if (i == m_connections.end()) return; + + m_connections.erase(i); + } + + void tracker_manager::queue_request( + io_service& ios + , connection_queue& cc + , tracker_request req + , std::string const& auth + , boost::weak_ptr c) + { + mutex_t::scoped_lock l(m_mutex); + TORRENT_ASSERT(req.num_want >= 0); + TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) return; + if (req.event == tracker_request::stopped) + req.num_want = 0; + + TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) + return; + + std::string protocol = req.url.substr(0, req.url.find(':')); + + boost::intrusive_ptr con; + +#ifdef TORRENT_USE_OPENSSL + if (protocol == "http" || protocol == "https") +#else + if (protocol == "http") +#endif + { + con = new http_tracker_connection( + ios, cc, *this, req, c + , m_ses, m_proxy, auth +#if TORRENT_USE_I2P + , &m_ses.m_i2p_conn +#endif + ); + } + else if (protocol == "udp") + { + con = new udp_tracker_connection( + ios, cc, *this, req , c, m_ses + , m_proxy); + } + else + { + // we need to post the error to avoid deadlock + if (boost::shared_ptr r = c.lock()) + ios.post(boost::bind(&request_callback::tracker_request_error, r, req + , -1, error_code(errors::unsupported_url_protocol) + , "", 0)); + return; + } + + m_connections.push_back(con); + + boost::shared_ptr cb = con->requester(); + if (cb) cb->m_manager = this; + con->start(); + } + + bool tracker_manager::incoming_packet(error_code const& e + , udp::endpoint const& ep, char const* buf, int size) + { + // m_ses.m_stat.received_tracker_bytes(len + 28); + for (tracker_connections_t::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + ++i; + // on_receive() may remove the tracker connection from the list + if (p->on_receive(e, ep, buf, size)) return true; + } + return false; + } + + bool tracker_manager::incoming_packet(error_code const& e + , char const* hostname, char const* buf, int size) + { + // m_ses.m_stat.received_tracker_bytes(len + 28); + for (tracker_connections_t::iterator i = m_connections.begin(); + i != m_connections.end();) + { + boost::intrusive_ptr p = *i; + ++i; + // on_receive() may remove the tracker connection from the list + if (p->on_receive_hostname(e, hostname, buf, size)) return true; + } + return false; + } + + void tracker_manager::abort_all_requests(bool all) + { + // removes all connections from m_connections + // except 'event=stopped'-requests + mutex_t::scoped_lock l(m_mutex); + + m_abort = true; + tracker_connections_t close_connections; + + for (tracker_connections_t::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + intrusive_ptr c = *i; + tracker_request const& req = c->tracker_req(); + if (req.event == tracker_request::stopped && !all) + continue; + + close_connections.push_back(c); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr rc = c->requester(); + if (rc) rc->debug_log("aborting: %s", req.url.c_str()); +#endif + } + l.unlock(); + + for (tracker_connections_t::iterator i = close_connections.begin() + , end(close_connections.end()); i != end; ++i) + { + (*i)->close(); + } + } + + bool tracker_manager::empty() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.empty(); + } + + int tracker_manager::num_requests() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.size(); + } +} diff --git a/apps/Launcher/ext/libtorrent/src/udp_socket.cpp b/apps/Launcher/ext/libtorrent/src/udp_socket.cpp new file mode 100644 index 0000000000..a76efb8f60 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/udp_socket.cpp @@ -0,0 +1,1485 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/assert.hpp" // for print_backtrace +#include "libtorrent/socket.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/string_util.hpp" // for allocate_string_copy +#include "libtorrent/broadcast_socket.hpp" // for is_any +#include +#include +#include +#include +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +using namespace libtorrent; + +udp_socket::udp_socket(asio::io_service& ios + , connection_queue& cc) + : m_observers_locked(false) + , m_ipv4_sock(ios) + , m_buf_size(0) + , m_new_buf_size(0) + , m_buf(0) +#if TORRENT_USE_IPV6 + , m_ipv6_sock(ios) +#endif + , m_bind_port(0) + , m_v4_outstanding(0) +#if TORRENT_USE_IPV6 + , m_v6_outstanding(0) +#endif + , m_socks5_sock(ios) + , m_connection_ticket(-1) + , m_cc(cc) + , m_resolver(ios) + , m_queue_packets(false) + , m_tunnel_packets(false) + , m_force_proxy(false) + , m_abort(false) + , m_outstanding_ops(0) +#if TORRENT_USE_IPV6 + , m_v6_write_subscribed(false) +#endif + , m_v4_write_subscribed(false) +{ +#if TORRENT_USE_ASSERTS + m_magic = 0x1337; + m_started = false; + m_outstanding_when_aborted = -1; + m_outstanding_connect_queue = 0; + m_outstanding_connect = 0; + m_outstanding_timeout = 0; + m_outstanding_resolve = 0; + m_outstanding_socks = 0; +#if defined BOOST_HAS_PTHREADS + m_thread = 0; +#endif +#endif + + m_buf_size = 2048; + m_new_buf_size = m_buf_size; + m_buf = (char*)malloc(m_buf_size); +} + +udp_socket::~udp_socket() +{ + free(m_buf); +#if TORRENT_USE_IPV6 + TORRENT_ASSERT_VAL(m_v6_outstanding == 0, m_v6_outstanding); +#endif + TORRENT_ASSERT_VAL(m_v4_outstanding == 0, m_v4_outstanding); + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(m_observers_locked == false); +#if TORRENT_USE_ASSERTS + m_magic = 0; +#endif + TORRENT_ASSERT(m_outstanding_ops == 0); +} + +#if TORRENT_USE_ASSERTS + #define CHECK_MAGIC check_magic_ cm_(m_magic) + struct check_magic_ + { + check_magic_(int& m_): m(m_) { TORRENT_ASSERT(m == 0x1337); } + ~check_magic_() { TORRENT_ASSERT(m == 0x1337); } + int& m; + }; +#else + #define CHECK_MAGIC do {} while (false) +#endif + +void udp_socket::send_hostname(char const* hostname, int port + , char const* p, int len, error_code& ec, int flags) +{ + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + + // if the sockets are closed, the udp_socket is closing too + if (!is_open()) + { + ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); + return; + } + + if (m_tunnel_packets) + { + // send udp packets through SOCKS5 server + wrap(hostname, port, p, len, ec); + return; + } + + // this function is only supported when we're using a proxy + if (!m_queue_packets && !m_force_proxy) + { + address target = address::from_string(hostname, ec); + if (!ec) send(udp::endpoint(target, port), p, len, ec, 0); + return; + } + + if (m_queue.size() > 1000 || (flags & dont_queue)) return; + + m_queue.push_back(queued_packet()); + queued_packet& qp = m_queue.back(); + qp.ep.port(port); + + address target = address::from_string(hostname, ec); + if (ec) qp.ep.address(target); + else qp.hostname = allocate_string_copy(hostname); + qp.buf.insert(qp.buf.begin(), p, p + len); + qp.flags = 0; +} + +void udp_socket::send(udp::endpoint const& ep, char const* p, int len + , error_code& ec, int flags) +{ + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + + // if the sockets are closed, the udp_socket is closing too + if (!is_open()) + { + ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); + return; + } + + if (!(flags & peer_connection) || m_proxy_settings.proxy_peer_connections) + { + if (m_tunnel_packets) + { + // send udp packets through SOCKS5 server + wrap(ep, p, len, ec); + return; + } + + if (m_queue_packets) + { + if (m_queue.size() > 1000 || (flags & dont_queue)) return; + + m_queue.push_back(queued_packet()); + queued_packet& qp = m_queue.back(); + qp.ep = ep; + qp.hostname = 0; + qp.flags = flags; + qp.buf.insert(qp.buf.begin(), p, p + len); + return; + } + } + + if (m_force_proxy) return; + +#if TORRENT_USE_IPV6 + if (ep.address().is_v6() && m_ipv6_sock.is_open()) + m_ipv6_sock.send_to(asio::buffer(p, len), ep, 0, ec); + else +#endif + m_ipv4_sock.send_to(asio::buffer(p, len), ep, 0, ec); + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_USE_IPV6 + if (ep.address().is_v6() && m_ipv6_sock.is_open()) + { + if (!m_v6_write_subscribed) + { + m_ipv6_sock.async_send(asio::null_buffers() + , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv6_sock)); + m_v6_write_subscribed = true; + } + } + else +#endif + { + if (!m_v4_write_subscribed) + { + m_ipv4_sock.async_send(asio::null_buffers() + , boost::bind(&udp_socket::on_writable, this, _1, &m_ipv4_sock)); + m_v4_write_subscribed = true; + } + } + } +} + +void udp_socket::on_writable(error_code const& ec, udp::socket* s) +{ +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + m_v6_write_subscribed = false; + else +#endif + m_v4_write_subscribed = false; + + call_writable_handler(); +} + +// called whenever the socket is readable +void udp_socket::on_read(error_code const& ec, udp::socket* s) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_read"); +#endif + + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_single_thread()); + +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + { + TORRENT_ASSERT(m_v6_outstanding > 0); + --m_v6_outstanding; + } + else +#endif + { + TORRENT_ASSERT(m_v4_outstanding > 0); + --m_v4_outstanding; + } + + if (ec == asio::error::operation_aborted) return; + if (m_abort) return; + + CHECK_MAGIC; + + for (;;) + { + error_code ec; + udp::endpoint ep; + size_t bytes_transferred = s->receive_from(asio::buffer(m_buf, m_buf_size), ep, 0, ec); + + // TODO: it would be nice to detect this on posix systems also +#ifdef TORRENT_WINDOWS + if ((ec == error_code(ERROR_MORE_DATA, get_system_category()) + || ec == error_code(WSAEMSGSIZE, get_system_category())) + && m_buf_size < 65536) + { + // if this function fails to allocate memory, m_buf_size + // is set to 0. In that case, don't issue the async_read(). + set_buf_size(m_buf_size * 2); + if (m_buf_size == 0) return; + continue; + } +#endif + + if (ec == asio::error::would_block || ec == asio::error::try_again) break; + on_read_impl(s, ep, ec, bytes_transferred); + } + call_drained_handler(); + setup_read(s); +} + +void udp_socket::call_handler(error_code const& ec, udp::endpoint const& ep, char const* buf, int size) +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + bool ret = false; + TORRENT_TRY { + ret = (*i)->incoming_packet(ec, ep, buf, size); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + if (ret) break; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_handler(error_code const& ec, const char* host, char const* buf, int size) +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + bool ret = false; + TORRENT_TRY { + ret = (*i)->incoming_packet(ec, host, buf, size); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + if (ret) break; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_drained_handler() +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + TORRENT_TRY { + (*i)->socket_drained(); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::call_writable_handler() +{ + m_observers_locked = true; + for (std::vector::iterator i = m_observers.begin(); + i != m_observers.end();) + { + TORRENT_TRY { + (*i)->writable(); + } TORRENT_CATCH (std::exception&) {} + if (*i == NULL) i = m_observers.erase(i); + else ++i; + } + if (!m_added_observers.empty()) + { + m_observers.insert(m_observers.end(), m_added_observers.begin(), m_added_observers.end()); + m_added_observers.clear(); + } + m_observers_locked = false; + if (m_new_buf_size != m_buf_size) + set_buf_size(m_new_buf_size); +} + +void udp_socket::subscribe(udp_socket_observer* o) +{ + TORRENT_ASSERT(std::find(m_observers.begin(), m_observers.end(), o) == m_observers.end()); + if (m_observers_locked) + m_added_observers.push_back(o); + else + m_observers.push_back(o); +} + +void udp_socket::unsubscribe(udp_socket_observer* o) +{ + std::vector::iterator i = std::find(m_observers.begin(), m_observers.end(), o); + if (i == m_observers.end()) return; + if (m_observers_locked) + *i = NULL; + else + m_observers.erase(i); +} + +void udp_socket::on_read_impl(udp::socket* s, udp::endpoint const& ep + , error_code const& e, std::size_t bytes_transferred) +{ + TORRENT_ASSERT(m_magic == 0x1337); + TORRENT_ASSERT(is_single_thread()); + + if (e) + { + call_handler(e, ep, 0, 0); + + // don't stop listening on recoverable errors + if (e != asio::error::host_unreachable + && e != asio::error::fault + && e != asio::error::connection_reset + && e != asio::error::connection_refused + && e != asio::error::connection_aborted + && e != asio::error::operation_aborted + && e != asio::error::network_reset + && e != asio::error::network_unreachable +#ifdef WIN32 + // ERROR_MORE_DATA means the same thing as EMSGSIZE + && e != error_code(ERROR_MORE_DATA, get_system_category()) + && e != error_code(ERROR_HOST_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_PORT_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_RETRY, get_system_category()) + && e != error_code(ERROR_NETWORK_UNREACHABLE, get_system_category()) + && e != error_code(ERROR_CONNECTION_REFUSED, get_system_category()) + && e != error_code(ERROR_CONNECTION_ABORTED, get_system_category()) +#endif + && e != asio::error::message_size) + { + return; + } + + if (m_abort) return; + + return; + } + + TORRENT_TRY { + + if (m_tunnel_packets) + { + // if the source IP doesn't match the proxy's, ignore the packet + if (ep == m_udp_proxy_addr) + unwrap(e, m_buf, bytes_transferred); + } + else if (!m_force_proxy) // block incoming packets that aren't coming via the proxy + { + call_handler(e, ep, m_buf, bytes_transferred); + } + + } TORRENT_CATCH (std::exception&) {} +} + +void udp_socket::setup_read(udp::socket* s) +{ + if (m_abort) return; + +#if TORRENT_USE_IPV6 + if (s == &m_ipv6_sock) + ++m_v6_outstanding; + else +#endif + ++m_v4_outstanding; + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_read"); +#endif + + udp::endpoint ep; + TORRENT_TRY + { + s->async_receive_from(asio::null_buffers() + , ep, boost::bind(&udp_socket::on_read, this, _1, s)); + } + TORRENT_CATCH(boost::system::system_error& e) + { +#ifdef BOOST_NO_EXCEPTIONS + // dummy + error_code ec; + boost::system::system_error e(ec); +#endif + get_io_service().post(boost::bind(&udp_socket::on_read + , this, e.code(), s)); + } +} + +void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + char header[25]; + char* h = header; + + write_uint16(0, h); // reserved + write_uint8(0, h); // fragment + write_uint8(ep.address().is_v4()?1:4, h); // atyp + write_endpoint(ep, h); + + boost::array iovec; + iovec[0] = asio::const_buffer(header, h - header); + iovec[1] = asio::const_buffer(p, len); + +#if TORRENT_USE_IPV6 + if (m_udp_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) +#endif + m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#if TORRENT_USE_IPV6 + else + m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#endif +} + +void udp_socket::wrap(char const* hostname, int port, char const* p, int len, error_code& ec) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + char header[270]; + char* h = header; + + write_uint16(0, h); // reserved + write_uint8(0, h); // fragment + write_uint8(3, h); // atyp + int hostlen = (std::min)(strlen(hostname), size_t(255)); + write_uint8(hostlen, h); // hostname len + memcpy(h, hostname, hostlen); + h += hostlen; + write_uint16(port, h); + + boost::array iovec; + iovec[0] = asio::const_buffer(header, h - header); + iovec[1] = asio::const_buffer(p, len); + +#if TORRENT_USE_IPV6 + if (m_udp_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) +#endif + m_ipv4_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#if TORRENT_USE_IPV6 + else + m_ipv6_sock.send_to(iovec, m_udp_proxy_addr, 0, ec); +#endif +} + +// unwrap the UDP packet from the SOCKS5 header +void udp_socket::unwrap(error_code const& e, char const* buf, int size) +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + // the minimum socks5 header size + if (size <= 10) return; + + char const* p = buf; + p += 2; // reserved + int frag = read_uint8(p); + // fragmentation is not supported + if (frag != 0) return; + + udp::endpoint sender; + + int atyp = read_uint8(p); + if (atyp == 1) + { + // IPv4 + sender = read_v4_endpoint(p); + } +#if TORRENT_USE_IPV6 + else if (atyp == 4) + { + // IPv6 + sender = read_v6_endpoint(p); + } +#endif + else + { + int len = read_uint8(p); + if (len > (buf + size) - p) return; + std::string hostname(p, p + len); + p += len; + call_handler(e, hostname.c_str(), p, size - (p - buf)); + return; + } + + call_handler(e, sender, p, size - (p - buf)); +} + +#if !defined BOOST_ASIO_ENABLE_CANCELIO && defined TORRENT_WINDOWS +#error BOOST_ASIO_ENABLE_CANCELIO needs to be defined when building libtorrent to enable cancel() in asio on windows +#endif + +void udp_socket::close() +{ + TORRENT_ASSERT(is_single_thread()); + TORRENT_ASSERT(m_magic == 0x1337); + + error_code ec; + // if we close the socket here, we can't shut down + // utp connections or NAT-PMP. We need to cancel the + // outstanding operations + m_ipv4_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_ipv4_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_ipv6_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); +#endif + m_socks5_sock.cancel(ec); + if (ec == error::operation_not_supported) + m_socks5_sock.close(ec); + TORRENT_ASSERT_VAL(!ec || ec == error::bad_descriptor, ec); + m_resolver.cancel(); + m_abort = true; + +#if TORRENT_USE_ASSERTS + m_outstanding_when_aborted = num_outstanding(); +#endif + + if (m_connection_ticket >= 0) + { + if (m_cc.done(m_connection_ticket)) + m_connection_ticket = -1; + + // we just called done, which means on_timeout + // won't be called. Decrement the outstanding + // ops counter for that +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + } + +} + +void udp_socket::set_buf_size(int s) +{ + TORRENT_ASSERT(is_single_thread()); + + if (m_observers_locked) + { + // we can't actually reallocate the buffer while + // it's being used by the observers, we have to + // do that once we're done iterating over them + m_new_buf_size = s; + return; + } + + if (s == m_buf_size) return; + + bool no_mem = false; + void* tmp = realloc(m_buf, s); + if (tmp != 0) + { + m_buf = (char*)tmp; + m_buf_size = s; + m_new_buf_size = s; + } + else + { + no_mem = true; + } + + if (no_mem) + { + free(m_buf); + m_buf = 0; + m_buf_size = 0; + m_new_buf_size = 0; + udp::endpoint ep; + call_handler(error::no_memory, ep, 0, 0); + close(); + } + + int size = m_buf_size; + + // don't shrink the size of the receive buffer + error_code ec; + boost::asio::socket_base::receive_buffer_size recv_size; + m_ipv4_sock.get_option(recv_size, ec); + if (!ec) size = (std::max)(recv_size.value(), size); +#if TORRENT_USE_IPV6 + m_ipv6_sock.get_option(recv_size, ec); + if (!ec) size = (std::max)(recv_size.value(), size); +#endif + + error_code ignore_errors; + // set the internal buffer sizes as well + m_ipv4_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) + , ignore_errors); +#if TORRENT_USE_IPV6 + m_ipv6_sock.set_option(boost::asio::socket_base::receive_buffer_size(size) + , ignore_errors); +#endif +} + +void udp_socket::bind(udp::endpoint const& ep, error_code& ec) +{ + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + TORRENT_ASSERT(m_abort == false); + if (m_abort) + { + ec = boost::asio::error::operation_aborted; + return; + } + + if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec); +#if TORRENT_USE_IPV6 + if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec); +#endif + + if (ep.address().is_v4()) + { + m_ipv4_sock.open(udp::v4(), ec); + if (ec) return; + m_ipv4_sock.bind(ep, ec); + if (ec) return; + udp::socket::non_blocking_io ioc(true); + m_ipv4_sock.io_control(ioc, ec); + if (ec) return; + setup_read(&m_ipv4_sock); + } + +#if TORRENT_USE_IPV6 + if (supports_ipv6() && (ep.address().is_v6() || is_any(ep.address()))) + { + udp::endpoint ep6 = ep; + if (is_any(ep.address())) ep6.address(address_v6::any()); + m_ipv6_sock.open(udp::v6(), ec); + if (ec) return; +#ifdef IPV6_V6ONLY + m_ipv6_sock.set_option(v6only(true), ec); + ec.clear(); +#endif + m_ipv6_sock.bind(ep6, ec); + if (ec) return; + udp::socket::non_blocking_io ioc(true); + m_ipv6_sock.io_control(ioc, ec); + if (ec) return; + setup_read(&m_ipv6_sock); + } +#endif +#if TORRENT_USE_ASSERTS + m_started = true; +#endif + m_bind_port = ep.port(); +} + +void udp_socket::set_proxy_settings(proxy_settings const& ps) +{ + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + error_code ec; + m_socks5_sock.close(ec); + m_tunnel_packets = false; + + m_proxy_settings = ps; + + if (m_abort) return; + + if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw) + { + m_queue_packets = true; + // connect to socks5 server and open up the UDP tunnel + tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_resolve; +#endif +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_name_lookup"); +#endif + m_resolver.async_resolve(q, boost::bind( + &udp_socket::on_name_lookup, this, _1, _2)); + } +} + +void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_name_lookup"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_resolve > 0); + --m_outstanding_resolve; +#endif + + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (m_abort) return; + CHECK_MAGIC; + + if (e == asio::error::operation_aborted) return; + + TORRENT_ASSERT(is_single_thread()); + + if (e) + { + if (m_force_proxy) + { + call_handler(e, udp::endpoint(), 0, 0); + } + else + { + // if we can't connect to the proxy, and + // we're not in privacy mode, try to just + // not use a proxy + m_proxy_settings = proxy_settings(); + m_tunnel_packets = false; + } + + drain_queue(); + return; + } + + m_proxy_addr.address(i->endpoint().address()); + m_proxy_addr.port(i->endpoint().port()); + // on_connect may be called from within this thread + // the semantics for on_connect and on_timeout is + // a bit complicated. See comments in connection_queue.hpp + // for more details. This semantic determines how and + // when m_outstanding_ops may be decremented + // To simplyfy this, it's probably a good idea to + // merge on_connect and on_timeout to a single function + + // on_timeout may be called before on_connected + // so increment the outstanding ops + // it may also not be called in case we call + // connection_queue::done first, so be sure to + // decrement if that happens + m_outstanding_ops += 2; + +#if TORRENT_USE_ASSERTS + ++m_outstanding_timeout; + ++m_outstanding_connect_queue; +#endif + m_cc.enqueue(boost::bind(&udp_socket::on_connect, this, _1) + , boost::bind(&udp_socket::on_timeout, this), seconds(10)); +} + +void udp_socket::on_timeout() +{ +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + m_queue_packets = false; + + if (m_abort) return; + + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + error_code ec; + m_socks5_sock.close(ec); + TORRENT_ASSERT(m_cc.done(m_connection_ticket) == false); + m_connection_ticket = -1; +} + +void udp_socket::on_connect(int ticket) +{ + TORRENT_ASSERT(is_single_thread()); +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_connect_queue > 0); + --m_outstanding_connect_queue; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + CHECK_MAGIC; + + if (ticket == -1) + { +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + close(); + return; + } + + if (m_abort) return; + if (is_closed()) return; + + if (m_connection_ticket != -1) + { + // there's already an outstanding connect. Cancel it. + m_socks5_sock.close(); + m_connection_ticket = -1; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_connected"); +#endif + m_connection_ticket = ticket; + + error_code ec; + m_socks5_sock.open(m_proxy_addr.address().is_v4()?tcp::v4():tcp::v6(), ec); + + // enable keepalives + m_socks5_sock.set_option(boost::asio::socket_base::keep_alive(true), ec); + + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_connect; +#endif + m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port()) + , boost::bind(&udp_socket::on_connected, this, _1, ticket)); +} + +void udp_socket::on_connected(error_code const& e, int ticket) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_connected"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_connect > 0); + --m_outstanding_connect; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + CHECK_MAGIC; + + TORRENT_ASSERT(is_single_thread()); + if (m_connection_ticket == -1 + && e == asio::error::operation_aborted) + { + // this socket has already been aborted and closed. + return; + } + + if (m_cc.done(ticket)) + { + // if the tickets mismatch, another connection attempt + // was initiated while waiting for this one to complete. + if (ticket == m_connection_ticket) + m_connection_ticket = -1; + } + + // we just called done, which means on_timeout + // won't be called. Decrement the outstanding + // ops counter for that +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_timeout > 0); + --m_outstanding_timeout; + print_backtrace(timeout_stack, sizeof(timeout_stack)); +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (e == asio::error::operation_aborted) return; + + // if ticket != m_connection_ticket, it means m_connection_ticket + // will not have been reset, and it means we are still waiting + // for a connection attempt. + if (m_connection_ticket != -1) return; + + if (m_abort) return; + + if (e) + { + call_handler(e, udp::endpoint(), 0, 0); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 authentication methods + char* p = &m_tmp_buf[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_proxy_settings.username.empty() + || m_proxy_settings.type == proxy_settings::socks5) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake1"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::handshake1, this, _1)); +} + +void udp_socket::handshake1(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake1"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake2"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2) + , boost::bind(&udp_socket::handshake2, this, _1)); +} + +void udp_socket::handshake2(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake2"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + + if (e) + { + drain_queue(); + return; + } + + using namespace libtorrent::detail; + + TORRENT_ASSERT(is_single_thread()); + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < 5) + { + error_code ec; + m_socks5_sock.close(ec); + drain_queue(); + return; + } + + if (method == 0) + { + socks_forward_udp(/*l*/); + } + else if (method == 2) + { + if (m_proxy_settings.username.empty()) + { + error_code ec; + m_socks5_sock.close(ec); + drain_queue(); + return; + } + + // start sub-negotiation + char* p = &m_tmp_buf[0]; + write_uint8(1, p); + write_uint8(m_proxy_settings.username.size(), p); + write_string(m_proxy_settings.username, p); + write_uint8(m_proxy_settings.password.size(), p); + write_string(m_proxy_settings.password, p); + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake3"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::handshake3, this, _1)); + } + else + { + drain_queue(); + error_code ec; + m_socks5_sock.close(ec); + return; + } +} + +void udp_socket::handshake3(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake3"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::on_handshake4"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 2) + , boost::bind(&udp_socket::handshake4, this, _1)); +} + +void udp_socket::handshake4(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::on_handshake4"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + + using namespace libtorrent::detail; + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1 || status != 0) + { + drain_queue(); + return; + } + + socks_forward_udp(/*l*/); +} + +void udp_socket::socks_forward_udp() +{ + CHECK_MAGIC; + using namespace libtorrent::detail; + + // send SOCKS5 UDP command + char* p = &m_tmp_buf[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(3, p); // UDP ASSOCIATE command + write_uint8(0, p); // reserved + error_code ec; + write_uint8(1, p); // ATYP = IPv4 + write_uint32(0, p); // 0.0.0.0 + write_uint16(0, p); // :0 + TORRENT_ASSERT_VAL(p - m_tmp_buf < int(sizeof(m_tmp_buf)), (p - m_tmp_buf)); +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::connect1"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) + , boost::bind(&udp_socket::connect1, this, _1)); +} + +void udp_socket::connect1(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::connect1"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::connect2"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 10) + , boost::bind(&udp_socket::connect2, this, _1)); +} + +void udp_socket::connect2(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::connect2"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + + if (m_abort) + { + m_queue.clear(); + return; + } + CHECK_MAGIC; + if (e) + { + drain_queue(); + return; + } + + TORRENT_ASSERT(is_single_thread()); + + using namespace libtorrent::detail; + + char* p = &m_tmp_buf[0]; + int version = read_uint8(p); // VERSION + int status = read_uint8(p); // STATUS + ++p; // RESERVED + int atyp = read_uint8(p); // address type + + if (version != 5 || status != 0) + { + drain_queue(); + return; + } + + if (atyp == 1) + { + m_udp_proxy_addr.address(address_v4(read_uint32(p))); + m_udp_proxy_addr.port(read_uint16(p)); + } + else + { + // in this case we need to read more data from the socket + TORRENT_ASSERT(false && "not implemented yet!"); + drain_queue(); + return; + } + + m_tunnel_packets = true; + drain_queue(); + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_socket::hung_up"); +#endif + ++m_outstanding_ops; +#if TORRENT_USE_ASSERTS + ++m_outstanding_socks; +#endif + asio::async_read(m_socks5_sock, asio::buffer(m_tmp_buf, 10) + , boost::bind(&udp_socket::hung_up, this, _1)); +} + +void udp_socket::hung_up(error_code const& e) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_socket::hung_up"); +#endif +#if TORRENT_USE_ASSERTS + TORRENT_ASSERT(m_outstanding_socks > 0); + --m_outstanding_socks; +#endif + TORRENT_ASSERT(m_outstanding_ops > 0); + --m_outstanding_ops; + TORRENT_ASSERT(m_outstanding_ops == m_outstanding_connect + + m_outstanding_timeout + + m_outstanding_resolve + + m_outstanding_connect_queue + + m_outstanding_socks); + if (m_abort) return; + CHECK_MAGIC; + TORRENT_ASSERT(is_single_thread()); + + if (e == asio::error::operation_aborted || m_abort) return; + + // the socks connection was closed, re-open it + set_proxy_settings(m_proxy_settings); +} + +void udp_socket::drain_queue() +{ + m_queue_packets = false; + + // forward all packets that were put in the queue + while (!m_queue.empty()) + { + queued_packet const& p = m_queue.front(); + error_code ec; + if (p.hostname) + { + udp_socket::send_hostname(p.hostname, p.ep.port(), &p.buf[0] + , p.buf.size(), ec, p.flags | dont_queue); + free(p.hostname); + } + else + { + udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec, p.flags | dont_queue); + } + m_queue.pop_front(); + } +} + +rate_limited_udp_socket::rate_limited_udp_socket(io_service& ios + , connection_queue& cc) + : udp_socket(ios, cc) + , m_rate_limit(8000) + , m_quota(8000) + , m_last_tick(time_now()) +{ +} + +bool rate_limited_udp_socket::send(udp::endpoint const& ep, char const* p + , int len, error_code& ec, int flags) +{ + ptime now = time_now_hires(); + time_duration delta = now - m_last_tick; + m_last_tick = now; + + // add any new quota we've accrued since last time + m_quota += boost::uint64_t(m_rate_limit) * total_microseconds(delta) / 1000000; + + // allow 3 seconds worth of burst + if (m_quota > 3 * m_rate_limit) m_quota = 3 * m_rate_limit; + + // if there's no quota, and it's OK to drop, just + // drop the packet + if (m_quota < len && (flags & dont_drop) == 0) return false; + + m_quota -= len; + if (m_quota < 0) m_quota = 0; + udp_socket::send(ep, p, len, ec, flags); + return true; +} + diff --git a/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp b/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp new file mode 100644 index 0000000000..07230f11f5 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/udp_tracker_connection.cpp @@ -0,0 +1,724 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_any +#include "libtorrent/random.hpp" + +namespace libtorrent +{ + + std::map + udp_tracker_connection::m_connection_cache; + + mutex udp_tracker_connection::m_cache_mutex; + + udp_tracker_connection::udp_tracker_connection( + io_service& ios + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , boost::weak_ptr c + , aux::session_impl& ses + , proxy_settings const& proxy) + : tracker_connection(man, req, ios, c) + , m_abort(false) + , m_transaction_id(0) + , m_ses(ses) + , m_attempts(0) + , m_state(action_error) + , m_proxy(proxy) + { + } + + void udp_tracker_connection::start() + { + std::string hostname; + std::string protocol; + int port; + error_code ec; + + using boost::tuples::ignore; + boost::tie(protocol, ignore, hostname, port, ignore) + = parse_url_components(tracker_req().url, ec); + if (port == -1) port = protocol == "http" ? 80 : 443; + + if (ec) + { + tracker_connection::fail(ec); + return; + } + + session_settings const& settings = m_ses.settings(); + + if (m_proxy.proxy_hostnames + && (m_proxy.type == proxy_settings::socks5 + || m_proxy.type == proxy_settings::socks5_pw)) + { + m_hostname = hostname; + m_target.port(port); + start_announce(); + } + else + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("udp_tracker_connection::name_lookup"); +#endif + tcp::resolver::query q(hostname, to_string(port).elems); + m_ses.m_host_resolver.async_resolve(q + , boost::bind( + &udp_tracker_connection::name_lookup, self(), _1, _2)); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ initiating name lookup: \"%s\" ]" + , hostname.c_str()); +#endif + } + + set_timeout(tracker_req().event == tracker_request::stopped + ? settings.stop_tracker_timeout + : settings.tracker_completion_timeout + , settings.tracker_receive_timeout); + } + + void udp_tracker_connection::fail(error_code const& ec, int code + , char const* msg, int interval, int min_interval) + { + // m_target failed. remove it from the endpoint list + std::list::iterator i = std::find(m_endpoints.begin() + , m_endpoints.end(), tcp::endpoint(m_target.address(), m_target.port())); + + if (i != m_endpoints.end()) m_endpoints.erase(i); + + // if that was the last one, fail the whole announce + if (m_endpoints.empty()) + { + tracker_connection::fail(ec, code, msg, interval, min_interval); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ host: \"%s\" ip: \"%s\" | error: \"%s\" ]" + , m_hostname.c_str(), print_endpoint(m_target).c_str(), ec.message().c_str()); +#endif + + // pick another target endpoint and try again + m_target = pick_target_endpoint(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER trying next IP [ host: \"%s\" ip: \"%s\" ]" + , m_hostname.c_str(), print_endpoint(m_target).c_str()); +#endif + m_ses.m_io_service.post(boost::bind( + &udp_tracker_connection::start_announce, self())); + + session_settings const& settings = m_ses.settings(); + set_timeout(tracker_req().event == tracker_request::stopped + ? settings.stop_tracker_timeout + : settings.tracker_completion_timeout + , settings.tracker_receive_timeout); + } + + void udp_tracker_connection::name_lookup(error_code const& error + , tcp::resolver::iterator i) + { +#if defined TORRENT_ASIO_DEBUGGING + complete_async("udp_tracker_connection::name_lookup"); +#endif + if (m_abort) return; + if (error == asio::error::operation_aborted) return; + if (error || i == tcp::resolver::iterator()) + { + fail(error); + return; + } + + boost::shared_ptr cb = requester(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER [ name lookup successful ]"); +#endif + if (cancelled()) + { + fail(error_code(errors::torrent_aborted)); + return; + } + + restart_read_timeout(); + + // look for an address that has the same kind as the one + // we're listening on. To make sure the tracker get our + // correct listening address. + + std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1)); + + if (tracker_req().apply_ip_filter) + { + // remove endpoints that are filtered by the IP filter + for (std::list::iterator k = m_endpoints.begin(); + k != m_endpoints.end();) + { + if (m_ses.m_ip_filter.access(k->address()) == ip_filter::blocked) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) cb->debug_log("*** UDP_TRACKER [ IP blocked by filter: %s ]" + , print_address(k->address()).c_str()); +#endif + k = m_endpoints.erase(k); + } + else + ++k; + } + } + + // if all endpoints were filtered by the IP filter, we can't connect + if (m_endpoints.empty()) + { + fail(error_code(errors::banned_by_ip_filter)); + return; + } + + m_target = pick_target_endpoint(); + + if (cb) cb->m_tracker_address = tcp::endpoint(m_target.address(), m_target.port()); + + start_announce(); + } + + udp::endpoint udp_tracker_connection::pick_target_endpoint() const + { + std::list::const_iterator iter = m_endpoints.begin(); + udp::endpoint target = udp::endpoint(iter->address(), iter->port()); + + if (bind_interface() != address_v4::any()) + { + // find first endpoint that matches our bind interface type + for (; iter != m_endpoints.end() && iter->address().is_v4() + != bind_interface().is_v4(); ++iter); + + if (iter == m_endpoints.end()) + { + TORRENT_ASSERT(target.address().is_v4() != bind_interface().is_v4()); + boost::shared_ptr cb = requester(); + if (cb) + { + char const* tracker_address_type = target.address().is_v4() ? "IPv4" : "IPv6"; + char const* bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + char msg[200]; + snprintf(msg, sizeof(msg) + , "the tracker only resolves to an %s address, and you're " + "listening on an %s socket. This may prevent you from receiving " + "incoming connections." + , tracker_address_type, bind_address_type); + + cb->tracker_warning(tracker_req(), msg); + } + } + else + { + target = udp::endpoint(iter->address(), iter->port()); + } + } + + return target; + } + + void udp_tracker_connection::start_announce() + { + mutex::scoped_lock l(m_cache_mutex); + std::map::iterator cc + = m_connection_cache.find(m_target.address()); + if (cc != m_connection_cache.end()) + { + // we found a cached entry! Now, we can only + // use if if it hasn't expired + if (time_now() < cc->second.expires) + { + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + return; + } + // if it expired, remove it from the cache + m_connection_cache.erase(cc); + } + l.unlock(); + + send_udp_connect(); + } + + void udp_tracker_connection::on_timeout(error_code const& ec) + { + if (ec) + { + fail(ec); + return; + } + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + boost::shared_ptr cb = requester(); + if (cb) cb->debug_log("*** UDP_TRACKER [ timed out url: %s ]", tracker_req().url.c_str()); +#endif + fail(error_code(errors::timed_out)); + } + + void udp_tracker_connection::close() + { + error_code ec; + tracker_connection::close(); + } + + bool udp_tracker_connection::on_receive_hostname(error_code const& e + , char const* hostname, char const* buf, int size) + { + // just ignore the hostname this came from, pretend that + // it's from the same endpoint we sent it to (i.e. the same + // port). We have so many other ways of confirming this packet + // comes from the tracker anyway, so it's not a big deal + return on_receive(e, m_target, buf, size); + } + + bool udp_tracker_connection::on_receive(error_code const& e + , udp::endpoint const& ep, char const* buf, int size) + { + // ignore resposes before we've sent any requests + if (m_state == action_error) return false; + + if (m_abort) return false; + + // ignore packet not sent from the tracker + // if m_target is inaddr_any, it suggests that we + // sent the packet through a proxy only knowing + // the hostname, in which case this packet might be for us + if (!is_any(m_target.address()) && m_target != ep) return false; + + if (e) fail(e); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + cb->debug_log("<== UDP_TRACKER_PACKET [ size: %d ]", size); + } +#endif + + // ignore packets smaller than 8 bytes + if (size < 8) return false; + + const char* ptr = buf; + int action = detail::read_int32(ptr); + int transaction = detail::read_int32(ptr); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + cb->debug_log("*** UDP_TRACKER_PACKET [ action: %d ]", action); + } +#endif + + // ignore packets with incorrect transaction id + if (m_transaction_id != transaction) return false; + + if (action == action_error) + { + fail(error_code(errors::tracker_failure), -1, std::string(ptr, size - 8).c_str()); + return true; + } + + // ignore packets that's not a response to our message + if (action != m_state) return false; + + restart_read_timeout(); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + cb->debug_log("*** UDP_TRACKER_RESPONSE [ tid: %x ]" + , int(transaction)); + } +#endif + + switch (m_state) + { + case action_connect: + return on_connect_response(buf, size); + case action_announce: + return on_announce_response(buf, size); + case action_scrape: + return on_scrape_response(buf, size); + default: break; + } + return false; + } + + bool udp_tracker_connection::on_connect_response(char const* buf, int size) + { + // ignore packets smaller than 16 bytes + if (size < 16) return false; + + restart_read_timeout(); + buf += 8; // skip header + + // reset transaction + m_transaction_id = 0; + m_attempts = 0; + boost::uint64_t connection_id = detail::read_int64(buf); + + mutex::scoped_lock l(m_cache_mutex); + connection_cache_entry& cce = m_connection_cache[m_target.address()]; + cce.connection_id = connection_id; + cce.expires = time_now() + seconds(m_ses.m_settings.udp_tracker_token_expiry); + + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + return true; + } + + void udp_tracker_connection::send_udp_connect() + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + char hex_ih[41]; + to_hex((char const*)&tracker_req().info_hash[0], 20, hex_ih); + cb->debug_log("==> UDP_TRACKER_CONNECT [%s]", hex_ih); + } +#endif + if (m_abort) return; + + char buf[16]; + char* ptr = buf; + + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + detail::write_uint32(0x417, ptr); + detail::write_uint32(0x27101980, ptr); // connection_id + detail::write_int32(action_connect, ptr); // action (connect) + detail::write_int32(m_transaction_id, ptr); // transaction_id + TORRENT_ASSERT(ptr - buf == sizeof(buf)); + + error_code ec; + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, 16, ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, 16, ec); + } + m_state = action_connect; + sent_bytes(16 + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + + void udp_tracker_connection::send_udp_scrape() + { + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + if (m_abort) return; + + std::map::iterator i + = m_connection_cache.find(m_target.address()); + // this isn't really supposed to happen + TORRENT_ASSERT(i != m_connection_cache.end()); + if (i == m_connection_cache.end()) return; + + char buf[8 + 4 + 4 + 20]; + char* out = buf; + + detail::write_int64(i->second.connection_id, out); // connection_id + detail::write_int32(action_scrape, out); // action (scrape) + detail::write_int32(m_transaction_id, out); // transaction_id + // info_hash + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS + out += 20; + TORRENT_ASSERT(out - buf == sizeof(buf)); +#endif + + error_code ec; + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, sizeof(buf), ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, sizeof(buf), ec); + } + m_state = action_scrape; + sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + + bool udp_tracker_connection::on_announce_response(char const* buf, int size) + { + if (size < 20) return false; + + buf += 8; // skip header + restart_read_timeout(); + int interval = detail::read_int32(buf); + int min_interval = 60; + int incomplete = detail::read_int32(buf); + int complete = detail::read_int32(buf); + int num_peers = (size - 20) / 6; + if ((size - 20) % 6 != 0) + { + fail(error_code(errors::invalid_tracker_response_length)); + return false; + } + + boost::shared_ptr cb = requester(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + if (cb) + { + boost::shared_ptr cb = requester(); + cb->debug_log("<== UDP_TRACKER_RESPONSE [ url: %s ]", tracker_req().url.c_str()); + } +#endif + + if (!cb) + { + close(); + return true; + } + + std::vector peer_list; + for (int i = 0; i < num_peers; ++i) + { + // TODO: it would be more efficient to not use a string here. + // however, the problem is that some trackers will respond + // with actual strings. For example i2p trackers + peer_entry e; + char ip_string[100]; + unsigned int a = detail::read_uint8(buf); + unsigned int b = detail::read_uint8(buf); + unsigned int c = detail::read_uint8(buf); + unsigned int d = detail::read_uint8(buf); + snprintf(ip_string, 100, "%u.%u.%u.%u", a, b, c, d); + e.ip = ip_string; + e.port = detail::read_uint16(buf); + e.pid.clear(); + peer_list.push_back(e); + } + + std::list
ip_list; + for (std::list::const_iterator i = m_endpoints.begin() + , end(m_endpoints.end()); i != end; ++i) + { + ip_list.push_back(i->address()); + } + + cb->tracker_response(tracker_req(), m_target.address(), ip_list + , peer_list, interval, min_interval, complete, incomplete, 0, address(), "" /*trackerid*/); + + close(); + return true; + } + + bool udp_tracker_connection::on_scrape_response(char const* buf, int size) + { + restart_read_timeout(); + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(error_code(errors::invalid_tracker_transaction_id)); + return false; + } + + if (action == action_error) + { + fail(error_code(errors::tracker_failure), -1, std::string(buf, size - 8).c_str()); + return true; + } + + if (action != action_scrape) + { + fail(error_code(errors::invalid_tracker_action)); + return true; + } + + if (size < 20) + { + fail(error_code(errors::invalid_tracker_response_length)); + return true; + } + + int complete = detail::read_int32(buf); + int downloaded = detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + + boost::shared_ptr cb = requester(); + if (!cb) + { + close(); + return true; + } + + cb->tracker_scrape_response(tracker_req() + , complete, incomplete, downloaded, -1); + + close(); + return true; + } + + void udp_tracker_connection::send_udp_announce() + { + if (m_transaction_id == 0) + m_transaction_id = random() ^ (random() << 16); + + if (m_abort) return; + + char buf[800]; + char* out = buf; + + tracker_request const& req = tracker_req(); + const bool stats = req.send_stats; + session_settings const& settings = m_ses.settings(); + + std::map::iterator i + = m_connection_cache.find(m_target.address()); + // this isn't really supposed to happen + TORRENT_ASSERT(i != m_connection_cache.end()); + if (i == m_connection_cache.end()) return; + + detail::write_int64(i->second.connection_id, out); // connection_id + detail::write_int32(action_announce, out); // action (announce) + detail::write_int32(m_transaction_id, out); // transaction_id + std::copy(req.info_hash.begin(), req.info_hash.end(), out); // info_hash + out += 20; + std::copy(req.pid.begin(), req.pid.end(), out); // peer_id + out += 20; + detail::write_int64(stats ? req.downloaded : 0, out); // downloaded + detail::write_int64(stats ? req.left : 0, out); // left + detail::write_int64(stats ? req.uploaded : 0, out); // uploaded + detail::write_int32(req.event, out); // event + // ip address + address_v4 announce_ip; + + if (!m_ses.settings().anonymous_mode + && !settings.announce_ip.empty()) + { + error_code ec; + address ip = address::from_string(settings.announce_ip.c_str(), ec); + if (!ec && ip.is_v4()) announce_ip = ip.to_v4(); + } + detail::write_uint32(announce_ip.to_ulong(), out); + detail::write_int32(req.key, out); // key + detail::write_int32(req.num_want, out); // num_want + detail::write_uint16(req.listen_port, out); // port + + std::string request_string; + error_code ec; + using boost::tuples::ignore; + boost::tie(ignore, ignore, ignore, ignore, request_string) = parse_url_components(req.url, ec); + if (ec) request_string.clear(); + + if (!request_string.empty()) + { + int str_len = (std::min)(int(request_string.size()), 255); + request_string.resize(str_len); + + detail::write_uint8(2, out); + detail::write_uint8(str_len, out); + detail::write_string(request_string, out); + } + + TORRENT_ASSERT(out - buf <= int(sizeof(buf))); + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING + boost::shared_ptr cb = requester(); + if (cb) + { + char hex_ih[41]; + to_hex((char const*)&req.info_hash[0], 20, hex_ih); + cb->debug_log("==> UDP_TRACKER_ANNOUNCE [%s]", hex_ih); + } +#endif + + if (!m_hostname.empty()) + { + m_ses.m_udp_socket.send_hostname(m_hostname.c_str(), m_target.port(), buf, out - buf, ec); + } + else + { + m_ses.m_udp_socket.send(m_target, buf, out - buf, ec); + } + m_state = action_announce; + sent_bytes(out - buf + 28); // assuming UDP/IP header + ++m_attempts; + if (ec) + { + fail(ec); + return; + } + } + +} + diff --git a/apps/Launcher/ext/libtorrent/src/upnp.cpp b/apps/Launcher/ext/libtorrent/src/upnp.cpp new file mode 100644 index 0000000000..f3ef5b5747 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/upnp.cpp @@ -0,0 +1,1603 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/enum_net.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/random.hpp" + +#if defined TORRENT_ASIO_DEBUGGING +#include "libtorrent/debug.hpp" +#endif + +#include +#include +#if BOOST_VERSION < 103500 +#include +#include +#else +#include +#include +#endif +#include + +namespace libtorrent { + +namespace upnp_errors +{ + boost::system::error_code make_error_code(error_code_enum e) + { + return error_code(e, get_upnp_category()); + } + +} // upnp_errors namespace + +static error_code ec; + +// TODO: listen_interface is not used. It's meant to bind the broadcast socket +upnp::upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb, log_callback_t const& lcb + , bool ignore_nonrouters, void* state) + : m_user_agent(user_agent) + , m_callback(cb) + , m_log_callback(lcb) + , m_retry_count(0) + , m_io_service(ios) + , m_socket(udp::endpoint(address_v4::from_string("239.255.255.250", ec), 1900) + , boost::bind(&upnp::on_reply, self(), _1, _2, _3)) + , m_broadcast_timer(ios) + , m_refresh_timer(ios) + , m_map_timer(ios) + , m_disabled(false) + , m_closing(false) + , m_ignore_non_routers(ignore_nonrouters) + , m_cc(cc) +{ + TORRENT_ASSERT(cb); + + error_code ec; + m_socket.open(ios, ec); + + if (state) + { + upnp_state_t* s = (upnp_state_t*)state; + m_devices.swap(s->devices); + m_mappings.swap(s->mappings); + delete s; + } + + m_mappings.reserve(10); +} + +void* upnp::drain_state() +{ + upnp_state_t* s = new upnp_state_t; + s->mappings.swap(m_mappings); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + i->upnp_connection.reset(); + s->devices.swap(m_devices); + return s; +} + +upnp::~upnp() +{ +} + +void upnp::discover_device() +{ + mutex::scoped_lock l(m_mutex); + if (m_socket.num_send_sockets() == 0) + log("No network interfaces to broadcast to", l); + + discover_device_impl(l); +} + +void upnp::log(char const* msg, mutex::scoped_lock& l) +{ + l.unlock(); + m_log_callback(msg); + l.lock(); +} + +void upnp::discover_device_impl(mutex::scoped_lock& l) +{ + const char msearch[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n\r\n"; + + error_code ec; +#ifdef TORRENT_DEBUG_UPNP + // simulate packet loss + if (m_retry_count & 1) +#endif + m_socket.send(msearch, sizeof(msearch) - 1, ec); + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting." + , convert_from_native(ec.message()).c_str()); + log(msg, l); + disable(ec, l); + return; + } + +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::resend_request"); +#endif + ++m_retry_count; + m_broadcast_timer.expires_from_now(seconds(2 * m_retry_count), ec); + m_broadcast_timer.async_wait(boost::bind(&upnp::resend_request + , self(), _1)); + + log("broadcasting search for rootdevice", l); +} + +// returns a reference to a mapping or -1 on failure +int upnp::add_mapping(upnp::protocol_type p, int external_port, int local_port) +{ + // external port 0 means _every_ port + TORRENT_ASSERT(external_port != 0); + + mutex::scoped_lock l(m_mutex); + + char msg[500]; + snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u " + "local_port: %u ] %s", (p == tcp?"tcp":"udp"), external_port + , local_port, m_disabled ? "DISABLED": ""); + log(msg, l); + if (m_disabled) return -1; + + std::vector::iterator i = std::find_if( + m_mappings.begin(), m_mappings.end() + , boost::bind(&global_mapping_t::protocol, _1) == int(none)); + + if (i == m_mappings.end()) + { + m_mappings.push_back(global_mapping_t()); + i = m_mappings.end() - 1; + } + + i->protocol = p; + i->external_port = external_port; + i->local_port = local_port; + + int mapping_index = i - m_mappings.begin(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + + if (int(d.mapping.size()) <= mapping_index) + d.mapping.resize(mapping_index + 1); + mapping_t& m = d.mapping[mapping_index]; + + m.action = mapping_t::action_add; + m.protocol = p; + m.external_port = external_port; + m.local_port = local_port; + + if (d.service_namespace) update_map(d, mapping_index, l); + } + + return mapping_index; +} + +void upnp::delete_mapping(int mapping) +{ + mutex::scoped_lock l(m_mutex); + + if (mapping >= int(m_mappings.size())) return; + + global_mapping_t& m = m_mappings[mapping]; + + char msg[500]; + snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u " + "local_port: %u ]", (m.protocol == tcp?"tcp":"udp"), m.external_port + , m.local_port); + log(msg, l); + + if (m.protocol == none) return; + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + + TORRENT_ASSERT(mapping < int(d.mapping.size())); + d.mapping[mapping].action = mapping_t::action_delete; + + if (d.service_namespace) update_map(d, mapping, l); + } +} + +bool upnp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + global_mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + +void upnp::resend_request(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::resend_request"); +#endif + if (ec) return; + + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + if (m_closing) return; + + if (m_retry_count < 12 + && (m_devices.empty() || m_retry_count < 4)) + { + discover_device_impl(l); + return; + } + + if (m_devices.empty()) + { + disable(errors::no_router, l); + return; + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + TORRENT_TRY + { + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str()); + log(msg, l); + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_xml, self(), _1, _2 + , boost::ref(d), _5))); + d.upnp_connection->get(d.url, seconds(30), 1); + } + TORRENT_CATCH (std::exception& exc) + { + TORRENT_DECLARE_DUMMY(std::exception, exc); + char msg[500]; + snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), exc.what()); + log(msg, l); + d.disabled = true; + } + } + } +} + +void upnp::on_reply(udp::endpoint const& from, char* buffer + , std::size_t bytes_transferred) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + using namespace libtorrent::detail; + + // parse out the url for the device + +/* + the response looks like this: + + HTTP/1.1 200 OK + ST:upnp:rootdevice + USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice + Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc + Server: Custom/1.0 UPnP/1.0 Proc/Ver + EXT: + Cache-Control:max-age=180 + DATE: Fri, 02 Jan 1970 08:10:38 GMT + + a notification looks like this: + + NOTIFY * HTTP/1.1 + Host:239.255.255.250:1900 + NT:urn:schemas-upnp-org:device:MediaServer:1 + NTS:ssdp:alive + Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e + USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1 + Cache-Control:max-age=900 + Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 + +*/ + error_code ec; + if (!in_local_network(m_io_service, from.address(), ec)) + { + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" + , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + } + else + { + char msg[400]; + int num_chars = snprintf(msg, sizeof(msg) + , "ignoring response from: %s. IP is not on local network. " + , print_endpoint(from).c_str()); + + std::vector net = enum_net_interfaces(m_io_service, ec); + for (std::vector::const_iterator i = net.begin() + , end(net.end()); i != end && num_chars < int(sizeof(msg)); ++i) + { + num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " + , print_address(i->interface_address).c_str(), print_address(i->netmask).c_str()); + } + log(msg, l); + return; + } + } + + bool non_router = false; + if (m_ignore_non_routers) + { + std::vector routes = enum_routes(m_io_service, ec); + if (std::find_if(routes.begin(), routes.end() + , boost::bind(&ip_route::gateway, _1) == from.address()) == routes.end()) + { + // this upnp device is filtered because it's not in the + // list of configured routers + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "failed to enumerate routes when " + "receiving response from: %s: %s" + , print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + } + else + { + char msg[400]; + int num_chars = snprintf(msg, sizeof(msg), "SSDP response from: " + "%s: IP is not a router. " + , print_endpoint(from).c_str()); + for (std::vector::const_iterator i = routes.begin() + , end(routes.end()); i != end && num_chars < int(sizeof(msg)); ++i) + { + num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " + , print_address(i->gateway).c_str(), print_address(i->netmask).c_str()); + } + log(msg, l); + non_router = true; + } + } + } + + http_parser p; + bool error = false; + p.incoming(buffer::const_interval(buffer + , buffer + bytes_transferred), error); + if (error) + { + char msg[500]; + snprintf(msg, sizeof(msg), "received malformed HTTP from: %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + if (p.status_code() != 200 && p.method() != "notify") + { + if (p.method().empty()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "HTTP status %u from %s" + , p.status_code(), print_endpoint(from).c_str()); + log(msg, l); + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "HTTP method %s from %s" + , p.method().c_str(), print_endpoint(from).c_str()); + log(msg, l); + } + return; + } + + if (!p.header_finished()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + std::string url = p.header("location"); + if (url.empty()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "missing location header from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + rootdevice d; + d.url = url; + + std::set::iterator i = m_devices.find(d); + + if (i == m_devices.end()) + { + std::string protocol; + std::string auth; + error_code ec; + // we don't have this device in our list. Add it + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s" + , d.url.c_str(), print_endpoint(from).c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + return; + } + + // ignore the auth here. It will be re-parsed + // by the http connection later + + if (protocol != "http") + { + char msg[500]; + snprintf(msg, sizeof(msg), "unsupported protocol %s from %s" + , protocol.c_str(), print_endpoint(from).c_str()); + log(msg, l); + return; + } + + if (d.port == 0) + { + char msg[500]; + snprintf(msg, sizeof(msg), "URL with port 0 from %s" + , print_endpoint(from).c_str()); + log(msg, l); + return; + } + + char msg[500]; + snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)" + , d.url.c_str(), int(m_devices.size())); + log(msg, l); + + if (m_devices.size() >= 50) + { + char msg[500]; + snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s" + , int(m_devices.size()), d.url.c_str()); + log(msg, l); + return; + } + d.non_router = non_router; + + TORRENT_ASSERT(d.mapping.empty()); + for (std::vector::iterator j = m_mappings.begin() + , end(m_mappings.end()); j != end; ++j) + { + mapping_t m; + m.action = mapping_t::action_add; + m.local_port = j->local_port; + m.external_port = j->external_port; + m.protocol = j->protocol; + d.mapping.push_back(m); + } + boost::tie(i, boost::tuples::ignore) = m_devices.insert(d); + } + + + // iterate over the devices we know and connect and issue the mappings + try_map_upnp(l); + + if (m_ignore_non_routers) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::map_timer"); +#endif + // check back in in a little bit to see if we have seen any + // devices at one of our default routes. If not, we want to override + // ignoring them and use them instead (better than not working). + m_map_timer.expires_from_now(seconds(1), ec); + m_map_timer.async_wait(boost::bind(&upnp::map_timer + , self(), _1)); + } +} + +void upnp::map_timer(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::map_timer"); +#endif + if (ec) return; + if (m_closing) return; + + mutex::scoped_lock l(m_mutex); + try_map_upnp(l, true); +} + +void upnp::try_map_upnp(mutex::scoped_lock& l, bool timer) +{ + if (m_devices.empty()) return; + + bool override_ignore_non_routers = false; + if (m_ignore_non_routers && timer) + { + // if we don't ave any devices that match our default route, we + // should try to map with the ones we did hear from anyway, + // regardless of if they are not running at our gateway. + override_ignore_non_routers = std::find_if(m_devices.begin() + , m_devices.end(), boost::bind(&rootdevice::non_router, _1) == false) + == m_devices.end(); + if (override_ignore_non_routers) + { + char msg[500]; + snprintf(msg, sizeof(msg), "overriding ignore non-routers"); + log(msg, l); + } + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + // if we're ignoring non-routers, skip them. If on_timer is + // set, we expect to have received all responses and if we don't + // have any devices at our default route, then issue requests + // to any device we found. + if (m_ignore_non_routers && i->non_router + && !override_ignore_non_routers) + continue; + + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + TORRENT_TRY + { + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to: %s" + , d.url.c_str()); + log(msg, l); + + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_xml, self(), _1, _2 + , boost::ref(d), _5))); + d.upnp_connection->get(d.url, seconds(30), 1); + } + TORRENT_CATCH (std::exception& exc) + { + TORRENT_DECLARE_DUMMY(std::exception, exc); + char msg[500]; + snprintf(msg, sizeof(msg), "connection failed to: %s %s" + , d.url.c_str(), exc.what()); + log(msg, l); + d.disabled = true; + } + } + } +} + +void upnp::post(upnp::rootdevice const& d, char const* soap + , char const* soap_action, mutex::scoped_lock& l) +{ + TORRENT_ASSERT(d.magic == 1337); + TORRENT_ASSERT(d.upnp_connection); + + char header[2048]; + snprintf(header, sizeof(header), "POST %s HTTP/1.1\r\n" + "Host: %s:%u\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "Content-Length: %d\r\n" + "Soapaction: \"%s#%s\"\r\n\r\n" + "%s" + , d.path.c_str(), d.hostname.c_str(), d.port + , int(strlen(soap)), d.service_namespace, soap_action + , soap); + + d.upnp_connection->sendbuffer = header; + + char msg[1024]; + snprintf(msg, sizeof(msg), "sending: %s", header); + log(msg, l); +} + +void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "mapping %u aborted", i); + log(msg, l); + return; + } + + char const* soap_action = "AddPortMapping"; + + std::string local_endpoint = print_address(c.socket().local_endpoint(ec).address()); + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + "%u" + "%s" + "%u" + "%s" + "1" + "%s at %s:%d" + "%u" + "" + , soap_action, d.service_namespace, d.mapping[i].external_port + , (d.mapping[i].protocol == udp ? "UDP" : "TCP") + , d.mapping[i].local_port + , local_endpoint.c_str() + , m_user_agent.c_str(), local_endpoint.c_str(), d.mapping[i].local_port + , d.lease_duration, soap_action); + + post(d, soap, soap_action, l); +} + +void upnp::next(rootdevice& d, int i, mutex::scoped_lock& l) +{ + if (i < num_mappings() - 1) + { + update_map(d, i + 1, l); + } + else + { + std::vector::iterator j + = std::find_if(d.mapping.begin(), d.mapping.end() + , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); + if (j == d.mapping.end()) return; + + update_map(d, j - d.mapping.begin(), l); + } +} + +void upnp::update_map(rootdevice& d, int i, mutex::scoped_lock& l) +{ + TORRENT_ASSERT(d.magic == 1337); + TORRENT_ASSERT(i < int(d.mapping.size())); + TORRENT_ASSERT(d.mapping.size() == m_mappings.size()); + + if (d.upnp_connection) return; + + boost::intrusive_ptr me(self()); + + mapping_t& m = d.mapping[i]; + + if (m.action == mapping_t::action_none + || m.protocol == none) + { + char msg[500]; + snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i); + log(msg, l); + m.action = mapping_t::action_none; + next(d, i, l); + return; + } + + TORRENT_ASSERT(!d.upnp_connection); + TORRENT_ASSERT(d.service_namespace); + + char msg[500]; + snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str()); + log(msg, l); + if (m.action == mapping_t::action_add) + { + if (m.failcount > 5) + { + m.action = mapping_t::action_none; + // giving up + next(d, i, l); + return; + } + + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_map_response, self(), _1, _2 + , boost::ref(d), i, _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i))); + + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); + } + else if (m.action == mapping_t::action_delete) + { + if (d.upnp_connection) d.upnp_connection->close(); + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_unmap_response, self(), _1, _2 + , boost::ref(d), i, _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::delete_port_mapping, self(), boost::ref(d), i))); + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); + } + + m.action = mapping_t::action_none; +} + +void upnp::delete_port_mapping(rootdevice& d, int i) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "unmapping %u aborted", i); + log(msg, l); + return; + } + + char const* soap_action = "DeletePortMapping"; + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + "%u" + "%s" + "" + , soap_action, d.service_namespace + , d.mapping[i].external_port + , (d.mapping[i].protocol == udp ? "UDP" : "TCP") + , soap_action); + + post(d, soap, soap_action, l); +} + +namespace +{ + void copy_tolower(std::string& dst, char const* src) + { + dst.clear(); + while (*src) dst.push_back(to_lower(*src++)); + } +} + +struct parse_state +{ + parse_state(): in_service(false), service_type(0) {} + void reset(char const* st) + { + in_service = false; + service_type = st; + tag_stack.clear(); + control_url.clear(); + model.clear(); + url_base.clear(); + } + bool in_service; + std::list tag_stack; + std::string control_url; + char const* service_type; + std::string model; + std::string url_base; + bool top_tags(const char* str1, const char* str2) + { + std::list::reverse_iterator i = tag_stack.rbegin(); + if (i == tag_stack.rend()) return false; + if (!string_equal_no_case(i->c_str(), str2)) return false; + ++i; + if (i == tag_stack.rend()) return false; + if (!string_equal_no_case(i->c_str(), str1)) return false; + return true; + } +}; + +TORRENT_EXTRA_EXPORT void find_control_url(int type, char const* string, parse_state& state) +{ + if (type == xml_start_tag) + { + std::string tag; + copy_tolower(tag, string); + state.tag_stack.push_back(tag); +// std::copy(state.tag_stack.begin(), state.tag_stack.end(), std::ostream_iterator(std::cout, " ")); +// std::cout << std::endl; + } + else if (type == xml_end_tag) + { + if (!state.tag_stack.empty()) + { + if (state.in_service && state.tag_stack.back() == "service") + state.in_service = false; + state.tag_stack.pop_back(); + } + } + else if (type == xml_string) + { + if (state.tag_stack.empty()) return; +// std::cout << " " << string << std::endl; + if (!state.in_service && state.top_tags("service", "servicetype")) + { + if (string_equal_no_case(string, state.service_type)) + state.in_service = true; + } + else if (state.control_url.empty() && state.in_service && state.top_tags("service", "controlurl")) + { + // default to the first (or only) control url in the router's listing + state.control_url = string; + } + else if (state.model.empty() && state.top_tags("device", "modelname")) + { + state.model = string; + } + else if (state.tag_stack.back() == "urlbase") + { + state.url_base = string; + } + } +} + +void upnp::on_upnp_xml(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" + , d.url.c_str(), convert_from_native(e.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (!p.header_finished()) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message" + , d.url.c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" + , d.url.c_str(), convert_from_native(p.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + parse_state s; + s.reset("urn:schemas-upnp-org:service:WANIPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_control_url, _1, _2, boost::ref(s))); + if (!s.control_url.empty()) + { + d.service_namespace = s.service_type; + if (!s.model.empty()) m_model = s.model; + } + else + { + // we didn't find the WAN IP connection, look for + // a PPP connection + s.reset("urn:schemas-upnp-org:service:WANPPPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_control_url, _1, _2, boost::ref(s))); + if (!s.control_url.empty()) + { + d.service_namespace = s.service_type; + if (!s.model.empty()) m_model = s.model; + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s" + , d.url.c_str()); + log(msg, l); + d.disabled = true; + return; + } + } + + if (!s.url_base.empty() && s.control_url.substr(0, 7) != "http://") + { + // avoid double slashes in path + if (s.url_base[s.url_base.size()-1] == '/' + && !s.control_url.empty() + && s.control_url[0] == '/') + s.url_base.erase(s.url_base.end()-1); + d.control_url = s.url_base + s.control_url; + } + else d.control_url = s.control_url; + + std::string protocol; + std::string auth; + error_code ec; + if (!d.control_url.empty() && d.control_url[0] == '/') + { + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + d.control_url = protocol + "://" + d.hostname + ":" + + to_string(d.port).elems + s.control_url; + } + + char msg[500]; + snprintf(msg, sizeof(msg), "found control URL: %s namespace %s " + "urlbase: %s in response from %s" + , d.control_url.c_str(), d.service_namespace + , s.url_base.c_str(), d.url.c_str()); + log(msg, l); + + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.control_url, ec); + if (d.port == -1) d.port = protocol == "http" ? 80 : 443; + + if (ec) + { + char msg[500]; + snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s" + , d.control_url.c_str(), convert_from_native(ec.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + d.upnp_connection.reset(new http_connection(m_io_service + , m_cc, boost::bind(&upnp::on_upnp_get_ip_address_response, self(), _1, _2 + , boost::ref(d), _5), true, default_max_bottled_buffer_size + , boost::bind(&upnp::get_ip_address, self(), boost::ref(d)))); + d.upnp_connection->start(d.hostname, to_string(d.port).elems + , seconds(10), 1); +} + +void upnp::get_ip_address(rootdevice& d) +{ + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + + if (!d.upnp_connection) + { + TORRENT_ASSERT(d.disabled); + char msg[500]; + snprintf(msg, sizeof(msg), "getting external IP address"); + log(msg, l); + return; + } + + char const* soap_action = "GetExternalIPAddress"; + + char soap[2048]; + error_code ec; + snprintf(soap, sizeof(soap), "\n" + "" + "" + "" + , soap_action, d.service_namespace + , soap_action); + + post(d, soap, soap_action, l); +} + +void upnp::disable(error_code const& ec, mutex::scoped_lock& l) +{ + m_disabled = true; + + // kill all mappings + for (std::vector::iterator i = m_mappings.begin() + , end(m_mappings.end()); i != end; ++i) + { + if (i->protocol == none) continue; + i->protocol = none; + l.unlock(); + m_callback(i - m_mappings.begin(), address(), 0, ec); + l.lock(); + } + + // we cannot clear the devices since there + // might be outstanding requests relying on + // the device entry being present when they + // complete + error_code e; + m_broadcast_timer.cancel(e); + m_refresh_timer.cancel(e); + m_map_timer.cancel(e); + m_socket.close(); +} + +namespace +{ + struct error_code_parse_state + { + error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {} + bool in_error_code; + bool exit; + int error_code; + }; + + void find_error_code(int type, char const* string, error_code_parse_state& state) + { + if (state.exit) return; + if (type == xml_start_tag && !std::strcmp("errorCode", string)) + { + state.in_error_code = true; + } + else if (type == xml_string && state.in_error_code) + { + state.error_code = std::atoi(string); + state.exit = true; + } + } + + struct ip_address_parse_state: public error_code_parse_state + { + ip_address_parse_state(): in_ip_address(false) {} + bool in_ip_address; + std::string ip_address; + }; + + void find_ip_address(int type, char const* string, ip_address_parse_state& state) + { + find_error_code(type, string, state); + if (state.exit) return; + + if (type == xml_start_tag && !std::strcmp("NewExternalIPAddress", string)) + { + state.in_ip_address = true; + } + else if (type == xml_string && state.in_ip_address) + { + state.ip_address = string; + state.exit = true; + } + } + + struct error_code_t + { + int code; + char const* msg; + }; + + error_code_t error_codes[] = + { + {0, "no error"} + , {402, "Invalid Arguments"} + , {501, "Action Failed"} + , {714, "The specified value does not exist in the array"} + , {715, "The source IP address cannot be wild-carded"} + , {716, "The external port cannot be wild-carded"} + , {718, "The port mapping entry specified conflicts with " + "a mapping assigned previously to another client"} + , {724, "Internal and External port values must be the same"} + , {725, "The NAT implementation only supports permanent " + "lease times on port mappings"} + , {726, "RemoteHost must be a wildcard and cannot be a " + "specific IP address or DNS name"} + , {727, "ExternalPort must be a wildcard and cannot be a specific port "} + }; + +} + +#if BOOST_VERSION >= 103500 + +struct upnp_error_category : boost::system::error_category +{ + virtual const char* name() const BOOST_SYSTEM_NOEXCEPT + { + return "UPnP error"; + } + + virtual std::string message(int ev) const BOOST_SYSTEM_NOEXCEPT + { + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {ev, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); + if (e != end && e->code == ev) + { + return e->msg; + } + char msg[500]; + snprintf(msg, sizeof(msg), "unknown UPnP error (%d)", ev); + return msg; + } + + virtual boost::system::error_condition default_error_condition( + int ev) const BOOST_SYSTEM_NOEXCEPT + { + return boost::system::error_condition(ev, *this); + } +}; + +boost::system::error_category& get_upnp_category() +{ + static upnp_error_category cat; + return cat; +} + +#else + +boost::system::error_category& get_upnp_category() +{ + static ::asio::error::error_category cat(21); + return cat; +} + +#endif + +void upnp::on_upnp_get_ip_address_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (m_closing) return; + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + if (!p.header_finished()) + { + log("error while getting external IP address: incomplete http message", l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address: %s" + , convert_from_native(p.message()).c_str()); + log(msg, l); + if (num_mappings() > 0) update_map(d, 0, l); + return; + } + + // response may look like + // + // + // + // 192.168.160.19 + // + // + // + + char msg[500]; + snprintf(msg, sizeof(msg), "get external IP address response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + + ip_address_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_ip_address, _1, _2, boost::ref(s))); + if (s.error_code != -1) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while getting external IP address, code: %u" + , s.error_code); + log(msg, l); + } + + if (!s.ip_address.empty()) { + snprintf(msg, sizeof(msg), "got router external IP address %s", s.ip_address.c_str()); + log(msg, l); + d.external_ip = address::from_string(s.ip_address.c_str(), ec); + } else { + log("failed to find external IP address in response", l); + } + + if (num_mappings() > 0) update_map(d, 0, l); +} + +void upnp::on_upnp_map_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while adding port map: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + d.disabled = true; + return; + } + + if (m_closing) return; + +// error code response may look like this: +// +// +// +// s:Client +// UPnPError +// +// +// 402 +// Invalid Args +// +// +// +// +// + + if (!p.header_finished()) + { + log("error while adding port map: incomplete http message", l); + next(d, mapping, l); + return; + } + + std::string ct = p.header("content-type"); + if (!ct.empty() + && ct.find_first_of("text/xml") == std::string::npos + && ct.find_first_of("text/soap+xml") == std::string::npos + && ct.find_first_of("application/xml") == std::string::npos + && ct.find_first_of("application/soap+xml") == std::string::npos + ) + { + char msg[300]; + snprintf(msg, sizeof(msg), "error while adding port map: invalid content-type, \"%s\". Expected text/xml or application/soap+xml" + , ct.c_str()); + log(msg, l); + next(d, mapping, l); + return; + } + + // We don't want to ignore responses with return codes other than 200 + // since those might contain valid UPnP error codes + + error_code_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_error_code, _1, _2, boost::ref(s))); + + if (s.error_code != -1) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while adding port map, code: %u" + , s.error_code); + log(msg, l); + } + + mapping_t& m = d.mapping[mapping]; + + if (s.error_code == 725) + { + // only permanent leases supported + d.lease_duration = 0; + m.action = mapping_t::action_add; + ++m.failcount; + update_map(d, mapping, l); + return; + } + else if (s.error_code == 727) + { + return_error(mapping, s.error_code, l); + } + else if ((s.error_code == 718 || s.error_code == 501) && m.failcount < 4) + { + // some routers return 501 action failed, instead of 716 + // The external port conflicts with another mapping + // pick a random port + m.external_port = 40000 + (random() % 10000); + m.action = mapping_t::action_add; + ++m.failcount; + update_map(d, mapping, l); + return; + } + else if (s.error_code != -1) + { + return_error(mapping, s.error_code, l); + } + + char msg[500]; + snprintf(msg, sizeof(msg), "map response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + + if (s.error_code == -1) + { + l.unlock(); + m_callback(mapping, d.external_ip, m.external_port, error_code()); + l.lock(); + if (d.lease_duration > 0) + { + m.expires = time_now() + + seconds(int(d.lease_duration * 0.75f)); + ptime next_expire = m_refresh_timer.expires_at(); + if (next_expire < time_now() + || next_expire > m.expires) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::on_expire"); +#endif + error_code ec; + m_refresh_timer.expires_at(m.expires, ec); + m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); + } + } + else + { + m.expires = max_time(); + } + m.failcount = 0; + } + + next(d, mapping, l); +} + +void upnp::return_error(int mapping, int code, mutex::scoped_lock& l) +{ + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {code, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , boost::bind(&error_code_t::code, _1) < boost::bind(&error_code_t::code, _2)); + std::string error_string = "UPnP mapping error "; + error_string += to_string(code).elems; + if (e != end && e->code == code) + { + error_string += ": "; + error_string += e->msg; + } + l.unlock(); + m_callback(mapping, address(), 0, error_code(code, get_upnp_category())); + l.lock(); +} + +void upnp::on_upnp_unmap_response(error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping + , http_connection& c) +{ + boost::intrusive_ptr me(self()); + + mutex::scoped_lock l(m_mutex); + + TORRENT_ASSERT(d.magic == 1337); + if (d.upnp_connection && d.upnp_connection.get() == &c) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e && e != asio::error::eof) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while deleting portmap: %s" + , convert_from_native(e.message()).c_str()); + log(msg, l); + } + else if (!p.header_finished()) + { + log("error while deleting portmap: incomplete http message", l); + } + else if (p.status_code() != 200) + { + char msg[500]; + snprintf(msg, sizeof(msg), "error while deleting portmap: %s" + , convert_from_native(p.message()).c_str()); + log(msg, l); + } + else + { + char msg[500]; + snprintf(msg, sizeof(msg), "unmap response: %s" + , std::string(p.get_body().begin, p.get_body().end).c_str()); + log(msg, l); + } + + error_code_parse_state s; + if (p.header_finished()) + { + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , boost::bind(&find_error_code, _1, _2, boost::ref(s))); + } + + l.unlock(); + m_callback(mapping, address(), 0, p.status_code() != 200 + ? error_code(p.status_code(), get_http_category()) + : error_code(s.error_code, get_upnp_category())); + l.lock(); + + d.mapping[mapping].protocol = none; + + next(d, mapping, l); +} + +void upnp::on_expire(error_code const& ec) +{ +#if defined TORRENT_ASIO_DEBUGGING + complete_async("upnp::on_expire"); +#endif + if (ec) return; + + ptime now = time_now(); + ptime next_expire = max_time(); + + mutex::scoped_lock l(m_mutex); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + for (int m = 0; m < num_mappings(); ++m) + { + if (d.mapping[m].expires != max_time()) + continue; + + if (d.mapping[m].expires < now) + { + d.mapping[m].expires = max_time(); + update_map(d, m, l); + } + else if (d.mapping[m].expires < next_expire) + { + next_expire = d.mapping[m].expires; + } + } + } + if (next_expire != max_time()) + { +#if defined TORRENT_ASIO_DEBUGGING + add_outstanding_async("upnp::on_expire"); +#endif + error_code e; + m_refresh_timer.expires_at(next_expire, e); + m_refresh_timer.async_wait(boost::bind(&upnp::on_expire, self(), _1)); + } +} + +void upnp::close() +{ + mutex::scoped_lock l(m_mutex); + + error_code ec; + m_refresh_timer.cancel(ec); + m_broadcast_timer.cancel(ec); + m_map_timer.cancel(ec); + m_closing = true; + m_socket.close(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + TORRENT_ASSERT(d.magic == 1337); + if (d.control_url.empty()) continue; + for (std::vector::iterator j = d.mapping.begin() + , end(d.mapping.end()); j != end; ++j) + { + if (j->protocol == none) continue; + if (j->action == mapping_t::action_add) + { + j->action = mapping_t::action_none; + continue; + } + j->action = mapping_t::action_delete; + m_mappings[j - d.mapping.begin()].protocol = none; + } + if (num_mappings() > 0) update_map(d, 0, l); + } +} + +} + diff --git a/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp b/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp new file mode 100644 index 0000000000..270459a7ba --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ut_metadata.cpp @@ -0,0 +1,613 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include +#include + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/ut_metadata.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/random.hpp" +#ifdef TORRENT_STATS +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent { namespace +{ + enum + { + // this is the max number of bytes we'll + // queue up in the send buffer. If we exceed this, + // we'll wait another second before checking + // the send buffer size again. So, this may limit + // the rate at which we can server metadata to + // 160 kiB/s + send_buffer_limit = 0x4000 * 10, + + // this is the max number of requests we'll queue + // up. If we get more requests tha this, we'll + // start rejecting them, claiming we don't have + // metadata. If the torrent is greater than 16 MiB, + // we may hit this case (and the client requesting + // doesn't throttle its requests) + max_incoming_requests = 1024 + }; + + int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + + struct ut_metadata_peer_plugin; + + struct ut_metadata_plugin : torrent_plugin + { + ut_metadata_plugin(torrent& t) + : m_torrent(t) + , m_metadata_progress(0) + , m_metadata_size(0) + { + } + + virtual void on_files_checked() + { + // if the torrent is a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + buffer::const_interval metadata() const + { + TORRENT_ASSERT(m_torrent.valid_metadata()); + if (!m_metadata) + { + m_metadata = m_torrent.torrent_file().metadata(); + m_metadata_size = m_torrent.torrent_file().metadata_size(); + TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final() + == m_torrent.torrent_file().info_hash()); + } + return buffer::const_interval(m_metadata.get(), m_metadata.get() + + m_metadata_size); + } + + bool received_metadata(ut_metadata_peer_plugin& source + , char const* buf, int size, int piece, int total_size); + + // returns a piece of the metadata that + // we should request. + // returns -1 if we should hold off the request + int metadata_request(bool has_metadata); + + // this is called from the peer_connection for + // each piece of metadata it receives + void metadata_progress(int total_size, int received) + { + m_metadata_progress += received; + m_metadata_size = total_size; + m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size); + } + + void on_piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + void metadata_size(int size) + { + if (m_metadata_size > 0 || size <= 0 || size > 4 * 1024 * 1024) return; + m_metadata_size = size; + m_metadata.reset(new char[size]); + m_requested_metadata.resize(div_round_up(size, 16 * 1024)); + } + + private: + torrent& m_torrent; + + // this buffer is filled with the info-section of + // the metadata file while downloading it from + // peers, and while sending it. + // it is mutable because it's generated lazily + mutable boost::shared_array m_metadata; + + int m_metadata_progress; + mutable int m_metadata_size; + + struct metadata_piece + { + metadata_piece(): num_requests(0), last_request(0) {} + int num_requests; + time_t last_request; + boost::weak_ptr source; + bool operator<(metadata_piece const& rhs) const + { return num_requests < rhs.num_requests; } + }; + + // this vector keeps track of how many times each meatdata + // block has been requested and who we ended up getting it from + // std::numeric_limits::max() means we have the piece + std::vector m_requested_metadata; + }; + + + struct ut_metadata_peer_plugin : peer_plugin, boost::enable_shared_from_this + { + friend struct ut_metadata_plugin; + + ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc + , ut_metadata_plugin& tp) + : m_message_index(0) + , m_request_limit(min_time()) + , m_torrent(t) + , m_pc(pc) + , m_tp(tp) + {} + + virtual char const* type() const { return "ut_metadata"; } + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["ut_metadata"] = 2; + if (m_torrent.valid_metadata()) + h["metadata_size"] = m_tp.metadata().left(); + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find_dict("m"); + if (!messages) return false; + + int index = messages->dict_find_int_value("ut_metadata", -1); + if (index == -1) return false; + m_message_index = index; + + int metadata_size = h.dict_find_int_value("metadata_size"); + if (metadata_size > 0) + m_tp.metadata_size(metadata_size); + else + m_pc.set_has_metadata(false); + + maybe_send_request(); + return true; + } + + void write_metadata_packet(int type, int piece) + { + TORRENT_ASSERT(type >= 0 && type <= 2); + TORRENT_ASSERT(!m_pc.associated_torrent().expired()); + +#ifdef TORRENT_VERBOSE_LOGGING + char const* names[] = {"request", "data", "dont-have"}; + char const* n = ""; + if (type >= 0 && type < 3) n = names[type]; + m_pc.peer_log("==> UT_METADATA [ type: %d (%s) | piece: %d ]", type, n, piece); +#endif + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + + entry e; + e["msg_type"] = type; + e["piece"] = piece; + + char const* metadata = 0; + int metadata_piece_size = 0; + + if (type == 1) + { + + if (piece < 0 || piece >= int(m_tp.metadata().left() + 16 * 1024 - 1)/(16*1024)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** UT_METADATA [ invalid piece %d metadata size: %d ]" + , piece, int(m_tp.metadata().left())); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return; + } + + TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata()); + e["total_size"] = m_tp.metadata().left(); + int offset = piece * 16 * 1024; + metadata = m_tp.metadata().begin + offset; + metadata_piece_size = (std::min)( + int(m_tp.metadata().left() - offset), 16 * 1024); + TORRENT_ASSERT(metadata_piece_size > 0); + TORRENT_ASSERT(offset >= 0); + TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.metadata().left())); + } + + char msg[200]; + char* header = msg; + char* p = &msg[6]; + int len = bencode(p, e); + int total_size = 2 + len + metadata_piece_size; + namespace io = detail; + io::write_uint32(total_size, header); + io::write_uint8(bt_peer_connection::msg_extended, header); + io::write_uint8(m_message_index, header); + + m_pc.send_buffer(msg, len + 6); + if (metadata_piece_size) m_pc.append_const_send_buffer( + metadata, metadata_piece_size); + } + + virtual bool on_extended(int length + , int extended_msg, buffer::const_interval body) + { + if (extended_msg != 2) return false; + if (m_message_index == 0) return false; + + if (length > 17 * 1024) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ packet too big %d ]", length); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + if (!m_pc.packet_finished()) return true; + + int len; + entry msg = bdecode(body.begin, body.end, len); + if (msg.type() != entry::dictionary_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ not a dictionary ]"); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + + entry const* type_ent = msg.find_key("msg_type"); + entry const* piece_ent = msg.find_key("piece"); + if (type_ent == 0 || type_ent->type() != entry::int_t + || piece_ent == 0 || piece_ent->type() != entry::int_t) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ missing or invalid keys ]"); +#endif + m_pc.disconnect(errors::invalid_metadata_message, 2); + return true; + } + int type = type_ent->integer(); + int piece = piece_ent->integer(); + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== UT_METADATA [ type: %d | piece: %d ]", type, piece); +#endif + + switch (type) + { + case 0: // request + { + if (!m_torrent.valid_metadata()) + { + write_metadata_packet(2, piece); + return true; + } + if (m_pc.send_buffer_size() < send_buffer_limit) + write_metadata_packet(1, piece); + else if (m_incoming_requests.size() < max_incoming_requests) + m_incoming_requests.push_back(piece); + else + write_metadata_packet(2, piece); + } + break; + case 1: // data + { + std::vector::iterator i = std::find(m_sent_requests.begin() + , m_sent_requests.end(), piece); + + // unwanted piece? + if (i == m_sent_requests.end()) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** UT_METADATA [ UNWANTED / TIMED OUT ]"); +#endif + return true; + } + + m_sent_requests.erase(i); + entry const* total_size = msg.find_key("total_size"); + m_tp.received_metadata(*this, body.begin + len, body.left() - len, piece + , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0); + maybe_send_request(); + } + break; + case 2: // have no data + { + m_request_limit = (std::max)(time_now() + minutes(1), m_request_limit); + std::vector::iterator i = std::find(m_sent_requests.begin() + , m_sent_requests.end(), piece); + // unwanted piece? + if (i == m_sent_requests.end()) return true; + m_sent_requests.erase(i); + } + break; + default: + // unknown message, ignore + break; + } + return true; + } + + virtual void tick() + { + maybe_send_request(); + while (!m_incoming_requests.empty() + && m_pc.send_buffer_size() < send_buffer_limit) + { + int piece = m_incoming_requests.front(); + m_incoming_requests.erase(m_incoming_requests.begin()); + write_metadata_packet(1, piece); + } + } + + void maybe_send_request() + { + if (m_pc.is_disconnecting()) return; + + // if we don't have any metadata, and this peer + // supports the request metadata extension + // and we aren't currently waiting for a request + // reply. Then, send a request for some metadata. + if (!m_torrent.valid_metadata() + && m_message_index != 0 + && m_sent_requests.size() < 2 + && has_metadata()) + { + int piece = m_tp.metadata_request(m_pc.has_metadata()); + if (piece == -1) return; + + m_sent_requests.push_back(piece); + write_metadata_packet(0, piece); + } + } + + bool has_metadata() const + { + return m_pc.has_metadata() || (time_now() > m_request_limit); + } + + void failed_hash_check(ptime const& now) + { + m_request_limit = now + seconds(20 + (boost::int64_t(random()) * 50) / UINT_MAX); + } + + private: + + // this is the message index the remote peer uses + // for metadata extension messages. + int m_message_index; + + // this is set to the next time we can request pieces + // again. It is updated every time we get a + // "I don't have metadata" message, but also when + // we receive metadata that fails the infohash check + ptime m_request_limit; + + // request queues + std::vector m_sent_requests; + std::vector m_incoming_requests; + + torrent& m_torrent; + bt_peer_connection& m_pc; + ut_metadata_plugin& m_tp; + }; + + boost::shared_ptr ut_metadata_plugin::new_connection( + peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + bt_peer_connection* c = static_cast(pc); + return boost::shared_ptr(new ut_metadata_peer_plugin(m_torrent, *c, *this)); + } + + // has_metadata is false if the peer making the request has not announced + // that it has metadata. In this case, it shouldn't prevent other peers + // from requesting this block by setting a timeout on it. + int ut_metadata_plugin::metadata_request(bool has_metadata) + { + std::vector::iterator i = std::min_element( + m_requested_metadata.begin(), m_requested_metadata.end()); + + if (m_requested_metadata.empty()) + { + // if we don't know how many pieces there are + // just ask for piece 0 + m_requested_metadata.resize(1); + i = m_requested_metadata.begin(); + } + + int piece = i - m_requested_metadata.begin(); + + // don't request the same block more than once every 3 seconds + time_t now = time(0); + if (now - m_requested_metadata[piece].last_request < 3) return -1; + + ++m_requested_metadata[piece].num_requests; + + // only set the timeout on this block, only if the peer + // has metadata. This is to prevent peers with no metadata + // to starve out sending requests to peers with metadata + if (has_metadata) + m_requested_metadata[piece].last_request = now; + + return piece; + } + + inline bool ut_metadata_plugin::received_metadata( + ut_metadata_peer_plugin& source + , char const* buf, int size, int piece, int total_size) + { + if (m_torrent.valid_metadata()) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ ALREADY HAVE METADATA ]"); +#endif + m_torrent.add_redundant_bytes(size, torrent::piece_unknown); + return false; + } + + if (!m_metadata) + { + // verify the total_size + if (total_size <= 0 || total_size > m_torrent.session().settings().max_metadata_size) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ metadata size too big: %d ]", total_size); +#endif +// #error post alert + return false; + } + + m_metadata.reset(new char[total_size]); + m_requested_metadata.resize(div_round_up(total_size, 16 * 1024)); + m_metadata_size = total_size; + } + + if (piece < 0 || piece >= int(m_requested_metadata.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ piece: %d INVALID ]", piece); +#endif + return false; + } + + if (total_size != m_metadata_size) + { +#ifdef TORRENT_VERBOSE_LOGGING + source.m_pc.peer_log("*** UT_METADATA [ total_size: %d INCONSISTENT WITH: %d ]" + , total_size, m_metadata_size); +#endif + // they disagree about the size! + return false; + } + + if (piece * 16 * 1024 + size > m_metadata_size) + { + // this piece is invalid + return false; + } + + std::memcpy(&m_metadata[piece * 16 * 1024], buf, size); + // mark this piece has 'have' + m_requested_metadata[piece].num_requests = (std::numeric_limits::max)(); + m_requested_metadata[piece].source = source.shared_from_this(); + + bool have_all = std::count_if(m_requested_metadata.begin() + , m_requested_metadata.end(), boost::bind(&metadata_piece::num_requests, _1) + == (std::numeric_limits::max)()) == int(m_requested_metadata.size()); + + if (!have_all) return false; + + if (!m_torrent.set_metadata(&m_metadata[0], m_metadata_size)) + { + if (!m_torrent.valid_metadata()) + { + ptime now = time_now(); + // any peer that we downloaded metadata from gets a random time + // penalty, from 5 to 30 seconds or so. During this time we don't + // make any metadata requests from those peers (to mix it up a bit + // of which peers we use) + // if we only have one block, and thus requested it from a single + // peer, we bump up the retry time a lot more to try other peers + bool single_peer = m_requested_metadata.size() == 1; + for (int i = 0; i < int(m_requested_metadata.size()); ++i) + { + m_requested_metadata[i].num_requests = 0; + boost::shared_ptr peer + = m_requested_metadata[i].source.lock(); + if (!peer) continue; + + peer->failed_hash_check(single_peer ? now + minutes(5) : now); + } + } + return false; + } + + // clear the storage for the bitfield + std::vector().swap(m_requested_metadata); + + return true; + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_ut_metadata_plugin(torrent* t, void*) + { + // don't add this extension if the torrent is private + if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr(); + return boost::shared_ptr(new ut_metadata_plugin(*t)); + } + +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/ut_pex.cpp b/apps/Launcher/ext/libtorrent/src/ut_pex.cpp new file mode 100644 index 0000000000..eeefbb7830 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/ut_pex.cpp @@ -0,0 +1,690 @@ +/* + +Copyright (c) 2006-2014, MassaRoddel, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/broadcast_socket.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/random.hpp" + +#include "libtorrent/extensions/ut_pex.hpp" + +#ifdef TORRENT_VERBOSE_LOGGING +#include "libtorrent/lazy_entry.hpp" +#endif + +namespace libtorrent { namespace +{ + const char extension_name[] = "ut_pex"; + + enum + { + extension_index = 1, + max_peer_entries = 100 + }; + + bool send_peer(peer_connection const& p) + { + // don't send out those peers that we haven't connected to + // (that have connected to us) and that aren't sharing their + // listening port + if (!p.is_outgoing() && !p.received_listen_port()) return false; + // don't send out peers that we haven't successfully connected to + if (p.is_connecting()) return false; + if (p.in_handshake()) return false; + return true; + } + + struct ut_pex_plugin: torrent_plugin + { + // randomize when we rebuild the pex message + // to evenly spread it out across all torrents + // the more torrents we have, the longer we can + // delay the rebuilding + ut_pex_plugin(torrent& t) + : m_torrent(t) + , m_last_msg(min_time()) + , m_peers_in_message(0) {} + + virtual boost::shared_ptr new_connection(peer_connection* pc); + + std::vector& get_ut_pex_msg() + { + return m_ut_pex_msg; + } + + int peers_in_msg() const + { + return m_peers_in_message; + } + + // the second tick of the torrent + // each minute the new lists of "added" + "added.f" and "dropped" + // are calculated here and the pex message is created + // each peer connection will use this message + // max_peer_entries limits the packet size + virtual void tick() + { + ptime now = time_now(); + if (now - m_last_msg < seconds(60)) return; + m_last_msg = now; + + int num_peers = m_torrent.num_peers(); + if (num_peers == 0) return; + + entry pex; + std::string& pla = pex["added"].string(); + std::string& pld = pex["dropped"].string(); + std::string& plf = pex["added.f"].string(); + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator pld_out(pld); + std::back_insert_iterator plf_out(plf); +#if TORRENT_USE_IPV6 + std::string& pla6 = pex["added6"].string(); + std::string& pld6 = pex["dropped6"].string(); + std::string& plf6 = pex["added6.f"].string(); + std::back_insert_iterator pla6_out(pla6); + std::back_insert_iterator pld6_out(pld6); + std::back_insert_iterator plf6_out(plf6); +#endif + + std::set dropped; + m_old_peers.swap(dropped); + + m_peers_in_message = 0; + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + peer_connection* peer = *i; + if (!send_peer(*peer)) continue; + + tcp::endpoint remote = peer->remote(); + m_old_peers.insert(remote); + + std::set::iterator di = dropped.find(remote); + if (di == dropped.end()) + { + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + if (peer->type() != peer_connection::bittorrent_connection) + continue; + + bt_peer_connection* p = static_cast(peer); + + // if the peer has told us which port its listening on, + // use that port. But only if we didn't connect to the peer. + // if we connected to it, use the port we know works + policy::peer *pi = 0; + if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) + remote.port(pi->port); + + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + // 0x04 - supports uTP. This is only a positive flags + // passing 0 doesn't mean the peer doesn't + // support uTP + // 0x08 - supports holepunching protocol. If this + // flag is received from a peer, it can be + // used as a rendezvous point in case direct + // connections to the peer fail + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + flags |= is_utp(*p->get_socket()) ? 4 : 0; + flags |= p->supports_holepunch() ? 8 : 0; + + // i->first was added since the last time + if (remote.address().is_v4()) + { + detail::write_endpoint(remote, pla_out); + detail::write_uint8(flags, plf_out); + } +#if TORRENT_USE_IPV6 + else + { + detail::write_endpoint(remote, pla6_out); + detail::write_uint8(flags, plf6_out); + } +#endif + ++num_added; + ++m_peers_in_message; + } + else + { + // this was in the previous message + // so, it wasn't dropped + dropped.erase(di); + } + } + + for (std::set::const_iterator i = dropped.begin() + , end(dropped.end()); i != end; ++i) + { + if (i->address().is_v4()) + detail::write_endpoint(*i, pld_out); +#if TORRENT_USE_IPV6 + else + detail::write_endpoint(*i, pld6_out); +#endif + ++m_peers_in_message; + } + + m_ut_pex_msg.clear(); + bencode(std::back_inserter(m_ut_pex_msg), pex); + } + + private: + torrent& m_torrent; + + std::set m_old_peers; + ptime m_last_msg; + std::vector m_ut_pex_msg; + int m_peers_in_message; + }; + + + struct ut_pex_peer_plugin : peer_plugin + { + ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp) + : m_torrent(t) + , m_pc(pc) + , m_tp(tp) + , m_last_msg(min_time()) + , m_message_index(0) + , m_first_time(true) + { + const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); + for (int i = 0; i < num_pex_timers; ++i) + { + m_last_pex[i]= min_time(); + } + } + + virtual char const* type() const { return "ut_pex"; } + + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages[extension_name] = extension_index; + } + + virtual bool on_extension_handshake(lazy_entry const& h) + { + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; + + int index = int(messages->dict_find_int_value(extension_name, -1)); + if (index == -1) return false; + m_message_index = index; + return true; + } + + virtual bool on_extended(int length, int msg, buffer::const_interval body) + { + if (msg != extension_index) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + { + m_pc.disconnect(errors::pex_message_too_large, 2); + return true; + } + + if (body.left() < length) return true; + + ptime now = time_now(); + if (now - m_last_pex[0] < seconds(60)) + { + // this client appears to be trying to flood us + // with pex messages. Don't allow that. + m_pc.disconnect(errors::too_frequent_pex); + return true; + } + + const int num_pex_timers = sizeof(m_last_pex)/sizeof(m_last_pex[0]); + for (int i = 0; i < num_pex_timers-1; ++i) + m_last_pex[i] = m_last_pex[i+1]; + m_last_pex[num_pex_timers-1] = now; + + lazy_entry pex_msg; + error_code ec; + int ret = lazy_bdecode(body.begin, body.end, pex_msg, ec); + if (ret != 0 || pex_msg.type() != lazy_entry::dict_t) + { + m_pc.disconnect(errors::invalid_pex_message, 2); + return true; + } + + lazy_entry const* p = pex_msg.dict_find_string("dropped"); + +#ifdef TORRENT_VERBOSE_LOGGING + int num_dropped = 0; + int num_added = 0; + if (p) num_dropped += p->string_length()/6; +#endif + if (p) + { + int num_peers = p->string_length() / 6; + char const* in = p->string_ptr(); + + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); + peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); + if (j != m_peers.end() && *j == v) m_peers.erase(j); + } + } + + p = pex_msg.dict_find_string("added"); + lazy_entry const* pf = pex_msg.dict_find_string("added.f"); + +#ifdef TORRENT_VERBOSE_LOGGING + if (p) num_added += p->string_length() / 6; +#endif + if (p != 0 + && pf != 0 + && pf->string_length() == p->string_length() / 6) + { + int num_peers = pf->string_length(); + char const* in = p->string_ptr(); + char const* fin = pf->string_ptr(); + + peer_id pid(0); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + char flags = *fin++; + + if (int(m_peers.size()) >= m_torrent.settings().max_pex_peers) break; + + // ignore local addresses unless the peer is local to us + if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; + + peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port()); + peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v); + // do we already know about this peer? + if (j != m_peers.end() && *j == v) continue; + m_peers.insert(j, v); + p.add_peer(adr, pid, peer_info::pex, flags); + } + } + +#if TORRENT_USE_IPV6 + + lazy_entry const* p6 = pex_msg.dict_find("dropped6"); +#ifdef TORRENT_VERBOSE_LOGGING + if (p6) num_dropped += p6->string_length() / 18; +#endif + if (p6 != 0 && p6->type() == lazy_entry::string_t) + { + int num_peers = p6->string_length() / 18; + char const* in = p6->string_ptr(); + + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v6_endpoint(in); + peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); + peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); + if (j != m_peers6.end() && *j == v) m_peers6.erase(j); + } + } + + p6 = pex_msg.dict_find("added6"); +#ifdef TORRENT_VERBOSE_LOGGING + if (p6) num_added += p6->string_length() / 18; +#endif + lazy_entry const* p6f = pex_msg.dict_find("added6.f"); + if (p6 != 0 + && p6f != 0 + && p6->type() == lazy_entry::string_t + && p6f->type() == lazy_entry::string_t + && p6f->string_length() == p6->string_length() / 18) + { + int num_peers = p6f->string_length(); + char const* in = p6->string_ptr(); + char const* fin = p6f->string_ptr(); + + peer_id pid(0); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v6_endpoint(in); + char flags = *fin++; + // ignore local addresses unless the peer is local to us + if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue; + if (int(m_peers6.size()) >= m_torrent.settings().max_pex_peers) break; + + peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port()); + peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v); + // do we already know about this peer? + if (j != m_peers6.end() && *j == v) continue; + m_peers6.insert(j, v); + p.add_peer(adr, pid, peer_info::pex, flags); + } + } +#endif +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("<== PEX [ dropped: %d added: %d ]" + , num_dropped, num_added); +#endif + return true; + } + + // the peers second tick + // every minute we send a pex message + virtual void tick() + { + // no handshake yet + if (!m_message_index) return; + + ptime now = time_now(); + if (now - m_last_msg < seconds(60)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** PEX [ waiting: %d seconds to next msg ]" + , total_seconds(seconds(60) - (now - m_last_msg))); +#endif + return; + } + static ptime global_last = min_time(); + + int num_peers = m_torrent.num_peers(); + if (num_peers <= 1) return; + + // don't send pex messages more often than 1 every 100 ms, and + // allow pex messages to be sent 5 seconds apart if there isn't + // contention + int delay = (std::min)((std::max)(60000 / num_peers, 100), 3000); + + if (now - global_last < milliseconds(delay)) + { +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("*** PEX [ global-wait: %d ]", total_seconds(milliseconds(delay) - (now - global_last))); +#endif + return; + } + + // this will allow us to catch up, even if our timer + // has lower resolution than delay + if (global_last == min_time()) + global_last = now; + else + global_last += milliseconds(delay); + + m_last_msg = now; + + if (m_first_time) + { + send_ut_peer_list(); + m_first_time = false; + } + else + { + send_ut_peer_diff(); + } + } + + void send_ut_peer_diff() + { + // if there's no change in out peer set, don't send anything + if (m_tp.peers_in_msg() == 0) return; + + std::vector const& pex_msg = m_tp.get_ut_pex_msg(); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + pex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&pex_msg[0], pex_msg.size()); + +#ifdef TORRENT_VERBOSE_LOGGING + lazy_entry m; + error_code ec; + int ret = lazy_bdecode(&pex_msg[0], &pex_msg[0] + pex_msg.size(), m, ec); + TORRENT_ASSERT(ret == 0); + TORRENT_ASSERT(!ec); + int num_dropped = 0; + int num_added = 0; + lazy_entry const* e = m.dict_find_string("added"); + if (e) num_added += e->string_length() / 6; + e = m.dict_find_string("dropped"); + if (e) num_dropped += e->string_length() / 6; + e = m.dict_find_string("added6"); + if (e) num_added += e->string_length() / 18; + e = m.dict_find_string("dropped6"); + if (e) num_dropped += e->string_length() / 18; + m_pc.peer_log("==> PEX_DIFF [ dropped: %d added: %d msg_size: %d ]" + , num_dropped, num_added, int(pex_msg.size())); +#endif + } + + void send_ut_peer_list() + { + entry pex; + // leave the dropped string empty + pex["dropped"].string(); + std::string& pla = pex["added"].string(); + std::string& plf = pex["added.f"].string(); + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator plf_out(plf); + +#if TORRENT_USE_IPV6 + pex["dropped6"].string(); + std::string& pla6 = pex["added6"].string(); + std::string& plf6 = pex["added6.f"].string(); + std::back_insert_iterator pla6_out(pla6); + std::back_insert_iterator plf6_out(plf6); +#endif + + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + peer_connection* peer = *i; + if (!send_peer(*peer)) continue; + + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + if (peer->type() != peer_connection::bittorrent_connection) + continue; + + bt_peer_connection* p = static_cast(peer); + + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + // 0x04 - supports uTP. This is only a positive flags + // passing 0 doesn't mean the peer doesn't + // support uTP + // 0x08 - supports holepunching protocol. If this + // flag is received from a peer, it can be + // used as a rendezvous point in case direct + // connections to the peer fail + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + flags |= is_utp(*p->get_socket()) ? 4 : 0; + flags |= p->supports_holepunch() ? 8 : 0; + + tcp::endpoint remote = peer->remote(); + + policy::peer *pi = 0; + if (!p->is_outgoing() && (pi = peer->peer_info_struct()) && pi->port > 0) + remote.port(pi->port); + + // i->first was added since the last time + if (remote.address().is_v4()) + { + detail::write_endpoint(remote, pla_out); + detail::write_uint8(flags, plf_out); + } +#if TORRENT_USE_IPV6 + else + { + detail::write_endpoint(remote, pla6_out); + detail::write_uint8(flags, plf6_out); + } +#endif + ++num_added; + } + std::vector pex_msg; + bencode(std::back_inserter(pex_msg), pex); + + char msg[6]; + char* ptr = msg; + + detail::write_uint32(1 + 1 + pex_msg.size(), ptr); + detail::write_uint8(bt_peer_connection::msg_extended, ptr); + detail::write_uint8(m_message_index, ptr); + m_pc.send_buffer(msg, sizeof(msg)); + m_pc.send_buffer(&pex_msg[0], pex_msg.size()); + +#ifdef TORRENT_VERBOSE_LOGGING + m_pc.peer_log("==> PEX_FULL [ added: %d msg_size: %d ]", num_added, int(pex_msg.size())); +#endif + } + + torrent& m_torrent; + peer_connection& m_pc; + ut_pex_plugin& m_tp; + // stores all peers this this peer is connected to. These lists + // are updated with each pex message and are limited in size + // to protect against malicious clients. These lists are also + // used for looking up which peer a peer that supports holepunch + // came from. + // these are vectors to save memory and keep the items close + // together for performance. Inserting and removing is relatively + // cheap since the lists' size is limited + typedef std::vector > peers4_t; + peers4_t m_peers; +#if TORRENT_USE_IPV6 + typedef std::vector > peers6_t; + peers6_t m_peers6; +#endif + // the last pex messages we received + // [0] is the oldest one. There is a problem with + // rate limited connections, because we may sit + // for a long time, accumulating pex messages, and + // then once we read from the socket it will look like + // we received them all back to back. That's why + // we look at 6 pex messages back. + ptime m_last_pex[6]; + + ptime m_last_msg; + int m_message_index; + + // this is initialized to true, and set to + // false after the first pex message has been sent. + // it is used to know if a diff message or a) ful + // message should be sent. + bool m_first_time; + }; + + boost::shared_ptr ut_pex_plugin::new_connection(peer_connection* pc) + { + if (pc->type() != peer_connection::bittorrent_connection) + return boost::shared_ptr(); + + return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent + , *pc, *this)); + } +} } + +namespace libtorrent +{ + boost::shared_ptr create_ut_pex_plugin(torrent* t, void*) + { + if (t->torrent_file().priv() || (t->torrent_file().is_i2p() + && !t->settings().allow_i2p_mixed)) + { + return boost::shared_ptr(); + } + return boost::shared_ptr(new ut_pex_plugin(*t)); + } + + bool was_introduced_by(peer_plugin const* pp, tcp::endpoint const& ep) + { + ut_pex_peer_plugin* p = (ut_pex_peer_plugin*)pp; +#if TORRENT_USE_IPV6 + if (ep.address().is_v4()) + { +#endif + ut_pex_peer_plugin::peers4_t::value_type v(ep.address().to_v4().to_bytes(), ep.port()); + ut_pex_peer_plugin::peers4_t::const_iterator i + = std::lower_bound(p->m_peers.begin(), p->m_peers.end(), v); + return i != p->m_peers.end() && *i == v; +#if TORRENT_USE_IPV6 + } + else + { + ut_pex_peer_plugin::peers6_t::value_type v(ep.address().to_v6().to_bytes(), ep.port()); + ut_pex_peer_plugin::peers6_t::iterator i + = std::lower_bound(p->m_peers6.begin(), p->m_peers6.end(), v); + return i != p->m_peers6.end() && *i == v; + } +#endif + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/utf8.cpp b/apps/Launcher/ext/libtorrent/src/utf8.cpp new file mode 100644 index 0000000000..92ef3bc6ef --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utf8.cpp @@ -0,0 +1,104 @@ +/* + +Copyright (c) 2012-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/utf8.hpp" +#include "libtorrent/ConvertUTF.h" + +// on windows we need these functions for +// convert_to_native and convert_from_native +#if TORRENT_USE_WSTRING || defined TORRENT_WINDOWS + +namespace libtorrent +{ + utf8_conv_result_t utf8_wchar(const std::string &utf8, std::wstring &wide) + { + // allocate space for worst-case + wide.resize(utf8.size()); + wchar_t const* dst_start = wide.c_str(); + char const* src_start = utf8.c_str(); + ConversionResult ret; + if (sizeof(wchar_t) == sizeof(UTF32)) + { + ret = ConvertUTF8toUTF32((const UTF8**)&src_start, (const UTF8*)src_start + + utf8.size(), (UTF32**)&dst_start, (UTF32*)dst_start + wide.size() + , lenientConversion); + wide.resize(dst_start - wide.c_str()); + return (utf8_conv_result_t)ret; + } + else if (sizeof(wchar_t) == sizeof(UTF16)) + { + ret = ConvertUTF8toUTF16((const UTF8**)&src_start, (const UTF8*)src_start + + utf8.size(), (UTF16**)&dst_start, (UTF16*)dst_start + wide.size() + , lenientConversion); + wide.resize(dst_start - wide.c_str()); + return (utf8_conv_result_t)ret; + } + else + { + return source_illegal; + } + } + + utf8_conv_result_t wchar_utf8(const std::wstring &wide, std::string &utf8) + { + // allocate space for worst-case + utf8.resize(wide.size() * 6); + if (wide.empty()) return conversion_ok; + char* dst_start = &utf8[0]; + wchar_t const* src_start = wide.c_str(); + ConversionResult ret; + if (sizeof(wchar_t) == sizeof(UTF32)) + { + ret = ConvertUTF32toUTF8((const UTF32**)&src_start, (const UTF32*)src_start + + wide.size(), (UTF8**)&dst_start, (UTF8*)dst_start + utf8.size() + , lenientConversion); + utf8.resize(dst_start - &utf8[0]); + return (utf8_conv_result_t)ret; + } + else if (sizeof(wchar_t) == sizeof(UTF16)) + { + ret = ConvertUTF16toUTF8((const UTF16**)&src_start, (const UTF16*)src_start + + wide.size(), (UTF8**)&dst_start, (UTF8*)dst_start + utf8.size() + , lenientConversion); + utf8.resize(dst_start - &utf8[0]); + return (utf8_conv_result_t)ret; + } + else + { + return source_illegal; + } + } +} + +#endif + diff --git a/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp b/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp new file mode 100644 index 0000000000..c89fdf95ac --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utp_socket_manager.cpp @@ -0,0 +1,479 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/udp_socket.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/instantiate_connection.hpp" +#include "libtorrent/socket_io.hpp" +#include "libtorrent/broadcast_socket.hpp" // for is_teredo +#include "libtorrent/random.hpp" + +// #define TORRENT_DEBUG_MTU 1135 + +namespace libtorrent +{ + + utp_socket_manager::utp_socket_manager(session_settings const& sett, udp_socket& s + , incoming_utp_callback_t cb) + : m_sock(s) + , m_cb(cb) + , m_last_socket(0) + , m_new_connection(-1) + , m_sett(sett) + , m_last_route_update(min_time()) + , m_last_if_update(min_time()) + , m_sock_buf_size(0) + { + memset(m_counters, 0, sizeof(m_counters)); + } + + utp_socket_manager::~utp_socket_manager() + { + for (socket_map_t::iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end; ++i) + { + delete_utp_impl(i->second); + } + } + + void utp_socket_manager::get_status(utp_status& s) const + { + s.num_idle = 0; + s.num_syn_sent = 0; + s.num_connected = 0; + s.num_fin_sent = 0; + s.num_close_wait = 0; + + s.packet_loss = m_counters[packet_loss]; + s.timeout = m_counters[timeout]; + s.packets_in = m_counters[packets_in]; + s.packets_out = m_counters[packets_out]; + s.fast_retransmit = m_counters[fast_retransmit]; + s.packet_resend = m_counters[packet_resend]; + s.samples_above_target = m_counters[samples_above_target]; + s.samples_below_target = m_counters[samples_below_target]; + s.payload_pkts_in = m_counters[payload_pkts_in]; + s.payload_pkts_out = m_counters[payload_pkts_out]; + s.invalid_pkts_in = m_counters[invalid_pkts_in]; + s.redundant_pkts_in = m_counters[redundant_pkts_in]; + + for (socket_map_t::const_iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end; ++i) + { + int state = utp_socket_state(i->second); + switch (state) + { + case 0: ++s.num_idle; break; + case 1: ++s.num_syn_sent; break; + case 2: ++s.num_connected; break; + case 3: ++s.num_fin_sent; break; + case 4: ++s.num_close_wait; break; + case 5: ++s.num_close_wait; break; + } + } + } + + void utp_socket_manager::tick(ptime now) + { + for (socket_map_t::iterator i = m_utp_sockets.begin() + , end(m_utp_sockets.end()); i != end;) + { + if (should_delete(i->second)) + { + delete_utp_impl(i->second); + if (m_last_socket == i->second) m_last_socket = 0; + m_utp_sockets.erase(i++); + continue; + } + tick_utp_impl(i->second, now); + ++i; + } + } + + void utp_socket_manager::mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu) + { + if (time_now() - m_last_route_update > seconds(60)) + { + m_last_route_update = time_now(); + error_code ec; + m_routes = enum_routes(m_sock.get_io_service(), ec); + } + + int mtu = 0; + if (!m_routes.empty()) + { + for (std::vector::iterator i = m_routes.begin() + , end(m_routes.end()); i != end; ++i) + { + if (!match_addr_mask(addr, i->destination, i->netmask)) continue; + + // assume that we'll actually use the route with the largest + // MTU (seems like a reasonable assumption). + // this could however be improved by using the route metrics + // and the prefix length of the netmask to order the matches + if (mtu < i->mtu) mtu = i->mtu; + } + } + + if (mtu == 0) + { + if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; + else mtu = TORRENT_ETHERNET_MTU; + } + +#if defined __APPLE__ + // apple has a very strange loopback. It appears you can't + // send messages of the reported MTU size, and you don't get + // EWOULDBLOCK either. + if (is_loopback(addr)) + { + if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU; + else mtu = TORRENT_ETHERNET_MTU; + } +#endif + + // clamp the MTU within reasonable bounds + if (mtu < TORRENT_INET_MIN_MTU) mtu = TORRENT_INET_MIN_MTU; + else if (mtu > TORRENT_INET_MAX_MTU) mtu = TORRENT_INET_MAX_MTU; + + link_mtu = mtu; + + mtu -= TORRENT_UDP_HEADER; + + if (m_sock.get_proxy_settings().type == proxy_settings::socks5 + || m_sock.get_proxy_settings().type == proxy_settings::socks5_pw) + { + // this is for the IP layer + address proxy_addr = m_sock.proxy_addr().address(); + if (proxy_addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; + else mtu -= TORRENT_IPV6_HEADER; + + // this is for the SOCKS layer + mtu -= TORRENT_SOCKS5_HEADER; + + // the address field in the SOCKS header + if (addr.is_v4()) mtu -= 4; + else mtu -= 16; + + } + else + { + if (addr.is_v4()) mtu -= TORRENT_IPV4_HEADER; + else mtu -= TORRENT_IPV6_HEADER; + } + + utp_mtu = mtu; + } + + void utp_socket_manager::send_packet(udp::endpoint const& ep, char const* p + , int len, error_code& ec, int flags) + { + if (!m_sock.is_open()) + { + ec = asio::error::operation_aborted; + return; + } + +#ifdef TORRENT_DEBUG_MTU + // drop packets that exceed the debug MTU + if ((flags & dont_fragment) && len > TORRENT_DEBUG_MTU) return; +#endif + +#ifdef TORRENT_HAS_DONT_FRAGMENT + error_code tmp; + if (flags & utp_socket_manager::dont_fragment) + m_sock.set_option(libtorrent::dont_fragment(true), tmp); +#endif + m_sock.send(ep, p, len, ec); +#ifdef TORRENT_HAS_DONT_FRAGMENT + if (flags & utp_socket_manager::dont_fragment) + m_sock.set_option(libtorrent::dont_fragment(false), tmp); +#endif + } + + int utp_socket_manager::local_port(error_code& ec) const + { + return m_sock.local_endpoint(ec).port(); + } + + tcp::endpoint utp_socket_manager::local_endpoint(address const& remote, error_code& ec) const + { + tcp::endpoint socket_ep = m_sock.local_endpoint(ec); + + // first enumerate the routes in the routing table + if (time_now() - m_last_route_update > seconds(60)) + { + m_last_route_update = time_now(); + error_code ec; + m_routes = enum_routes(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + } + + if (m_routes.empty()) return socket_ep; + // then find the best match + ip_route* best = &m_routes[0]; + for (std::vector::iterator i = m_routes.begin() + , end(m_routes.end()); i != end; ++i) + { + if (is_any(i->destination) && i->destination.is_v4() == remote.is_v4()) + { + best = &*i; + continue; + } + + if (match_addr_mask(remote, i->destination, i->netmask)) + { + best = &*i; + continue; + } + } + + // best now tells us which interface we would send over + // for this target. Now figure out what the local address + // is for that interface + + if (time_now() - m_last_if_update > seconds(60)) + { + m_last_if_update = time_now(); + error_code ec; + m_interfaces = enum_net_interfaces(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + } + + for (std::vector::iterator i = m_interfaces.begin() + , end(m_interfaces.end()); i != end; ++i) + { + if (i->interface_address.is_v4() != remote.is_v4()) + continue; + + if (strcmp(best->name, i->name) == 0) + return tcp::endpoint(i->interface_address, socket_ep.port()); + } + return socket_ep; + } + + bool utp_socket_manager::incoming_packet(error_code const& ec, udp::endpoint const& ep + , char const* p, int size) + { +// UTP_LOGV("incoming packet size:%d\n", size); + + if (size < int(sizeof(utp_header))) return false; + + utp_header const* ph = (utp_header*)p; + +// UTP_LOGV("incoming packet version:%d\n", int(ph->get_version())); + + if (ph->get_version() != 1) return false; + + const ptime receive_time = time_now_hires(); + + // parse out connection ID and look for existing + // connections. If found, forward to the utp_stream. + boost::uint16_t id = ph->connection_id; + + // first test to see if it's the same socket as last time + // in most cases it is + if (m_last_socket + && utp_match(m_last_socket, ep, id)) + { + return utp_incoming_packet(m_last_socket, p, size, ep, receive_time); + } + + std::pair r = + m_utp_sockets.equal_range(id); + + for (; r.first != r.second; ++r.first) + { + if (!utp_match(r.first->second, ep, id)) continue; + bool ret = utp_incoming_packet(r.first->second, p, size, ep, receive_time); + if (ret) m_last_socket = r.first->second; + return ret; + } + +// UTP_LOGV("incoming packet id:%d source:%s\n", id, print_endpoint(ep).c_str()); + + if (!m_sett.enable_incoming_utp) + return false; + + // if not found, see if it's a SYN packet, if it is, + // create a new utp_stream + if (ph->get_type() == ST_SYN) + { + // possible SYN flood. Just ignore + if (int(m_utp_sockets.size()) > m_sett.connections_limit * 2) + return false; + +// UTP_LOGV("not found, new connection id:%d\n", m_new_connection); + + boost::shared_ptr c(new (std::nothrow) socket_type(m_sock.get_io_service())); + if (!c) return false; + + TORRENT_ASSERT(m_new_connection == -1); + // create the new socket with this ID + m_new_connection = id; + + instantiate_connection(m_sock.get_io_service(), proxy_settings(), *c, 0, this); + utp_stream* str = c->get(); + TORRENT_ASSERT(str); + int link_mtu, utp_mtu; + mtu_for_dest(ep.address(), link_mtu, utp_mtu); + utp_init_mtu(str->get_impl(), link_mtu, utp_mtu); + bool ret = utp_incoming_packet(str->get_impl(), p, size, ep, receive_time); + if (!ret) return false; + m_cb(c); + // the connection most likely changed its connection ID here + // we need to move it to the correct ID + return true; + } + + if (ph->get_type() == ST_RESET) return false; + + // #error send reset + + return false; + } + + void utp_socket_manager::subscribe_writable(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_stalled_sockets.begin(), m_stalled_sockets.end() + , s) == m_stalled_sockets.end()); + m_stalled_sockets.push_back(s); + } + + void utp_socket_manager::writable() + { + std::vector stalled_sockets; + m_stalled_sockets.swap(stalled_sockets); + for (std::vector::iterator i = stalled_sockets.begin() + , end(stalled_sockets.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_writable(s); + } + } + + void utp_socket_manager::socket_drained() + { + // flush all deferred acks + + std::vector deferred_acks; + m_deferred_acks.swap(deferred_acks); + for (std::vector::iterator i = deferred_acks.begin() + , end(deferred_acks.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_send_ack(s); + } + + std::vector drained_event; + m_drained_event.swap(drained_event); + for (std::vector::iterator i = drained_event.begin() + , end(drained_event.end()); i != end; ++i) + { + utp_socket_impl* s = *i; + utp_socket_drained(s); + } + } + + void utp_socket_manager::defer_ack(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_deferred_acks.begin(), m_deferred_acks.end(), s) + == m_deferred_acks.end()); + m_deferred_acks.push_back(s); + } + + void utp_socket_manager::subscribe_drained(utp_socket_impl* s) + { + TORRENT_ASSERT(std::find(m_drained_event.begin(), m_drained_event.end(), s) + == m_drained_event.end()); + m_drained_event.push_back(s); + } + + void utp_socket_manager::remove_socket(boost::uint16_t id) + { + socket_map_t::iterator i = m_utp_sockets.find(id); + if (i == m_utp_sockets.end()) return; + delete_utp_impl(i->second); + if (m_last_socket == i->second) m_last_socket = 0; + m_utp_sockets.erase(i); + } + + void utp_socket_manager::set_sock_buf(int size) + { + if (size < m_sock_buf_size) return; + m_sock.set_buf_size(size); + error_code ec; + // add more socket buffer storage on the lower level socket + // to avoid dropping packets because of a full receive buffer + // while processing a packet + + // only update the buffer size if it's bigger than + // what we already have + datagram_socket::receive_buffer_size recv_buf_size_opt; + m_sock.get_option(recv_buf_size_opt, ec); + if (recv_buf_size_opt.value() < size * 10) + { + m_sock.set_option(datagram_socket::receive_buffer_size(size * 10), ec); + m_sock.set_option(datagram_socket::send_buffer_size(size * 3), ec); + } + m_sock_buf_size = size; + } + + void utp_socket_manager::inc_stats_counter(int counter) + { + TORRENT_ASSERT(counter >= 0); + TORRENT_ASSERT(counter < num_counters); + ++m_counters[counter]; + } + + utp_socket_impl* utp_socket_manager::new_utp_socket(utp_stream* str) + { + boost::uint16_t send_id = 0; + boost::uint16_t recv_id = 0; + if (m_new_connection != -1) + { + send_id = m_new_connection; + recv_id = m_new_connection + 1; + m_new_connection = -1; + } + else + { + send_id = random() & 0xffff; + recv_id = send_id - 1; + } + utp_socket_impl* impl = construct_utp_impl(recv_id, send_id, str, this); + m_utp_sockets.insert(std::make_pair(recv_id, impl)); + return impl; + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/utp_stream.cpp b/apps/Launcher/ext/libtorrent/src/utp_stream.cpp new file mode 100644 index 0000000000..52bcb01f6b --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/utp_stream.cpp @@ -0,0 +1,3525 @@ +/* + +Copyright (c) 2009-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/config.hpp" +#include "libtorrent/utp_stream.hpp" +#include "libtorrent/sliding_average.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/timestamp_history.hpp" +#include "libtorrent/error.hpp" +#include "libtorrent/random.hpp" +#include "libtorrent/invariant_check.hpp" +#include +#include + +#define TORRENT_UTP_LOG 0 +#define TORRENT_VERBOSE_UTP_LOG 0 +#define TORRENT_UT_SEQ 1 + +#if TORRENT_UTP_LOG +#include +#include "libtorrent/socket_io.hpp" +#endif + +namespace libtorrent { + +#if TORRENT_UTP_LOG + +char const* packet_type_names[] = { "ST_DATA", "ST_FIN", "ST_STATE", "ST_RESET", "ST_SYN" }; +char const* socket_state_names[] = { "NONE", "SYN_SENT", "CONNECTED", "FIN_SENT", "ERROR", "DELETE" }; + +static struct utp_logger +{ + FILE* utp_log_file; + mutex utp_log_mutex; + + utp_logger() : utp_log_file(0) + { + utp_log_file = fopen("utp.log", "w+"); + } + ~utp_logger() + { + if (utp_log_file) fclose(utp_log_file); + } +} log_file_holder; + +void utp_log(char const* fmt, ...) +{ + mutex::scoped_lock lock(log_file_holder.utp_log_mutex); + static ptime start = time_now_hires(); + fprintf(log_file_holder.utp_log_file, "[%012" PRId64 "] ", total_microseconds(time_now_hires() - start)); + va_list l; + va_start(l, fmt); + vfprintf(log_file_holder.utp_log_file, fmt, l); + va_end(l); +} + +#define UTP_LOG utp_log +#if TORRENT_VERBOSE_UTP_LOG +#define UTP_LOGV utp_log +#else +#define UTP_LOGV if (false) printf +#endif + +#else + +#define UTP_LOG if (false) printf +#define UTP_LOGV if (false) printf + +#endif + +enum +{ + ACK_MASK = 0xffff, + + // the number of packets that'll fit in the reorder buffer + max_packets_reorder = 512, + + // if a packet receives more than this number of + // duplicate acks, we'll trigger a fast re-send + dup_ack_limit = 3, + + // the max number of packets to fast-resend per + // selective ack message + // only re-sending a single packet per sack + // appears to improve performance by making it + // less likely to loose the re-sent packet. Because + // when that happens, we must time-out in order + // to continue, which takes a long time. + sack_resend_limit = 1 +}; + +// compare if lhs is less than rhs, taking wrapping +// into account. if lhs is close to UINT_MAX and rhs +// is close to 0, lhs is assumed to have wrapped and +// considered smaller +TORRENT_EXTRA_EXPORT bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs, boost::uint32_t mask) +{ + // distance walking from lhs to rhs, downwards + boost::uint32_t dist_down = (lhs - rhs) & mask; + // distance walking from lhs to rhs, upwards + boost::uint32_t dist_up = (rhs - lhs) & mask; + + // if the distance walking up is shorter, lhs + // is less than rhs. If the distance walking down + // is shorter, then rhs is less than lhs + return dist_up < dist_down; +} + +// used for out-of-order incoming packets +// as well as sent packets that are waiting to be ACKed +struct packet +{ + // the last time this packet was sent + ptime send_time; + + // the number of bytes actually allocated in 'buf' + boost::uint16_t allocated; + + // the size of the buffer 'buf' points to + boost::uint16_t size; + + // this is the offset to the payload inside the buffer + // this is also used as a cursor to describe where the + // next payload that hasn't been consumed yet starts + boost::uint16_t header_size; + + // the number of times this packet has been sent + boost::uint8_t num_transmissions:6; + + // true if we need to send this packet again. All + // outstanding packets are marked as needing to be + // resent on timeouts + bool need_resend:1; + + // this is set to true for packets that were + // sent with the DF bit set (Don't Fragment) + bool mtu_probe:1; + +#ifdef TORRENT_DEBUG + int num_fast_resend; +#endif + + // the actual packet buffer + boost::uint8_t buf[1]; +}; + +// since the uTP socket state may be needed after the +// utp_stream is closed, it's kept in a separate struct +// whose lifetime is not tied to the lifetime of utp_stream + +// the utp socket is closely modelled after the asio async +// operations and handler model. For writing to the socket, +// the client provides a list of buffers (for gather/writev +// style of I/O) and whenever the socket can write another +// packet to the stream, it picks up data from these buffers. +// When all of the data has been written, or enough time has +// passed since we first started writing, the write handler +// is called and the write buffer is reset. This means that +// we're not writing anything at all while waiting for the +// client to re-issue a write request. + +// reading is a little bit more complicated, since we must +// be able to receive data even when the user doesn't have +// an outstanding read operation on the socket. When the user +// does however, we want to receive data directly into the +// user's buffer instead of first copying it into our receive +// buffer. This is why the receive case is more complicated. +// There are two receive buffers. One provided by the user, +// which when present is always used. The other one is used +// when the user doesn't have an outstanding read request, +// and hence hasn't provided any buffer space to receive into. + +// the user provided read buffer is called "m_read_buffer" and +// its size is "m_read_buffer_size". The buffer we spill over +// into when the user provided buffer is full or when there +// is none, is "m_receive_buffer" and "m_receive_buffer_size" +// respectively. + +// in order to know when to trigger the read and write handlers +// there are two counters, m_read and m_written, which count +// the number of bytes we've stuffed into the user provided +// read buffer or written to the stream from the write buffer. +// These are used to trigger the handlers if we're written a +// large number of bytes. It's also triggered if we're filled +// the whole read buffer, or written the entire write buffer. +// The last way the handlers can be triggered is if we're read +// or written some, and enough time has elapsed since then. + +// when we receive data into m_receive_buffer (i.e. the buffer +// used when there's no user provided one) is stored as a +// number of heap allocated packets. This is just because it's +// simple to reuse the data structured and it provides all the +// functionality needed for this buffer. + +struct utp_socket_impl +{ + utp_socket_impl(boost::uint16_t recv_id, boost::uint16_t send_id + , void* userdata, utp_socket_manager* sm) + : m_sm(sm) + , m_userdata(userdata) + , m_nagle_packet(NULL) + , m_read_handler(0) + , m_write_handler(0) + , m_connect_handler(0) + , m_remote_address() + , m_timeout(time_now_hires() + milliseconds(m_sm->connect_timeout())) + , m_last_history_step(time_now_hires()) + , m_cwnd(TORRENT_ETHERNET_MTU << 16) + , m_ssthres(0) + , m_buffered_incoming_bytes(0) + , m_reply_micro(0) + , m_adv_wnd(TORRENT_ETHERNET_MTU) + , m_bytes_in_flight(0) + , m_read(0) + , m_write_buffer_size(0) + , m_written(0) + , m_receive_buffer_size(0) + , m_read_buffer_size(0) + , m_in_buf_size(1024 * 1024) + , m_in_packets(0) + , m_out_packets(0) + , m_send_delay(0) + , m_recv_delay(0) + , m_port(0) + , m_send_id(send_id) + , m_recv_id(recv_id) + , m_ack_nr(0) + , m_seq_nr(0) + , m_acked_seq_nr(0) + , m_fast_resend_seq_nr(0) + , m_eof_seq_nr(0) + , m_loss_seq_nr(0) + , m_mtu(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER - 8 - 24 - 36) + , m_mtu_floor(TORRENT_INET_MIN_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_ceiling(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_seq(0) + , m_duplicate_acks(0) + , m_num_timeouts(0) + , m_delay_sample_idx(0) + , m_state(UTP_STATE_NONE) + , m_eof(false) + , m_attached(true) + , m_nagle(true) + , m_slow_start(true) + , m_cwnd_full(false) + , m_deferred_ack(false) + , m_subscribe_drained(false) + , m_stalled(false) + { + TORRENT_ASSERT(m_userdata); + for (int i = 0; i != num_delay_hist; ++i) + m_delay_sample_hist[i] = (std::numeric_limits::max)(); + } + + ~utp_socket_impl(); + + void tick(ptime const& now); + void init_mtu(int link_mtu, int utp_mtu); + bool incoming_packet(boost::uint8_t const* buf, int size + , udp::endpoint const& ep, ptime receive_time); + void writable(); + + bool should_delete() const; + tcp::endpoint remote_endpoint(error_code& ec) const + { + if (m_state == UTP_STATE_NONE) + ec = asio::error::not_connected; + else + TORRENT_ASSERT(m_remote_address != address_v4::any()); + return tcp::endpoint(m_remote_address, m_port); + } + std::size_t available() const; + // returns true if there were handlers cancelled + // if it returns false, we can detach immediately + bool destroy(); + void detach(); + void send_syn(); + void send_fin(); + + void subscribe_drained(); + void defer_ack(); + void remove_sack_header(packet* p); + + enum packet_flags_t { pkt_ack = 1, pkt_fin = 2 }; + bool send_pkt(int flags = 0); + bool resend_packet(packet* p, bool fast_resend = false); + void send_reset(utp_header* ph); + void parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr, int size, int* acked_bytes + , ptime const now, boost::uint32_t& min_rtt); + void write_payload(boost::uint8_t* ptr, int size); + void maybe_inc_acked_seq_nr(); + void ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr); + void write_sack(boost::uint8_t* buf, int size) const; + void incoming(boost::uint8_t const* buf, int size, packet* p, ptime now); + void do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now); + int packet_timeout() const; + bool test_socket_state(); + void maybe_trigger_receive_callback(); + void maybe_trigger_send_callback(); + bool cancel_handlers(error_code const& ec, bool kill); + bool consume_incoming_data( + utp_header const* ph, boost::uint8_t const* ptr, int payload_size, ptime now); + void update_mtu_limits(); + void experienced_loss(int seq_nr); + + void check_receive_buffers() const; + +#if TORRENT_USE_INVARIANT_CHECKS + void check_invariant() const; +#endif + + utp_socket_manager* m_sm; + + // userdata pointer passed along + // with any callback. This is initialized to 0 + // then set to point to the utp_stream when + // hooked up, and then reset to 0 once the utp_stream + // detaches. This is used to know whether or not + // the socket impl is still attached to a utp_stream + // object. When it isn't, we'll never be able to + // signal anything back to the client, and in case + // of errors, we just have to delete ourselves + // i.e. transition to the UTP_STATE_DELETED state + void* m_userdata; + + // This is a platform-independent replacement + // for the regular iovec type in posix. Since + // it's not used in any system call, we might as + // well define our own type instead of wrapping + // the system's type. + struct iovec_t + { + iovec_t(void* b, size_t l): buf(b), len(l) {} + void* buf; + size_t len; + }; + + // if there's currently an async read or write + // operation in progress, these buffers are initialized + // and used, otherwise any bytes received are stuck in + // m_receive_buffer until another read is made + // as we flush from the write buffer, individual iovecs + // are updated to only refer to unflushed portions of the + // buffers. Buffers that empty are erased from the vector. + std::vector m_write_buffer; + + // if this is non NULL, it's a packet. This packet was held off because + // of NAGLE. We couldn't send it immediately. It's left + // here to accrue more bytes before we send it. + packet* m_nagle_packet; + + // the user provided read buffer. If this has a size greater + // than 0, we'll always prefer using it over putting received + // data in the m_receive_buffer. As data is stored in the + // read buffer, the iovec_t elements are adjusted to only + // refer to the unwritten portions of the buffers, and the + // ones that fill up are erased from the vector + std::vector m_read_buffer; + + // packets we've received without a read operation + // active. Store them here until the client triggers + // an async_read_some + std::vector m_receive_buffer; + + // this is the error on this socket. If m_state is + // set to UTP_STATE_ERROR_WAIT, this error should be + // forwarded to the client as soon as we have a new + // async operation initiated + error_code m_error; + + // these are the callbacks made into the utp_stream object + // on read/write/connect events + utp_stream::handler_t m_read_handler; + utp_stream::handler_t m_write_handler; + utp_stream::connect_handler_t m_connect_handler; + + // the address of the remote endpoint + address m_remote_address; + + // the local address + address m_local_address; + + // the send and receive buffers + // maps packet sequence numbers + packet_buffer m_inbuf; + packet_buffer m_outbuf; + + // the time when the last packet we sent times out. Including re-sends. + // if we ever end up not having sent anything in one second ( + // or one mean rtt + 2 average deviations, whichever is greater) + // we set our cwnd to 1 MSS. This condition can happen either because + // a packet has timed out and needs to be resent or because our + // cwnd is set to less than one MSS during congestion control. + // it can also happen if the other end sends an advertized window + // size less than one MSS. + ptime m_timeout; + + // the last time we stepped the timestamp history + ptime m_last_history_step; + + // the max number of bytes in-flight. This is a fixed point + // value, to get the true number of bytes, shift right 16 bits + // the value is always >= 0, but the calculations performed on + // it in do_ledbat() are signed. + boost::int64_t m_cwnd; + + timestamp_history m_delay_hist; + timestamp_history m_their_delay_hist; + + // the slow-start threshold. This is the congestion window size (m_cwnd) + // in bytes the last time we left slow-start mode. This is used as a + // threshold to leave slow-start earlier next time, to avoid packet-loss + boost::int32_t m_ssthres; + + // the number of bytes we have buffered in m_inbuf + boost::int32_t m_buffered_incoming_bytes; + + // the timestamp diff in the last packet received + // this is what we'll send back + boost::uint32_t m_reply_micro; + + // this is the advertized receive window the other end sent + // we'll never have more un-acked bytes in flight + // if this ever gets set to zero, we'll try one packet every + // second until the window opens up again + boost::uint32_t m_adv_wnd; + + // the number of un-acked bytes we have sent + boost::int32_t m_bytes_in_flight; + + // the number of bytes read into the user provided + // buffer. If this grows too big, we'll trigger the + // read handler. + boost::int32_t m_read; + + // the sum of the lengths of all iovec in m_write_buffer + boost::int32_t m_write_buffer_size; + + // the number of bytes already written to packets + // from m_write_buffer + boost::int32_t m_written; + + // the sum of all packets stored in m_receive_buffer + boost::int32_t m_receive_buffer_size; + + // the sum of all buffers in m_read_buffer + boost::int32_t m_read_buffer_size; + + // max number of bytes to allocate for receive buffer + boost::int32_t m_in_buf_size; + + // this holds the 3 last delay measurements, + // these are the actual corrected delay measurements. + // the lowest of the 3 last ones is used in the congestion + // controller. This is to not completely close the cwnd + // by a single outlier. + enum { num_delay_hist = 3 }; + boost::uint32_t m_delay_sample_hist[num_delay_hist]; + + // counters + boost::uint32_t m_in_packets; + boost::uint32_t m_out_packets; + + // the last send delay sample + boost::int32_t m_send_delay; + // the last receive delay sample + boost::int32_t m_recv_delay; + + // average RTT + sliding_average<16> m_rtt; + + // port of destination endpoint + boost::uint16_t m_port; + + boost::uint16_t m_send_id; + boost::uint16_t m_recv_id; + + // this is the ack we're sending back. We have + // received all packets up to this sequence number + boost::uint16_t m_ack_nr; + + // the sequence number of the next packet + // we'll send + boost::uint16_t m_seq_nr; + + // this is the sequence number of the packet that + // everything has been ACKed up to. Everything we've + // sent up to this point has been received by the other + // end. + boost::uint16_t m_acked_seq_nr; + + // each packet gets one chance of "fast resend". i.e. + // if we have multiple duplicate acks, we may send a + // packet immediately, if m_fast_resend_seq_nr is set + // to that packet's sequence number + boost::uint16_t m_fast_resend_seq_nr; + + // this is the sequence number of the FIN packet + // we've received. This sequence number is only + // valid if m_eof is true. We should not accept + // any packets beyond this sequence number from the + // other end + boost::uint16_t m_eof_seq_nr; + + // this is the lowest sequence number that, when lost, + // will cause the window size to be cut in half + boost::uint16_t m_loss_seq_nr; + + // the max number of bytes we can send in a packet + // including the header + boost::uint16_t m_mtu; + + // the floor is the largest packet that we have + // been able to get through without fragmentation + boost::uint16_t m_mtu_floor; + + // the ceiling is the largest packet that we might + // be able to get through without fragmentation. + // i.e. ceiling +1 is very likely to not get through + // or we have in fact experienced a drop or ICMP + // message indicating that it is + boost::uint16_t m_mtu_ceiling; + + // the sequence number of the probe in-flight + // this is 0 if there is no probe in flight + boost::uint16_t m_mtu_seq; + + // this is a counter of how many times the current m_acked_seq_nr + // has been ACKed. If it's ACKed more than 3 times, we assume the + // packet with the next sequence number has been lost, and we trigger + // a re-send. Ovbiously an ACK only counts as a duplicate as long as + // we have outstanding packets following it. + boost::uint8_t m_duplicate_acks; + + // the number of packet timeouts we've seen in a row + // this affects the packet timeout time + boost::uint8_t m_num_timeouts; + + enum state_t { + // not yet connected + UTP_STATE_NONE, + // sent a syn packet, not received any acks + UTP_STATE_SYN_SENT, + // syn-ack received and in normal operation + // of sending and receiving data + UTP_STATE_CONNECTED, + // fin sent, but all packets up to the fin packet + // have not yet been acked. We might still be waiting + // for a FIN from the other end + UTP_STATE_FIN_SENT, + + // ====== states beyond this point ===== + // === are considered closing states === + // === and will cause the socket to ==== + // ============ be deleted ============= + + // the socket has been gracefully disconnected + // and is waiting for the client to make a + // socket call so that we can communicate this + // fact and actually delete all the state, or + // there is an error on this socket and we're + // waiting to communicate this to the client in + // a callback. The error in either case is stored + // in m_error. If the socket has gracefully shut + // down, the error is error::eof. + UTP_STATE_ERROR_WAIT, + + // there are no more references to this socket + // and we can delete it + UTP_STATE_DELETE + }; + + // this is the cursor into m_delay_sample_hist + boost::uint8_t m_delay_sample_idx:2; + + // the state the socket is in + boost::uint8_t m_state:3; + + // this is set to true when we receive a fin + bool m_eof:1; + + // is this socket state attached to a user space socket? + bool m_attached:1; + + // this is true if nagle is enabled (which it is by default) + bool m_nagle:1; + + // this is true while the socket is in slow start mode. It's + // only in slow-start during the start-up phase. Slow start + // (contrary to what its name suggest) means that we're growing + // the congestion window (cwnd) exponetially rather than linearly. + // this is done at startup of a socket in order to find its + // link capacity faster. This behaves similar to TCP slow start + bool m_slow_start:1; + + // this is true as long as we have as many packets in + // flight as allowed by the congestion window (cwnd) + bool m_cwnd_full:1; + + // this is set to true when this socket has added itself to + // the utp socket manager's list of deferred acks. Once the + // burst of incoming UDP packets is all drained, the utp socket + // manager will send acks for all sockets on this list. + bool m_deferred_ack:1; + + // this is true if this socket has subscribed to be notified + // when this receive round is done + bool m_subscribe_drained:1; + + // if this socket tries to send a packet via the utp socket + // manager, and it fails with EWOULDBLOCK, the socket + // is stalled and this is set. It's also added to a list + // of sockets in the utp_socket_manager to be notified of + // the socket being writable again + bool m_stalled:1; +}; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +int socket_impl_size() { return sizeof(utp_socket_impl); } +#endif + +utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id + , boost::uint16_t send_id, void* userdata + , utp_socket_manager* sm) +{ + return new utp_socket_impl(recv_id, send_id, userdata, sm); +} + +void detach_utp_impl(utp_socket_impl* s) +{ + s->detach(); +} + +void delete_utp_impl(utp_socket_impl* s) +{ + delete s; +} + +bool should_delete(utp_socket_impl* s) +{ + return s->should_delete(); +} + +void tick_utp_impl(utp_socket_impl* s, ptime const& now) +{ + s->tick(now); +} + +void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu) +{ + s->init_mtu(link_mtu, utp_mtu); +} + +bool utp_incoming_packet(utp_socket_impl* s, char const* p + , int size, udp::endpoint const& ep, ptime receive_time) +{ + return s->incoming_packet((boost::uint8_t const*)p, size, ep, receive_time); +} + +bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id) +{ + return s->m_remote_address == ep.address() + && s->m_port == ep.port() + && s->m_recv_id == id; +} + +udp::endpoint utp_remote_endpoint(utp_socket_impl* s) +{ + return udp::endpoint(s->m_remote_address, s->m_port); +} + +boost::uint16_t utp_receive_id(utp_socket_impl* s) +{ + return s->m_recv_id; +} + +void utp_writable(utp_socket_impl* s) +{ + TORRENT_ASSERT(s->m_stalled); + s->m_stalled = false; + s->writable(); +} + +void utp_send_ack(utp_socket_impl* s) +{ + TORRENT_ASSERT(s->m_deferred_ack); + s->m_deferred_ack = false; + s->send_pkt(utp_socket_impl::pkt_ack); +} + +void utp_socket_drained(utp_socket_impl* s) +{ + s->m_subscribe_drained = false; + + // at this point, we know we won't receive any + // more packets this round. So, we may want to + // call the receive callback function to + // let the user consume it + + s->maybe_trigger_receive_callback(); + s->maybe_trigger_send_callback(); +} + +void utp_socket_impl::update_mtu_limits() +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(m_mtu_floor <= m_mtu_ceiling); + m_mtu = (m_mtu_floor + m_mtu_ceiling) / 2; + + if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) << 16; + + UTP_LOGV("%8p: updating MTU to: %d [%d, %d]\n" + , this, m_mtu, m_mtu_floor, m_mtu_ceiling); + + // clear the mtu probe sequence number since + // it was either dropped or acked + m_mtu_seq = 0; +} + +int utp_socket_state(utp_socket_impl const* s) +{ + return s->m_state; +} + +int utp_stream::send_delay() const +{ + return m_impl ? m_impl->m_send_delay : 0; +} + +int utp_stream::recv_delay() const +{ + return m_impl ? m_impl->m_recv_delay : 0; +} + +utp_stream::utp_stream(asio::io_service& io_service) + : m_io_service(io_service) + , m_impl(0) + , m_open(false) +{ +} + +utp_socket_impl* utp_stream::get_impl() +{ + return m_impl; +} + +void utp_stream::close() +{ + if (!m_impl) return; + if (!m_impl->destroy()) + { + if (!m_impl) return; + detach_utp_impl(m_impl); + m_impl = 0; + } +} + +std::size_t utp_stream::available() const +{ + return m_impl->available(); +} + +utp_stream::endpoint_type utp_stream::remote_endpoint(error_code& ec) const +{ + if (!m_impl) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return m_impl->remote_endpoint(ec); +} + +utp_stream::endpoint_type utp_stream::local_endpoint(error_code& ec) const +{ + if (m_impl == 0 || m_impl->m_sm == 0) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return tcp::endpoint(m_impl->m_local_address, m_impl->m_sm->local_port(ec)); +} + +utp_stream::~utp_stream() +{ + if (m_impl) + { + UTP_LOGV("%8p: utp_stream destructed\n", m_impl); + m_impl->destroy(); + detach_utp_impl(m_impl); + } + + m_impl = 0; +} + +void utp_stream::set_impl(utp_socket_impl* impl) +{ + TORRENT_ASSERT(m_impl == 0); + TORRENT_ASSERT(!m_open); + m_impl = impl; + m_open = true; +} + +int utp_stream::read_buffer_size() const +{ + TORRENT_ASSERT(m_impl); + return m_impl->m_receive_buffer_size; +} + +void utp_stream::on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling read handler read:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_read_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_read_handler, ec, bytes_transferred)); + s->m_read_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling write handler written:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_write_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_write_handler, ec, bytes_transferred)); + s->m_write_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_connect(void* self, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + TORRENT_ASSERT(s); + + UTP_LOGV("%8p: calling connect handler ec:%s kill:%d\n" + , s->m_impl, ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_connect_handler); + s->m_io_service.post(boost::bind(s->m_connect_handler, ec)); + s->m_connect_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::add_read_buffer(void* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + m_impl->m_read_buffer.push_back(utp_socket_impl::iovec_t(buf, len)); + m_impl->m_read_buffer_size += len; + + UTP_LOGV("%8p: add_read_buffer %d bytes\n", m_impl, int(len)); +} + +// this is the wrapper to add a user provided write buffer to the +// utp_socket_impl. It makes sure the m_write_buffer_size is kept +// up to date +void utp_stream::add_write_buffer(void const* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + m_impl->m_write_buffer.push_back(utp_socket_impl::iovec_t((void*)buf, len)); + m_impl->m_write_buffer_size += len; + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + UTP_LOGV("%8p: add_write_buffer %d bytes\n", m_impl, int(len)); +} + +// this is called when all user provided read buffers have been added +// and it's time to execute the async operation. The first thing we +// do is to copy any data stored in m_receive_buffer into the user +// provided buffer. This might be enough to in turn trigger the read +// handler immediately. +void utp_stream::set_read_handler(handler_t h) +{ + TORRENT_ASSERT(m_impl->m_userdata); + m_impl->m_read_handler = h; + if (m_impl->test_socket_state()) return; + + UTP_LOGV("%8p: new read handler. %d bytes in buffer\n" + , m_impl, m_impl->m_receive_buffer_size); + + TORRENT_ASSERT(m_impl->m_read_buffer_size > 0); + + // so, the client wants to read. If we already + // have some data in the read buffer, move it into the + // client's buffer right away + + m_impl->m_read += read_some(false); + m_impl->maybe_trigger_receive_callback(); +} + +size_t utp_stream::read_some(bool clear_buffers) +{ + if (m_impl->m_receive_buffer_size == 0) + { + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + return 0; + } + + std::vector::iterator target = m_impl->m_read_buffer.begin(); + + size_t ret = 0; + + int pop_packets = 0; + for (std::vector::iterator i = m_impl->m_receive_buffer.begin() + , end(m_impl->m_receive_buffer.end()); i != end;) + { + if (target == m_impl->m_read_buffer.end()) + { + UTP_LOGV(" No more target buffers: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + TORRENT_ASSERT(m_impl->m_read_buffer.empty()); + break; + } + + m_impl->check_receive_buffers(); + + packet* p = *i; + int to_copy = (std::min)(p->size - p->header_size, int(target->len)); + TORRENT_ASSERT(to_copy >= 0); + memcpy(target->buf, p->buf + p->header_size, to_copy); + ret += to_copy; + target->buf = ((char*)target->buf) + to_copy; + TORRENT_ASSERT(int(target->len) >= to_copy); + target->len -= to_copy; + m_impl->m_receive_buffer_size -= to_copy; + TORRENT_ASSERT(m_impl->m_read_buffer_size >= to_copy); + m_impl->m_read_buffer_size -= to_copy; + p->header_size += to_copy; + if (target->len == 0) target = m_impl->m_read_buffer.erase(target); + + m_impl->check_receive_buffers(); + + TORRENT_ASSERT(m_impl->m_receive_buffer_size >= 0); + + // Consumed entire packet + if (p->header_size == p->size) + { + free(p); + ++pop_packets; + *i = 0; + ++i; + } + + if (m_impl->m_receive_buffer_size == 0) + { + UTP_LOGV(" Didn't fill entire target: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + break; + } + } + // remove the packets from the receive_buffer that we already copied over + // and freed + m_impl->m_receive_buffer.erase(m_impl->m_receive_buffer.begin() + , m_impl->m_receive_buffer.begin() + pop_packets); + // we exited either because we ran out of bytes to copy + // or because we ran out of space to copy the bytes to + TORRENT_ASSERT(m_impl->m_receive_buffer_size == 0 + || m_impl->m_read_buffer.empty()); + + UTP_LOGV("%8p: %d packets moved from buffer to user space (%d bytes)\n" + , m_impl, pop_packets, int(ret)); + + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + TORRENT_ASSERT(ret > 0); + return ret; +} + +// this is called when all user provided write buffers have been +// added. Start trying to send packets with the payload immediately. +void utp_stream::set_write_handler(handler_t h) +{ + UTP_LOGV("%8p: new write handler. %d bytes to write\n" + , m_impl, m_impl->m_write_buffer_size); + + TORRENT_ASSERT(m_impl->m_write_buffer_size > 0); + + TORRENT_ASSERT(m_impl->m_userdata); + m_impl->m_write_handler = h; + m_impl->m_written = 0; + if (m_impl->test_socket_state()) return; + + // try to write. send_pkt returns false if there's + // no more payload to send or if the congestion window + // is full and we can't send more packets right now + while (m_impl->send_pkt()); + + // if there was an error in send_pkt(), m_impl may be + // 0 at this point + if (m_impl) m_impl->maybe_trigger_send_callback(); +} + +void utp_stream::do_connect(tcp::endpoint const& ep, utp_stream::connect_handler_t handler) +{ + int link_mtu, utp_mtu; + m_impl->m_sm->mtu_for_dest(ep.address(), link_mtu, utp_mtu); + m_impl->init_mtu(link_mtu, utp_mtu); + TORRENT_ASSERT(m_impl->m_connect_handler == 0); + m_impl->m_remote_address = ep.address(); + m_impl->m_port = ep.port(); + m_impl->m_connect_handler = handler; + + error_code ec; + m_impl->m_local_address = m_impl->m_sm->local_endpoint(m_impl->m_remote_address, ec).address(); + + if (m_impl->test_socket_state()) return; + m_impl->send_syn(); +} + +// =========== utp_socket_impl ============ + +utp_socket_impl::~utp_socket_impl() +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_attached); + TORRENT_ASSERT(!m_deferred_ack); + + UTP_LOGV("%8p: destroying utp socket state\n", this); + + // free any buffers we're holding + for (boost::uint16_t i = m_inbuf.cursor(), end((m_inbuf.cursor() + + m_inbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_inbuf.remove(i); + free(p); + } + for (boost::uint16_t i = m_outbuf.cursor(), end((m_outbuf.cursor() + + m_outbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_outbuf.remove(i); + free(p); + } + + for (std::vector::iterator i = m_receive_buffer.begin() + , end = m_receive_buffer.end(); i != end; ++i) + { + free(*i); + } + + free(m_nagle_packet); + m_nagle_packet = NULL; +} + +bool utp_socket_impl::should_delete() const +{ + INVARIANT_CHECK; + + // if the socket state is not attached anymore we're free + // to delete it from the client's point of view. The other + // endpoint however might still need to be told that we're + // closing the socket. Only delete the state if we're not + // attached and we're in a state where the other end doesn't + // expect the socket to still be alive + // when m_stalled is true, it means the socket manager has a + // pointer to this socket, waiting for the UDP socket to + // become writable again. We have to wait for that, so that + // the pointer is removed from that queue. Otherwise we would + // leave a dangling pointer in the socket manager + bool ret = (m_state >= UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE) + && !m_attached && !m_stalled; + + if (ret) + { + UTP_LOGV("%8p: should_delete() = true\n", this); + } + + return ret; +} + +void utp_socket_impl::maybe_trigger_receive_callback() +{ + INVARIANT_CHECK; + + // nothing has been read or there's no outstanding read operation + if (m_read == 0 || m_read_handler == 0) return; + + UTP_LOGV("%8p: calling read handler read:%d\n", this, m_read); + m_read_handler(m_userdata, m_read, m_error, false); + m_read_handler = 0; + m_read = 0; + m_read_buffer_size = 0; + m_read_buffer.clear(); +} + +void utp_socket_impl::maybe_trigger_send_callback() +{ + INVARIANT_CHECK; + + // nothing has been written or there's no outstanding write operation + if (m_written == 0 || m_write_handler == 0) return; + + UTP_LOGV("%8p: calling write handler written:%d\n", this, m_written); + + m_write_handler(m_userdata, m_written, m_error, false); + m_write_handler = 0; + m_written = 0; + m_write_buffer_size = 0; + m_write_buffer.clear(); +} + +bool utp_socket_impl::destroy() +{ + INVARIANT_CHECK; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: destroy state:%s\n", this, socket_state_names[m_state]); +#endif + + if (m_userdata == 0) return false; + + if (m_state == UTP_STATE_CONNECTED) + send_fin(); + + bool cancelled = cancel_handlers(asio::error::operation_aborted, true); + + m_userdata = 0; + + m_read_buffer.clear(); + m_read_buffer_size = 0; + + m_write_buffer.clear(); + m_write_buffer_size = 0; + + if ((m_state == UTP_STATE_ERROR_WAIT + || m_state == UTP_STATE_NONE + || m_state == UTP_STATE_SYN_SENT) && cancelled) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + } + + return cancelled; + + // #error our end is closing. Wait for everything to be acked +} + +void utp_socket_impl::detach() +{ + INVARIANT_CHECK; + + UTP_LOGV("%8p: detach()\n", this); + m_attached = false; +} + +void utp_socket_impl::send_syn() +{ + INVARIANT_CHECK; + + m_seq_nr = random() & 0xffff; + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + m_ack_nr = 0; + m_fast_resend_seq_nr = m_seq_nr; + + packet* p = (packet*)malloc(sizeof(packet) + sizeof(utp_header)); + p->size = sizeof(utp_header); + p->header_size = sizeof(utp_header); + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + h->type_ver = (ST_SYN << 4) | 1; + h->extension = 0; + // using recv_id here is intentional! This is an odd + // thing in uTP. The syn packet is sent with the connection + // ID that it expects to receive the syn ack on. All + // subsequent connection IDs will be this plus one. + h->connection_id = m_recv_id; + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = 0; + h->seq_nr = m_seq_nr; + h->ack_nr = 0; + + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time()) & 0xffffffff); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: send_syn seq_nr:%d id:%d target:%s\n" + , this, int(m_seq_nr), int(m_recv_id) + , print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str()); +#endif + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)h + , sizeof(utp_header), ec); + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + free(p); + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + if (!m_stalled) + ++p->num_transmissions; + + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); + m_outbuf.insert(m_seq_nr, p); + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + TORRENT_ASSERT(p->buf == (boost::uint8_t*)h); + + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_SYN_SENT; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +// if a send ever failed with EWOULDBLOCK, we +// subscribe to the udp socket and will be +// signalled with this function. +void utp_socket_impl::writable() +{ +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: writable\n", this); +#endif + if (should_delete()) return; + + while(send_pkt()); + + maybe_trigger_send_callback(); +} + +void utp_socket_impl::send_fin() +{ + INVARIANT_CHECK; + + send_pkt(pkt_fin); + // unless there was an error, we're now + // in FIN-SENT state + if (!m_error) + m_state = UTP_STATE_FIN_SENT; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +void utp_socket_impl::send_reset(utp_header* ph) +{ + INVARIANT_CHECK; + + utp_header h; + h.type_ver = (ST_RESET << 4) | 1; + h.extension = 0; + h.connection_id = m_send_id; + h.timestamp_difference_microseconds = m_reply_micro; + h.wnd_size = 0; + h.seq_nr = random() & 0xffff; + h.ack_nr = ph->seq_nr; + ptime now = time_now_hires(); + h.timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + + UTP_LOGV("%8p: send_reset seq_nr:%d id:%d ack_nr:%d\n" + , this, int(h.seq_nr), int(m_send_id), int(ph->seq_nr)); + + // ignore errors here + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)&h, sizeof(h), ec); +} + +std::size_t utp_socket_impl::available() const +{ + return m_receive_buffer_size; +} + +void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr + , int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt) +{ + INVARIANT_CHECK; + + if (size == 0) return; + + // this is the sequence number the current bit represents + int ack_nr = (packet_ack + 2) & ACK_MASK; + +#if TORRENT_VERBOSE_UTP_LOG + std::string bitmask; + bitmask.reserve(size); + for (boost::uint8_t const* b = ptr, *end = ptr + size; b != end; ++b) + { + unsigned char bitfield = unsigned(*b); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + bitmask += (mask & bitfield) ? "1" : "0"; + mask <<= 1; + } + } + UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u\n" + , this, ack_nr, bitmask.c_str(), m_seq_nr); +#endif + + // the number of acked packets past the fast re-send sequence number + // this is used to determine if we should trigger more fast re-sends + int dups = 0; + + // the sequence number of the last ACKed packet + int last_ack = packet_ack; + + // for each byte + for (boost::uint8_t const* end = ptr + size; ptr != end; ++ptr) + { + unsigned char bitfield = unsigned(*ptr); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + if (mask & bitfield) + { + last_ack = ack_nr; + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + if (compare_less_wrap(m_fast_resend_seq_nr, ack_nr, ACK_MASK)) ++dups; + // this bit was set, ack_nr was received + packet* p = (packet*)m_outbuf.remove(ack_nr); + if (p) + { + *acked_bytes += p->size - p->header_size; + // each ACKed packet counts as a duplicate ack + UTP_LOGV("%8p: duplicate_acks:%u fast_resend_seq_nr:%u\n" + , this, m_duplicate_acks, m_fast_resend_seq_nr); + ack_packet(p, now, min_rtt, ack_nr); + } + else + { + // this packet might have been acked by a previous + // selective ack + maybe_inc_acked_seq_nr(); + } + } + + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + + // we haven't sent packets past this point. + // if there are any more bits set, we have to + // ignore them anyway + if (ack_nr == m_seq_nr) break; + } + if (ack_nr == m_seq_nr) break; + } + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // we received more than dup_ack_limit ACKs in this SACK message. + // trigger fast re-send + if (dups >= dup_ack_limit && compare_less_wrap(m_fast_resend_seq_nr, last_ack, ACK_MASK)) + { + experienced_loss(m_fast_resend_seq_nr); + int num_resent = 0; + while (m_fast_resend_seq_nr != last_ack) + { + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + if (!p) continue; + ++num_resent; + if (!resend_packet(p, true)) break; + m_duplicate_acks = 0; + if (num_resent >= sack_resend_limit) break; + } + } +} + +// copies data from the write buffer into the packet +// pointed to by ptr +void utp_socket_impl::write_payload(boost::uint8_t* ptr, int size) +{ + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif + TORRENT_ASSERT(!m_write_buffer.empty() || size == 0); + TORRENT_ASSERT(m_write_buffer_size >= size); + std::vector::iterator i = m_write_buffer.begin(); + + if (size == 0) return; + + int buffers_to_clear = 0; + while (size > 0) + { + // i points to the iovec we'll start copying from + int to_copy = (std::min)(size, int(i->len)); + TORRENT_ASSERT(to_copy >= 0); + TORRENT_ASSERT(to_copy < INT_MAX / 2 && m_written < INT_MAX / 2); + memcpy(ptr, static_cast(i->buf), to_copy); + size -= to_copy; + m_written += to_copy; + ptr += to_copy; + i->len -= to_copy; + TORRENT_ASSERT(m_write_buffer_size >= to_copy); + m_write_buffer_size -= to_copy; + ((char const*&)i->buf) += to_copy; + if (i->len == 0) ++buffers_to_clear; + ++i; + } + + if (buffers_to_clear) + m_write_buffer.erase(m_write_buffer.begin() + , m_write_buffer.begin() + buffers_to_clear); + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif +} + +void utp_socket_impl::subscribe_drained() +{ + INVARIANT_CHECK; + + if (m_subscribe_drained) return; + + UTP_LOGV("%8p: subscribe drained\n", this); + m_subscribe_drained = true; + m_sm->subscribe_drained(this); +} + +void utp_socket_impl::defer_ack() +{ + INVARIANT_CHECK; + + if (m_deferred_ack) return; + + UTP_LOGV("%8p: defer ack\n", this); + m_deferred_ack = true; + m_sm->defer_ack(this); +} + +void utp_socket_impl::remove_sack_header(packet* p) +{ + INVARIANT_CHECK; + + // remove the sack header + boost::uint8_t* ptr = p->buf + sizeof(utp_header); + utp_header* h = (utp_header*)p->buf; + + TORRENT_ASSERT(h->extension == 1); + + h->extension = ptr[0]; + int sack_size = ptr[1]; + TORRENT_ASSERT(h->extension == 0); + + UTP_LOGV("%8p: removing SACK header, %d bytes\n" + , this, sack_size + 2); + + TORRENT_ASSERT(p->size >= p->header_size); + TORRENT_ASSERT(p->header_size >= sizeof(utp_header) + sack_size + 2); + memmove(ptr, ptr + sack_size + 2, p->size - p->header_size); + p->header_size -= sack_size + 2; + p->size -= sack_size + 2; +} + +struct holder +{ + holder(char* buf = NULL): m_buf(buf) {} + ~holder() { free(m_buf); } + + void reset(char* buf) + { + free(m_buf); + m_buf = buf; + } + + char* release() + { + char* ret = m_buf; + m_buf = NULL; + return ret; + } + +private: + + char* m_buf; +}; + +// sends a packet, pulls data from the write buffer (if there's any) +// if ack is true, we need to send a packet regardless of if there's +// any data. Returns true if we could send more data (i.e. call +// send_pkt() again) +// returns true if there is more space for payload in our +// congestion window, false if there is no more space. +bool utp_socket_impl::send_pkt(int flags) +{ + INVARIANT_CHECK; + + bool force = (flags & pkt_ack) || (flags & pkt_fin); + +// TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT || (flags & pkt_ack)); + + // first see if we need to resend any packets + + // TODO: this loop may not be very efficient + for (int i = (m_acked_seq_nr + 1) & ACK_MASK; i != m_seq_nr; i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (!p->need_resend) continue; + if (!resend_packet(p)) + { + // we couldn't resend the packet. It probably doesn't + // fit in our cwnd. If force is set, we need to continue + // to send our packet anyway, if we don't have force set, + // we might as well return + if (!force) return false; + // resend_packet might have failed + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return false; + break; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == i) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + } + + int sack = 0; + if (m_inbuf.size()) + { + // the SACK bitfield should ideally fit all + // the pieces we have successfully received + sack = (m_inbuf.span() + 7) / 8; + if (sack > 32) sack = 32; + } + + int header_size = sizeof(utp_header) + (sack ? sack + 2 : 0); + int payload_size = m_write_buffer_size; + if (m_mtu - header_size < payload_size) + payload_size = m_mtu - header_size; + + // if we have one MSS worth of data, make sure it fits in our + // congestion window and the advertized receive window from + // the other end. + if (m_bytes_in_flight + payload_size > (std::min)(int(m_cwnd >> 16) + , int(m_adv_wnd - m_bytes_in_flight))) + { + // this means there's not enough room in the send window for + // another packet. We have to hold off sending this data. + // we still need to send an ACK though + // if we're trying to send a FIN, make an exception + if ((flags & pkt_fin) == 0) payload_size = 0; + + // we're constrained by the window size + m_cwnd_full = true; + + UTP_LOGV("%8p: no space in window send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); + + if (!force) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: skipping send seq_nr:%d ack_nr:%d " + "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, int(m_seq_nr), int(m_ack_nr) + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); +#endif + return false; + } + } + + // if we don't have any data to send, or can't send any data + // and we don't have any data to force, don't send a packet + if (payload_size == 0 && !force && !m_nagle_packet) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: skipping send (no payload and no force) seq_nr:%d ack_nr:%d " + "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, int(m_seq_nr), int(m_ack_nr) + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , header_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); +#endif + return false; + } + + int packet_size = header_size + payload_size; + + packet* p = NULL; + boost::uint8_t* ptr = NULL; + utp_header* h = NULL; + +#if TORRENT_USE_ASSERTS + bool stack_alloced = false; +#endif + + // used to free the packet buffer in case we exit the + // function early + holder buf_holder; + + // payload size being zero means we're just sending + // an force. We should not pick up the nagle packet + if (!m_nagle_packet || (payload_size == 0 && force)) + { + // we only need a heap allocation if we have payload and + // need to keep the packet around (in the outbuf) + if (payload_size) + { + p = (packet*)malloc(sizeof(packet) + m_mtu); + p->allocated = m_mtu; + buf_holder.reset((char*)p); + + m_sm->inc_stats_counter(utp_socket_manager::payload_pkts_out); + } + else + { +#if TORRENT_USE_ASSERTS + stack_alloced = true; +#endif + TORRENT_ASSERT(force); + // this alloca() statement won't necessarily produce + // correctly aligned memory. That's why we ask for 7 more bytes + // and adjust our pointer to be aligned later + p = (packet*)TORRENT_ALLOCA(char, sizeof(packet) + packet_size + + sizeof(packet*) - 1); + p = (packet*)align_pointer(p); + UTP_LOGV("%8p: allocating %d bytes on the stack\n", this, packet_size); + p->allocated = packet_size; + } + + p->size = packet_size; + p->header_size = packet_size - payload_size; + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + ptr = p->buf; + h = (utp_header*)ptr; + ptr += sizeof(utp_header); + + h->extension = sack ? 1 : 0; + h->connection_id = m_send_id; + // seq_nr is ignored for ST_STATE packets, so it doesn't + // matter that we say this is a sequence number we haven't + // actually sent yet + h->seq_nr = m_seq_nr; + h->type_ver = ((payload_size ? ST_DATA : ST_STATE) << 4) | 1; + + write_payload(p->buf + p->header_size, payload_size); + } + else + { + // pick up the nagle packet and keep adding bytes to it + p = m_nagle_packet; + + ptr = p->buf + sizeof(utp_header); + h = (utp_header*)p->buf; + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + + // if the packet has a selective force header, we'll need + // to update it + if (h->extension == 1) + { + sack = ptr[1]; + // if we no longer have any out-of-order packets waiting + // to be delivered, there's no selective ack to be sent. + if (m_inbuf.size() == 0) + { + // we need to remove the sack header + remove_sack_header(p); + sack = 0; + } + } + else + sack = 0; + + boost::int32_t size_left = p->allocated - p->size; + TORRENT_ASSERT(size_left > 0); + size_left = (std::min)(size_left, m_write_buffer_size); + write_payload(p->buf + p->size, size_left); + p->size += size_left; + + UTP_LOGV("%8p: NAGLE appending %d bytes to nagle packet. new size: %d allocated: %d\n" + , this, size_left, p->size, p->allocated); + + // did we fill up the whole mtu? + // if we didn't, we may still send it if there's + // no bytes in flight + if (m_bytes_in_flight > 0 + && p->size < p->allocated + && !force + && m_nagle) + { + return false; + } + + // clear the nagle packet pointer and fall through + // sending p + m_nagle_packet = NULL; + + packet_size = p->size; + payload_size = p->size - p->header_size; + } + + if (sack) + { + *ptr++ = 0; // end of extension chain + *ptr++ = sack; // bytes for SACK bitfield + write_sack(ptr, sack); + ptr += sack; + TORRENT_ASSERT(ptr <= p->buf + p->header_size); + } + + if (m_bytes_in_flight > 0 + && p->size < p->allocated + && !force + && m_nagle) + { + // this is nagle. If we don't have a full packet + // worth of payload to send AND we have at least + // one outstanding packet, hold off. Once the + // outstanding packet is acked, we'll send this + // payload + UTP_LOGV("%8p: NAGLE not enough payload send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu); + TORRENT_ASSERT(m_nagle_packet == NULL); + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + m_nagle_packet = p; + buf_holder.release(); + return false; + } + + // MTU DISCOVERY + if (m_mtu_seq == 0 + && p->size > m_mtu_floor + && m_seq_nr != 0) + { + p->mtu_probe = true; + m_mtu_seq = m_seq_nr; + } + else + { + p->mtu_probe = false; + } + + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = (std::max)(m_in_buf_size - m_buffered_incoming_bytes + - m_receive_buffer_size, boost::int32_t(0)); + h->ack_nr = m_ack_nr; + + // if this is a FIN packet, override the type + if (flags & pkt_fin) + h->type_ver = (ST_FIN << 4) | 1; + + // fill in the timestamp as late as possible + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t( + total_microseconds(now - min_time()) & 0xffffffff); + +#if TORRENT_UTP_LOG + UTP_LOG("%8p: sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u " + "mtu_probe:%d extension:%d\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , p->size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds), int(p->mtu_probe) + , h->extension); +#endif + + error_code ec; +#ifdef TORRENT_DEBUG + // simulate 1% packet loss +// if ((rand() % 100) > 0) +#endif + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, p->size, ec + , p->mtu_probe ? utp_socket_manager::dont_fragment : 0); + + ++m_out_packets; + m_sm->inc_stats_counter(utp_socket_manager::packets_out); + + if (ec == error::message_size) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: error sending packet: %s\n", this, ec.message().c_str()); +#endif + // if we fail even though this is not a probe, we're screwed + // since we'd have to repacketize + TORRENT_ASSERT(p->mtu_probe); + m_mtu_ceiling = p->size - 1; + if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; + update_mtu_limits(); + // resend the packet immediately without + // it being an MTU probe + p->mtu_probe = false; + if (m_mtu_seq == m_ack_nr) + m_mtu_seq = 0; + ec.clear(); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: re-sending\n", this); +#endif + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, p->size, ec, 0); + } + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + TORRENT_ASSERT(stack_alloced != bool(payload_size)); + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return false; + } + + if (!m_stalled) + ++p->num_transmissions; + + // if we have payload, we need to save the packet until it's acked + // and progress m_seq_nr + if (p->size > p->header_size) + { + // if we're sending a payload packet, there should not + // be a nagle packet waiting for more data + TORRENT_ASSERT(m_nagle_packet == NULL); + +#if !TORRENT_UT_SEQ + // if the other end closed the connection immediately + // our FIN packet will end up having the same sequence + // number as the SYN, so this assert is invalid + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); +#endif + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + + // release the buffer, we're saving it in the circular + // buffer of outgoing packets + buf_holder.release(); + packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); + if (old) + { + TORRENT_ASSERT(((utp_header*)old->buf)->seq_nr == m_seq_nr); + if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; + free(old); + } + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + TORRENT_ASSERT(payload_size >= 0); + m_bytes_in_flight += p->size - p->header_size; + } + else + { + TORRENT_ASSERT(h->seq_nr == m_seq_nr); + } + + // if the socket is stalled, always return false, don't + // try to write more packets. We'll keep writing once + // the underlying UDP socket becomes writable + return m_write_buffer_size > 0 && !m_cwnd_full && !m_stalled; +} + +// size is in bytes +void utp_socket_impl::write_sack(boost::uint8_t* buf, int size) const +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(m_inbuf.size()); + int ack_nr = (m_ack_nr + 2) & ACK_MASK; + boost::uint8_t* end = buf + size; + + for (; buf != end; ++buf) + { + *buf = 0; + int mask = 1; + for (int i = 0; i < 8; ++i) + { + if (m_inbuf.at(ack_nr)) *buf |= mask; + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + } + } +} + +bool utp_socket_impl::resend_packet(packet* p, bool fast_resend) +{ + INVARIANT_CHECK; + + // for fast re-sends the packet hasn't been marked as needing resending + TORRENT_ASSERT(p->need_resend || fast_resend); + + if (m_error) return false; + + if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + m_mtu_seq = 0; + p->mtu_probe = false; + // we got multiple acks for the packet before our probe, assume + // it was dropped because it was too big + m_mtu_ceiling = p->size - 1; + update_mtu_limits(); + } + + // we can only resend the packet if there's + // enough space in our congestion window + // since we can't re-packetize, some packets that are + // larger than the congestion window must be allowed through + // but only if we don't have any outstanding bytes + int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - m_bytes_in_flight; + if (!fast_resend + && p->size - p->header_size > window_size_left + && m_bytes_in_flight > 0) + { + m_cwnd_full = true; + return false; + } + + // plus one since we have fast-resend as well, which doesn't + // necessarily trigger by a timeout + TORRENT_ASSERT(p->num_transmissions < m_sm->num_resends() + 1); + + TORRENT_ASSERT(p->size - p->header_size >= 0); + if (p->need_resend) m_bytes_in_flight += p->size - p->header_size; + + m_sm->inc_stats_counter(utp_socket_manager::packet_resend); + if (fast_resend) m_sm->inc_stats_counter(utp_socket_manager::fast_retransmit); + +#ifdef TORRENT_DEBUG + if (fast_resend) ++p->num_fast_resend; +#endif + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + // update packet header + h->timestamp_difference_microseconds = m_reply_micro; + p->send_time = time_now_hires(); + h->timestamp_microseconds = boost::uint32_t( + total_microseconds(p->send_time - min_time()) & 0xffffffff); + + // if the packet has a selective ack header, we'll need + // to update it + if (h->extension == 1 && h->ack_nr != m_ack_nr) + { + boost::uint8_t* ptr = p->buf + sizeof(utp_header); + int sack_size = ptr[1]; + if (m_inbuf.size()) + { + // update the sack header + write_sack(ptr + 2, sack_size); + TORRENT_ASSERT(ptr + sack_size + 2 <= p->buf + p->header_size); + } + else + { + remove_sack_header(p); + } + } + + h->ack_nr = m_ack_nr; + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)p->buf, p->size, ec); + ++m_out_packets; + m_sm->inc_stats_counter(utp_socket_manager::packets_out); + + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: re-sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , p->size, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds)); +#endif + + if (ec == error::would_block || ec == error::try_again) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: socket stalled\n", this); +#endif + if (!m_stalled) + { + m_stalled = true; + m_sm->subscribe_writable(this); + } + } + else if (ec) + { + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return false; + } + + if (!m_stalled) + ++p->num_transmissions; + + return !m_stalled; +} + +void utp_socket_impl::experienced_loss(int seq_nr) +{ + INVARIANT_CHECK; + + // since loss often comes in bursts, we only cut the + // window in half once per RTT. This is implemented + // by limiting which packets can cause us to cut the + // window size. The first packet that's lost will + // update the limit to the last sequence number we sent. + // i.e. only packet sent after this loss can cause another + // window size cut. The +1 is to turn the comparison into + // less than or equal to. If we experience loss of the + // same packet again, ignore it. + if (compare_less_wrap(seq_nr, m_loss_seq_nr + 1, ACK_MASK)) return; + + // if we happen to be in slow-start mode, we need to leave it + if (m_slow_start) + { + m_ssthres = m_cwnd >> 16; + m_slow_start = false; + UTP_LOGV("%8p: experienced loss, slow_start -> 0\n", this); + } + + // cut window size in 2 + m_cwnd = (std::max)(m_cwnd * m_sm->loss_multiplier() / 100, boost::int64_t(m_mtu << 16)); + m_loss_seq_nr = m_seq_nr; + UTP_LOGV("%8p: Lost packet %d caused cwnd cut\n", this, seq_nr); + + // the window size could go below one MMS here, if it does, + // we'll get a timeout in about one second + + m_sm->inc_stats_counter(utp_socket_manager::packet_loss); +} + +void utp_socket_impl::maybe_inc_acked_seq_nr() +{ + INVARIANT_CHECK; + + bool incremented = false; + // don't pass m_seq_nr, since we move into sequence + // numbers that haven't been sent yet, and aren't + // supposed to be in m_outbuf + // if the slot in m_outbuf is 0, it means the + // packet has been ACKed and removed from the send buffer + while (((m_acked_seq_nr + 1) & ACK_MASK) != m_seq_nr + && m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) == 0) + { + // increment the fast resend sequence number + if (m_fast_resend_seq_nr == m_acked_seq_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + m_acked_seq_nr = (m_acked_seq_nr + 1) & ACK_MASK; + incremented = true; + } + + if (!incremented) return; + + // update loss seq number if it's less than the packet + // that was just acked. If loss seq nr is greater, it suggests + // that we're still in a window that has experienced loss + if (compare_less_wrap(m_loss_seq_nr, m_acked_seq_nr, ACK_MASK)) + m_loss_seq_nr = m_acked_seq_nr; + m_duplicate_acks = 0; +} + +void utp_socket_impl::ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr) +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(p); + + // verify that the packet we're removing was in fact sent + // with the sequence number we expect + TORRENT_ASSERT(((utp_header*)p->buf)->seq_nr == seq_nr); + + if (!p->need_resend) + { + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + } + + if (seq_nr == m_mtu_seq && m_mtu_seq != 0) + { + TORRENT_ASSERT(p->mtu_probe); + // our mtu probe was acked! + m_mtu_floor = (std::max)(m_mtu_floor, p->size); + if (m_mtu_ceiling < m_mtu_floor) m_mtu_ceiling = m_mtu_floor; + update_mtu_limits(); + } + + // increment the acked sequence number counter + maybe_inc_acked_seq_nr(); + + boost::uint32_t rtt = boost::uint32_t(total_microseconds(receive_time - p->send_time)); + if (receive_time < p->send_time) + { + // this means our clock is not monotonic. Just assume the RTT was 100 ms + rtt = 100000; + + // the clock for this plaform is not monotonic! + TORRENT_ASSERT(false); + } + + UTP_LOGV("%8p: acked packet %d (%d bytes) (rtt:%u)\n" + , this, seq_nr, p->size - p->header_size, rtt / 1000); + + m_rtt.add_sample(rtt / 1000); + if (rtt < min_rtt) min_rtt = rtt; + free(p); +} + +void utp_socket_impl::incoming(boost::uint8_t const* buf, int size, packet* p, ptime now) +{ + INVARIANT_CHECK; + + while (!m_read_buffer.empty()) + { + if (p) + { + buf = p->buf + p->header_size; + TORRENT_ASSERT(p->size - p->header_size >= size); + } + iovec_t* target = &m_read_buffer.front(); + + int to_copy = (std::min)(size, int(target->len)); + memcpy(target->buf, buf, to_copy); + m_read += to_copy; + target->buf = ((boost::uint8_t*)target->buf) + to_copy; + target->len -= to_copy; + buf += to_copy; + UTP_LOGV("%8p: copied %d bytes into user receive buffer\n", this, to_copy); + TORRENT_ASSERT(m_read_buffer_size >= to_copy); + m_read_buffer_size -= to_copy; + size -= to_copy; + if (target->len == 0) m_read_buffer.erase(m_read_buffer.begin()); + if (p) + { + p->header_size += to_copy; + TORRENT_ASSERT(p->header_size <= p->size); + } + + if (size == 0) + { + TORRENT_ASSERT(p == 0 || p->header_size == p->size); + free(p); + return; + } + } + + TORRENT_ASSERT(m_read_buffer_size == 0); + + if (!p) + { + TORRENT_ASSERT(buf); + p = (packet*)malloc(sizeof(packet) + size); + p->size = size; + p->header_size = 0; + memcpy(p->buf, buf, size); + } + // save this packet until the client issues another read + m_receive_buffer.push_back(p); + m_receive_buffer_size += p->size - p->header_size; + + check_receive_buffers(); +} + +bool utp_socket_impl::cancel_handlers(error_code const& ec, bool kill) +{ + INVARIANT_CHECK; + + TORRENT_ASSERT(ec); + bool ret = m_read_handler || m_write_handler || m_connect_handler; + + // calling the callbacks with m_userdata being 0 will just crash + TORRENT_ASSERT((ret && bool(m_userdata)) || !ret); + + if (m_read_handler) m_read_handler(m_userdata, 0, ec, kill); + m_read_handler = 0; + if (m_write_handler) m_write_handler(m_userdata, 0, ec, kill); + m_write_handler = 0; + if (m_connect_handler) m_connect_handler(m_userdata, ec, kill); + m_connect_handler = 0; + return ret; +} + +bool utp_socket_impl::consume_incoming_data( + utp_header const* ph, boost::uint8_t const* ptr, int payload_size + , ptime now) +{ + INVARIANT_CHECK; + + if (ph->get_type() != ST_DATA) return false; + + if (m_eof && m_ack_nr == m_eof_seq_nr) + { + // What?! We've already received a FIN and everything up + // to it has been acked. Ignore this packet + UTP_LOG("%8p: ERROR: ignoring packet on shut down socket\n", this); + return true; + } + + if (m_read_buffer_size == 0 + && m_receive_buffer_size >= m_in_buf_size - m_buffered_incoming_bytes) + { + // if we don't have a buffer from the upper layer, and the + // number of queued up bytes, waiting for the upper layer, + // exceeds the advertized receive window, start ignoring + // more data packets + UTP_LOG("%8p: ERROR: our advertized window is not honored. " + "recv_buf: %d buffered_in: %d max_size: %d\n" + , this, m_receive_buffer_size, m_buffered_incoming_bytes, m_in_buf_size); + return false; + } + + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK)) + { + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size) + { + UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet\n", this); + return true; + } + + // we received a packet in order + incoming(ptr, payload_size, 0, now); + m_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + // If this packet was previously in the reorder buffer + // it would have been acked when m_ack_nr-1 was acked. + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + UTP_LOGV("%8p: remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + + for (;;) + { + int const next_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + packet* p = (packet*)m_inbuf.remove(next_ack_nr); + + if (!p) break; + + m_buffered_incoming_bytes -= p->size - p->header_size; + incoming(0, p->size - p->header_size, p, now); + + m_ack_nr = next_ack_nr; + + UTP_LOGV("%8p: reordered remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + } + } + else + { + // this packet was received out of order. Stick it in the + // reorder buffer until it can be delivered in order + + // have we already received this packet and passed it on + // to the client? + if (!compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // do we already have this packet? If so, just ignore it + if (m_inbuf.at(ph->seq_nr)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + if (m_buffered_incoming_bytes + m_receive_buffer_size + payload_size > m_in_buf_size) + { + UTP_LOGV("%8p: other end is not honoring our advertised window, dropping packet %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // we don't need to save the packet header, just the payload + packet* p = (packet*)malloc(sizeof(packet) + payload_size); + p->size = payload_size; + p->header_size = 0; + p->num_transmissions = 0; +#ifdef TORRENT_DEBUG + p->num_fast_resend = 0; +#endif + p->need_resend = false; + memcpy(p->buf, ptr, payload_size); + m_inbuf.insert(ph->seq_nr, p); + m_buffered_incoming_bytes += p->size; + + UTP_LOGV("%8p: out of order. insert inbuf: %d (%d) m_ack_nr: %d\n" + , this, int(ph->seq_nr), int(m_inbuf.size()), m_ack_nr); + } + + return false; +} + +// returns true of the socket was closed +bool utp_socket_impl::test_socket_state() +{ + INVARIANT_CHECK; + + // if the socket is in a state where it's dead, just waiting to + // tell the client that it's closed. Do that and transition into + // the deleted state, where it will be deleted + // it might be possible to get here twice, in which we need to + // cancel any new handlers as well, even though we're already + // in the delete state + if (!m_error) return false; + TORRENT_ASSERT(m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s error:%s\n" + , this, socket_state_names[m_state], m_error.message().c_str()); +#endif + + if (cancel_handlers(m_error, true)) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + return true; + } + return false; +} + +void utp_socket_impl::init_mtu(int link_mtu, int utp_mtu) +{ + INVARIANT_CHECK; + + // if we're in a RAM constrained environment, don't increase + // the buffer size for interfaces with large MTUs. Just stick + // to ethernet frame sizes + if (m_sm->allow_dynamic_sock_buf()) + { + // Make sure that we have enough socket buffer space + // for sending and receiving packets of this size + // add 10% for smaller ACKs and other overhead + m_sm->set_sock_buf(link_mtu * 11 / 10); + } + else if (link_mtu > TORRENT_ETHERNET_MTU) + { + // we can't use larger packets than this since we're + // not allocating any more memory for socket buffers + int decrease = link_mtu - TORRENT_ETHERNET_MTU; + utp_mtu -= decrease; + link_mtu -= decrease; + } + + // set the ceiling to what we found out from the interface + m_mtu_ceiling = utp_mtu; + + // however, start the search from a more conservative MTU + int overhead = link_mtu - utp_mtu; + m_mtu = TORRENT_ETHERNET_MTU - overhead; + if (m_mtu > m_mtu_ceiling) m_mtu = m_mtu_ceiling; + + if (m_mtu_floor > utp_mtu) m_mtu_floor = utp_mtu; + + // if the window size is smaller than one packet size + // set it to one + if ((m_cwnd >> 16) < m_mtu) m_cwnd = boost::int64_t(m_mtu) << 16; + + UTP_LOGV("%8p: initializing MTU to: %d [%d, %d]\n" + , this, m_mtu, m_mtu_floor, m_mtu_ceiling); +} + +// return false if this is an invalid packet +bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size + , udp::endpoint const& ep, ptime receive_time) +{ + INVARIANT_CHECK; + + utp_header* ph = (utp_header*)buf; + + m_sm->inc_stats_counter(utp_socket_manager::packets_in); + + if (ph->get_version() != 1) + { + UTP_LOG("%8p: ERROR: incoming packet version:%d (ignored)\n" + , this, int(ph->get_version())); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + // SYN packets have special (reverse) connection ids + if (ph->get_type() != ST_SYN && ph->connection_id != m_recv_id) + { + UTP_LOG("%8p: ERROR: incoming packet id:%d expected:%d (ignored)\n" + , this, int(ph->connection_id), int(m_recv_id)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + if (ph->get_type() >= NUM_TYPES) + { + UTP_LOG("%8p: ERROR: incoming packet type:%d (ignored)\n" + , this, int(ph->get_type())); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return false; + } + + if (m_state == UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + m_remote_address = ep.address(); + m_port = ep.port(); + } + + if (m_state != UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + UTP_LOG("%8p: ERROR: incoming packet type:ST_SYN (ignored)\n", this); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + + bool step = false; + if (receive_time - m_last_history_step > minutes(1)) + { + step = true; + m_last_history_step = receive_time; + } + + // this is the difference between their send time and our receive time + // 0 means no sample yet + boost::uint32_t their_delay = 0; + if (ph->timestamp_microseconds != 0) + { + boost::uint32_t timestamp = boost::uint32_t(total_microseconds(receive_time - min_time()) & 0xffffffff); + m_reply_micro = timestamp - ph->timestamp_microseconds; + boost::uint32_t prev_base = m_their_delay_hist.initialized() ? m_their_delay_hist.base() : 0; + their_delay = m_their_delay_hist.add_sample(m_reply_micro, step); + int base_change = m_their_delay_hist.base() - prev_base; + UTP_LOGV("%8p: their_delay::add_sample:%u prev_base:%u new_base:%u\n" + , this, m_reply_micro, prev_base, m_their_delay_hist.base()); + + if (prev_base && base_change < 0 && base_change > -10000 && m_delay_hist.initialized()) + { + // their base delay went down. This is caused by clock drift. To compensate, + // adjust our base delay upwards + // don't adjust more than 10 ms. If the change is that big, something is probably wrong + m_delay_hist.adjust_base(-base_change); + } + + UTP_LOGV("%8p: incoming packet reply_micro:%u base_change:%d\n" + , this, m_reply_micro, prev_base ? base_change : 0); + } + + // is this ACK valid? If the other end is ACKing + // a packet that hasn't been sent yet + // just ignore it. A 3rd party could easily inject a packet + // like this in a stream, don't sever it because of it. + // since m_seq_nr is the sequence number of the next packet + // we'll send (and m_seq_nr-1 was the last packet we sent), + // if the ACK we got is greater than the last packet we sent + // something is wrong. + // If our state is state_none, this packet must be a syn packet + // and the ack_nr should be ignored + boost::uint16_t cmp_seq_nr = (m_seq_nr - 1) & ACK_MASK; +#if TORRENT_UT_SEQ + if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE) + cmp_seq_nr = m_seq_nr; +#endif + if (m_state != UTP_STATE_NONE + && compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) + { + UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_seq_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } + + // check to make sure the sequence number of this packet + // is reasonable. If it's a data packet and we've already + // received it, ignore it. This is either a stray old packet + // that finally made it here (after having been re-sent) or + // an attempt to interfere with the connection from a 3rd party + // in both cases, we can safely ignore the timestamp and ACK + // information in this packet +/* + // even if we've already received this packet, we need to + // send another ack to it, since it may be a resend caused by + // our ack getting dropped + if (m_state != UTP_STATE_SYN_SENT + && ph->get_type() == ST_DATA + && !compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + // we've already received this packet + UTP_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } +*/ + + // if the socket is closing, always ignore any packet + // with a higher sequence number than the FIN sequence number + if (m_eof && compare_less_wrap(m_eof_seq_nr, ph->seq_nr, ACK_MASK)) + { +#if TORRENT_UTP_LOG + UTP_LOG("%8p: ERROR: incoming packet type: %s seq_nr:%d eof_seq_nr:%d (ignored)\n" + , this, packet_type_names[ph->get_type()], int(ph->seq_nr), m_eof_seq_nr); +#endif + return true; + } + + if (ph->get_type() == ST_DATA) + m_sm->inc_stats_counter(utp_socket_manager::payload_pkts_in); + + if (m_state != UTP_STATE_NONE + && m_state != UTP_STATE_SYN_SENT + && compare_less_wrap((m_ack_nr + max_packets_reorder) & ACK_MASK, ph->seq_nr, ACK_MASK)) + { + // this is too far out to fit in our reorder buffer. Drop it + // This is either an attack to try to break the connection + // or a seariously damaged connection that lost a lot of + // packets. Neither is very likely, and it should be OK + // to drop the timestamp information. + UTP_LOG("%8p: ERROR: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + m_sm->inc_stats_counter(utp_socket_manager::redundant_pkts_in); + return true; + } + + if (ph->get_type() == ST_RESET) + { + if (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) + { + UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_seq_nr); + return true; + } + if (compare_less_wrap(ph->ack_nr, m_acked_seq_nr , ACK_MASK)) + { + UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our acked_seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_acked_seq_nr); + return true; + } + UTP_LOGV("%8p: incoming packet type:RESET\n", this); + m_error = asio::error::connection_reset; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return true; + } + + ++m_in_packets; + + // this is a valid incoming packet, update the timeout timer + m_num_timeouts = 0; + m_timeout = receive_time + milliseconds(packet_timeout()); + UTP_LOGV("%8p: updating timeout to: now + %d\n" + , this, packet_timeout()); + + // the test for INT_MAX here is a work-around for a bug in uTorrent where + // it's sometimes sent as INT_MAX when it is in fact uninitialized + const boost::uint32_t sample = ph->timestamp_difference_microseconds == INT_MAX + ? 0 : ph->timestamp_difference_microseconds; + + boost::uint32_t delay = 0; + if (sample != 0) + { + delay = m_delay_hist.add_sample(sample, step); + m_delay_sample_hist[m_delay_sample_idx++] = delay; + if (m_delay_sample_idx >= num_delay_hist) m_delay_sample_idx = 0; + } + + int acked_bytes = 0; + + TORRENT_ASSERT(m_bytes_in_flight >= 0); + int prev_bytes_in_flight = m_bytes_in_flight; + + m_adv_wnd = ph->wnd_size; + + // if we get an ack for the same sequence number as + // was last ACKed, and we have outstanding packets, + // it counts as a duplicate ack + if (ph->ack_nr == m_acked_seq_nr && m_outbuf.size()) + { + ++m_duplicate_acks; + } + + boost::uint32_t min_rtt = (std::numeric_limits::max)(); + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // has this packet already been ACKed? + // if the ACK we just got is less than the max ACKed + // sequence number, it doesn't tell us anything. + // So, only act on it if the ACK is greater than the last acked + // sequence number + if (m_state != UTP_STATE_NONE && compare_less_wrap(m_acked_seq_nr, ph->ack_nr, ACK_MASK)) + { + int const next_ack_nr = ph->ack_nr; + + for (int ack_nr = (m_acked_seq_nr + 1) & ACK_MASK; + ack_nr != ((next_ack_nr + 1) & ACK_MASK); + ack_nr = (ack_nr + 1) & ACK_MASK) + { + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + packet* p = (packet*)m_outbuf.remove(ack_nr); + + if (!p) continue; + + acked_bytes += p->size - p->header_size; + ack_packet(p, receive_time, min_rtt, ack_nr); + } + + maybe_inc_acked_seq_nr(); + } + + // look for extended headers + boost::uint8_t const* ptr = buf; + ptr += sizeof(utp_header); + + unsigned int extension = ph->extension; + while (extension) + { + // invalid packet. It says it has an extension header + // but the packet is too short + if (ptr - buf + 2 > size) + { + UTP_LOG("%8p: ERROR: invalid extension header\n", this); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + int next_extension = *ptr++; + int len = *ptr++; + if (len < 0) + { + UTP_LOGV("%8p: invalid extension length:%d packet:%d\n" + , this, len, int(ptr - buf)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + if (ptr - buf + len > ptrdiff_t(size)) + { + UTP_LOG("%8p: ERROR: invalid extension header size:%d packet:%d\n" + , this, len, int(ptr - buf)); + m_sm->inc_stats_counter(utp_socket_manager::invalid_pkts_in); + return true; + } + switch(extension) + { + case 1: // selective ACKs + parse_sack(ph->ack_nr, ptr, len, &acked_bytes, receive_time, min_rtt); + break; + } + ptr += len; + extension = next_extension; + } + + // the send operation in parse_sack() may have set the socket to an error + // state, in which case we shouldn't continue + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + if (m_duplicate_acks >= dup_ack_limit + && ((m_acked_seq_nr + 1) & ACK_MASK) == m_fast_resend_seq_nr) + { + // LOSS + + UTP_LOGV("%8p: Packet %d lost. (%d duplicate acks, trigger fast-resend)\n", this, m_fast_resend_seq_nr, m_duplicate_acks); + + // resend the lost packet + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + TORRENT_ASSERT(p); + + // don't fast-resend this again + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + if (p) + { + experienced_loss(m_fast_resend_seq_nr); + resend_packet(p, true); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + } + + // ptr points to the payload of the packet + // size is the packet size, payload is the + // number of payload bytes are in this packet + const int header_size = ptr - buf; + const int payload_size = size - header_size; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incoming packet seq_nr:%d ack_nr:%d type:%s id:%d size:%d timestampdiff:%u timestamp:%u " + "our ack_nr:%d our seq_nr:%d our acked_seq_nr:%d our state:%s\n" + , this, int(ph->seq_nr), int(ph->ack_nr), packet_type_names[ph->get_type()] + , int(ph->connection_id), payload_size, boost::uint32_t(ph->timestamp_difference_microseconds) + , boost::uint32_t(ph->timestamp_microseconds), m_ack_nr, m_seq_nr, m_acked_seq_nr, socket_state_names[m_state]); +#endif + + if (ph->get_type() == ST_FIN) + { + // We ignore duplicate FIN packets, but we still need to ACK them. + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK) + || ph->seq_nr == m_ack_nr) + { + UTP_LOGV("%8p: FIN received in order\n", this); + + // The FIN arrived in order, nothing else is in the + // reorder buffer. + +// TORRENT_ASSERT(m_inbuf.size() == 0); + m_ack_nr = ph->seq_nr; + + // Transition to UTP_STATE_FIN_SENT. The sent FIN is also an ack + // to the FIN we received. Once we're in UTP_STATE_FIN_SENT we + // just need to wait for our FIN to be acked. + + if (m_state == UTP_STATE_FIN_SENT) + { + send_pkt(pkt_ack); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + else + { + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + } + + if (m_eof) + { + UTP_LOGV("%8p: duplicate FIN packet (ignoring)\n", this); + return true; + } + m_eof = true; + m_eof_seq_nr = ph->seq_nr; + + // we will respond with a fin once we have received everything up to m_eof_seq_nr + } + + switch (m_state) + { + case UTP_STATE_NONE: + { + if (ph->get_type() == ST_SYN) + { + // if we're in state_none, the only thing + // we accept are SYN packets. + m_state = UTP_STATE_CONNECTED; + + m_remote_address = ep.address(); + m_port = ep.port(); + + error_code ec; + m_local_address = m_sm->local_endpoint(m_remote_address, ec).address(); + + m_ack_nr = ph->seq_nr; + m_seq_nr = random() & 0xffff; + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + m_fast_resend_seq_nr = m_seq_nr; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: received ST_SYN state:%s seq_nr:%d ack_nr:%d\n" + , this, socket_state_names[m_state], m_seq_nr, m_ack_nr); +#endif + TORRENT_ASSERT(m_send_id == ph->connection_id); + TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff)); + + defer_ack(); + + return true; + } + else + { +#if TORRENT_UTP_LOG + UTP_LOG("%8p: ERROR: type:%s state:%s (ignored)\n" + , this, packet_type_names[ph->get_type()], socket_state_names[m_state]); +#endif + return true; + } + break; + } + case UTP_STATE_SYN_SENT: + { + // just wait for an ack to our SYN, ignore everything else + if (ph->ack_nr != ((m_seq_nr - 1) & ACK_MASK)) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incorrect ack_nr (%d) waiting for %d\n" + , this, int(ph->ack_nr), (m_seq_nr - 1) & ACK_MASK); +#endif + return true; + } + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_CONNECTED; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + + // only progress our ack_nr on ST_DATA messages + // since our m_ack_nr is uninitialized at this point + // we still need to set it to something regardless + if (ph->get_type() == ST_DATA) + m_ack_nr = ph->seq_nr; + else + m_ack_nr = (ph->seq_nr - 1) & ACK_MASK; + + // notify the client that the socket connected + if (m_connect_handler) + { + UTP_LOGV("%8p: calling connect handler\n", this); + m_connect_handler(m_userdata, m_error, false); + } + m_connect_handler = 0; + // fall through + } + case UTP_STATE_CONNECTED: + { + // the lowest seen RTT can be used to clamp the delay + // within reasonable bounds. The one-way delay is never + // higher than the round-trip time. + + if (sample && acked_bytes && prev_bytes_in_flight) + { + // it's impossible for delay to be more than the RTT, so make + // sure to clamp it as a sanity check + if (delay > min_rtt) delay = min_rtt; + + // only use the minimum from the last 3 delay measurements + delay = *std::min_element(m_delay_sample_hist, m_delay_sample_hist + num_delay_hist); + + do_ledbat(acked_bytes, delay, prev_bytes_in_flight, receive_time); + m_send_delay = delay; + } + + m_recv_delay = (std::min)(their_delay, min_rtt); + + consume_incoming_data(ph, ptr, payload_size, receive_time); + + // the parameter to send_pkt tells it if we're acking data + // If we are, we'll send an ACK regardless of if we have any + // space left in our send window or not. If we just got an ACK + // (i.e. ST_STATE) we're not ACKing anything. If we just + // received a FIN packet, we need to ack that as well + bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN; + boost::uint32_t prev_out_packets = m_out_packets; + + // try to send more data as long as we can + // if send_pkt returns true + while (send_pkt()); + + if (has_ack && prev_out_packets == m_out_packets) + { + // we need to ack some data we received, and we didn't + // end up sending any payload packets in the loop + // above (becasue m_out_packets would have been incremented + // in that case). This means we need to send an ack. + // don't do it right away, because we may still receive + // more packets. defer the ack to send as few acks as possible + defer_ack(); + } + + // we may want to call the user callback function at the end + // of this round. Subscribe to that event + subscribe_drained(); + + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + // Everything up to the FIN has been receieved, respond with a FIN + // from our side. + if (m_eof && m_ack_nr == ((m_eof_seq_nr - 1) & ACK_MASK)) + { + UTP_LOGV("%8p: incoming stream consumed\n", this); + + // This transitions to the UTP_STATE_FIN_SENT state. + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + +#if TORRENT_UTP_LOG + if (sample && acked_bytes && prev_bytes_in_flight) + { + char their_delay_base[20]; + if (m_their_delay_hist.initialized()) + snprintf(their_delay_base, sizeof(their_delay_base), "%u", m_their_delay_hist.base()); + else + strcpy(their_delay_base, "-"); + + char our_delay_base[20]; + if (m_delay_hist.initialized()) + snprintf(our_delay_base, sizeof(our_delay_base), "%u", m_delay_hist.base()); + else + strcpy(our_delay_base, "-"); + + UTP_LOG("%8p: " + "actual_delay:%u " + "our_delay:%f " + "their_delay:%f " + "off_target:%f " + "max_window:%u " + "upload_rate:%d " + "delay_base:%s " + "delay_sum:%f " + "target_delay:%d " + "acked_bytes:%d " + "cur_window:%d " + "scaled_gain:%f " + "rtt:%u " + "rate:%d " + "quota:%d " + "wnduser:%u " + "rto:%d " + "timeout:%d " + "get_microseconds:%u " + "cur_window_packets:%u " + "packet_size:%d " + "their_delay_base:%s " + "their_actual_delay:%u " + "seq_nr:%u " + "acked_seq_nr:%u " + "reply_micro:%u " + "min_rtt:%u " + "send_buffer:%d " + "recv_buffer:%d " + "fast_resend_seq_nr:%d " + "ssthres:%d " + "\n" + , this + , sample + , float(delay / 1000.f) + , float(their_delay / 1000.f) + , float(int(m_sm->target_delay() - delay)) / 1000.f + , boost::uint32_t(m_cwnd >> 16) + , 0 + , our_delay_base + , float(delay + their_delay) / 1000.f + , m_sm->target_delay() / 1000 + , acked_bytes + , m_bytes_in_flight + , 0.f // float(scaled_gain) + , m_rtt.mean() + , int((m_cwnd * 1000 / (m_rtt.mean()?m_rtt.mean():50)) >> 16) + , 0 + , m_adv_wnd + , packet_timeout() + , int(total_milliseconds(m_timeout - receive_time)) + , int(total_microseconds(receive_time - min_time())) + , (m_seq_nr - m_acked_seq_nr) & ACK_MASK + , m_mtu + , their_delay_base + , boost::uint32_t(m_reply_micro) + , m_seq_nr + , m_acked_seq_nr + , m_reply_micro + , min_rtt / 1000 + , m_write_buffer_size + , m_read_buffer_size + , m_fast_resend_seq_nr + , m_ssthres); + } +#endif + + return true; + } + case UTP_STATE_FIN_SENT: + { + // There are two ways we can end up in this state: + // + // 1. If the socket has been explicitly closed on our + // side, in which case m_eof is false. + // + // 2. If we received a FIN from the remote side, in which + // case m_eof is true. If this is the case, we don't + // come here until everything up to the FIN has been + // received. + // + // + // + + // At this point m_seq_nr - 1 is the FIN sequence number. + + // We can receive both ST_DATA and ST_STATE here, because after + // we have closed our end of the socket, the remote end might + // have data in the pipeline. We don't really care about the + // data, but we do have to ack it. Or rather, we have to ack + // the FIN that will come after the data. + + // Case 1: + // --------------------------------------------------------------- + // + // If we are here because the local endpoint was closed, we need + // to first wait for all of our messages to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // ---------------------- + // + // After that has happened we need to wait for the remote side + // to send its ST_FIN message. When we receive that we send an + // ST_STATE back to ack, and wait for a sufficient period. + // During this wait we keep acking incoming ST_FIN's. This is + // all handled at the top of this function. + // + // Note that the user handlers are all cancelled when the initial + // close() call happens, so nothing will happen on the user side + // after that. + + // Case 2: + // --------------------------------------------------------------- + // + // If we are here because we received a ST_FIN message, and then + // sent our own ST_FIN to ack that, we need to wait for our ST_FIN + // to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // After that has happened we know the remote side has all our + // data, and we can gracefully shut down. + + if (consume_incoming_data(ph, ptr, payload_size, receive_time)) + return true; + + if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + { + // When this happens we know that the remote side has + // received all of our packets. + + UTP_LOGV("%8p: FIN acked\n", this); + + if (!m_attached) + { + UTP_LOGV("%8p: close initiated here, delete socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_DELETE; + test_socket_state(); + } + else + { + UTP_LOGV("%8p: closing socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + } + } + + return true; + } + case UTP_STATE_DELETE: + default: + { + // respond with a reset + send_reset(ph); + return true; + } + } + + return false; +} + +void utp_socket_impl::do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now) +{ + INVARIANT_CHECK; + + // the portion of the in-flight bytes that were acked. This is used to make + // the gain factor be scaled by the rtt. The formula is applied once per + // rtt, or on every ACK skaled by the number of ACKs per rtt + TORRENT_ASSERT(in_flight > 0); + TORRENT_ASSERT(acked_bytes > 0); + + int target_delay = m_sm->target_delay(); + + // true if the upper layer is pushing enough data down the socket to be + // limited by the cwnd. If this is not the case, we should not adjust cwnd. + bool cwnd_saturated = (m_bytes_in_flight + acked_bytes + m_mtu > (m_cwnd >> 16)); + + // all of these are fixed points with 16 bits fraction portion + boost::int64_t window_factor = (boost::int64_t(acked_bytes) << 16) / in_flight; + boost::int64_t delay_factor = (boost::int64_t(target_delay - delay) << 16) / target_delay; + boost::int64_t scaled_gain; + + if (delay >= target_delay) + { + if (m_slow_start) + { + UTP_LOGV("%8p: off_target: %d slow_start -> 0\n", this, target_delay - delay); + m_ssthres = m_cwnd >> 16; + m_slow_start = false; + } + + m_sm->inc_stats_counter(utp_socket_manager::samples_above_target); + } + else + { + m_sm->inc_stats_counter(utp_socket_manager::samples_below_target); + } + + boost::int64_t linear_gain = (window_factor * delay_factor) >> 16; + linear_gain *= boost::int64_t(m_sm->gain_factor()); + + // if the user is not saturating the link (i.e. not filling the + // congestion window), don't adjust it at all. + if (cwnd_saturated) + { + boost::int64_t exponential_gain = boost::int64_t(acked_bytes) << 16; + if (m_slow_start) + { + // mimic TCP slow-start by adding the number of acked + // bytes to cwnd + if (m_ssthres != 0 && ((m_cwnd + exponential_gain) >> 16) > m_ssthres) + { + // if we would exeed the slow start threshold by growing the cwnd + // exponentially, don't do it, and leave slow-start mode. This + // make us avoid causing more delay and/or packet loss by being too + // aggressive + m_slow_start = false; + scaled_gain = linear_gain; + UTP_LOGV("%8p: cwnd > ssthres (%d) slow_start -> 0\n", this, m_ssthres); + } + else + { + scaled_gain = (std::max)(exponential_gain, linear_gain); + } + } + else + { + scaled_gain = linear_gain; + } + } + else + { + scaled_gain = 0; + } + + // make sure we don't wrap the cwnd + if (scaled_gain >= (std::numeric_limits::max)() - m_cwnd) + scaled_gain = (std::numeric_limits::max)() - m_cwnd - 1; + + UTP_LOGV("%8p: do_ledbat delay:%d off_target: %d window_factor:%f target_factor:%f " + "scaled_gain:%f cwnd:%d slow_start:%d\n" + , this, delay, target_delay - delay, window_factor / float(1 << 16) + , delay_factor / float(1 << 16) + , scaled_gain / float(1 << 16), int(m_cwnd >> 16) + , int(m_slow_start)); + + // if scaled_gain + m_cwnd <= 0, set m_cwnd to 0 + if (-scaled_gain >= m_cwnd) + { + m_cwnd = 0; + } + else + { + m_cwnd += scaled_gain; + TORRENT_ASSERT(m_cwnd > 0); + } + + TORRENT_ASSERT(m_cwnd >= 0); + + int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - in_flight + acked_bytes; + if (window_size_left >= m_mtu) + { + UTP_LOGV("%8p: mtu:%d in_flight:%d adv_wnd:%d cwnd:%d acked_bytes:%d cwnd_full -> 0\n" + , this, m_mtu, in_flight, int(m_adv_wnd), int(m_cwnd >> 16), acked_bytes); + m_cwnd_full = false; + } + + if ((m_cwnd >> 16) >= m_adv_wnd) + { + m_slow_start = false; + UTP_LOGV("%8p: cwnd > advertized wnd (%d) slow_start -> 0\n" + , this, m_adv_wnd); + } +} + +void utp_stream::bind(endpoint_type const& ep, error_code& ec) { } + +// returns the number of milliseconds a packet would have before +// it would time-out if it was sent right now. Takes the RTT estimate +// into account +int utp_socket_impl::packet_timeout() const +{ + INVARIANT_CHECK; + + // SYN packets have a bit longer timeout, since we don't + // have an RTT estimate yet, make a conservative guess + if (m_state == UTP_STATE_NONE) return 3000; + + // avoid overflow by simply capping based on number of timeouts as well + if (m_num_timeouts >= 7) return 60000; + + int timeout = (std::max)(m_sm->min_timeout(), m_rtt.mean() + m_rtt.avg_deviation() * 2); + if (m_num_timeouts > 0) timeout += (1 << (int(m_num_timeouts) - 1)) * 1000; + return timeout; +} + +void utp_socket_impl::tick(ptime const& now) +{ + INVARIANT_CHECK; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: tick:%s r: %d (%s) w: %d (%s)\n" + , this, socket_state_names[m_state], m_read, m_read_handler ? "handler" : "no handler" + , m_written, m_write_handler ? "handler" : "no handler"); +#endif + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // if we're already in an error state, we're just waiting for the + // client to perform an operation so that we can communicate the + // error. No need to do anything else with this socket + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + + if (now > m_timeout) + { + // TIMEOUT! + // set cwnd to 1 MSS + + m_sm->inc_stats_counter(utp_socket_manager::timeout); + + if (m_outbuf.size()) ++m_num_timeouts; + + if (m_num_timeouts > m_sm->num_resends()) + { + // the connection is dead + m_error = asio::error::timed_out; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && ((m_seq_nr - 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + // we timed out, and the only outstanding packet + // we had was the probe. Assume it was dropped + // because it was too big + m_mtu_ceiling = m_mtu - 1; + if (m_mtu_floor > m_mtu_ceiling) m_mtu_floor = m_mtu_ceiling; + update_mtu_limits(); + } + + if (m_bytes_in_flight == 0 && (m_cwnd >> 16) >= m_mtu) + { + // this is just a timeout because this direction of + // the stream is idle. Don't reset the cwnd, just decay it + m_cwnd = (std::max)(m_cwnd * 2 / 3, boost::int64_t(m_mtu) << 16); + } + else + { + // we timed out because a packet was not ACKed or because + // the cwnd was made smaller than one packet + m_cwnd = boost::int64_t(m_mtu) << 16; + } + + TORRENT_ASSERT(m_cwnd >= 0); + + m_timeout = now + milliseconds(packet_timeout()); + + UTP_LOGV("%8p: timeout resetting cwnd:%d\n" + , this, int(m_cwnd >> 16)); + + // we dropped all packets, that includes the mtu probe + m_mtu_seq = 0; + + // since we've already timed out now, don't count + // loss that we might detect for packets that just + // timed out + m_loss_seq_nr = m_seq_nr; + + // when we time out, the cwnd is reset to 1 MSS, which means we + // need to ramp it up quickly again. enter slow start mode. This time + // we're very likely to have an ssthres set, which will make us leave + // slow start before inducing more delay or loss. + m_slow_start = true; + UTP_LOGV("%8p: timeout slow_start -> 1\n", this); + + // we need to go one past m_seq_nr to cover the case + // where we just sent a SYN packet and then adjusted for + // the uTorrent sequence number reuse + for (int i = m_acked_seq_nr & ACK_MASK; + i != ((m_seq_nr + 1) & ACK_MASK); + i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (p->need_resend) continue; + p->need_resend = true; + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + UTP_LOGV("%8p: Packet %d lost (timeout).\n", this, i); + } + + TORRENT_ASSERT(m_bytes_in_flight == 0); + + // if we have a packet that needs re-sending, resend it + packet* p = (packet*)m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK); + if (p) + { + if (p->num_transmissions >= m_sm->num_resends() + || (m_state == UTP_STATE_SYN_SENT && p->num_transmissions >= m_sm->syn_resends()) + || (m_state == UTP_STATE_FIN_SENT && p->num_transmissions >= m_sm->fin_resends())) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: %d failed sends in a row. Socket timed out. state:%s\n" + , this, p->num_transmissions, socket_state_names[m_state]); +#endif + + // the connection is dead + m_error = asio::error::timed_out; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == ((m_acked_seq_nr + 1) & ACK_MASK)) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + // the packet timed out, resend it + resend_packet(p); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state < UTP_STATE_FIN_SENT) + { + send_pkt(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state == UTP_STATE_FIN_SENT) + { + // the connection is dead + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + } + + switch (m_state) + { + case UTP_STATE_NONE: + case UTP_STATE_DELETE: + return; +// case UTP_STATE_SYN_SENT: +// +// break; + } +} + +void utp_socket_impl::check_receive_buffers() const +{ + INVARIANT_CHECK; + + std::size_t size = 0; + + for (std::vector::const_iterator i = m_receive_buffer.begin() + , end(m_receive_buffer.end()); i != end; ++i) + { + if (packet const* p = *i) + size += p->size - p->header_size; + } + + TORRENT_ASSERT(int(size) == m_receive_buffer_size); +} + +#if TORRENT_USE_INVARIANT_CHECKS +void utp_socket_impl::check_invariant() const +{ + for (int i = m_outbuf.cursor(); + i != int((m_outbuf.cursor() + m_outbuf.span()) & ACK_MASK); + i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (m_mtu_seq == i && m_mtu_seq != 0 && p) + { + TORRENT_ASSERT(p->mtu_probe); + } + if (!p) continue; + TORRENT_ASSERT(((utp_header*)p->buf)->seq_nr == i); + } + + if (m_nagle_packet) + { + // if this packet is full, it should have been sent + TORRENT_ASSERT(m_nagle_packet->size < m_nagle_packet->allocated); + } +} +#endif +} + diff --git a/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp b/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp new file mode 100644 index 0000000000..c30ac4c961 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/web_connection_base.cpp @@ -0,0 +1,205 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include "libtorrent/web_connection_base.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + web_connection_base::web_connection_base( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : peer_connection(ses, t, s, remote, &web.peer_info) + , m_parser(http_parser::dont_parse_chunks) + , m_external_auth(web.auth) + , m_extra_headers(web.extra_headers) + , m_first_request(true) + , m_ssl(false) + , m_body_start(0) + { + INVARIANT_CHECK; + + // we only want left-over bandwidth + set_priority(1); + + // since this is a web seed, change the timeout + // according to the settings. + set_timeout(ses.settings().urlseed_timeout); + + std::string protocol; + error_code ec; + boost::tie(protocol, m_basic_auth, m_host, m_port, m_path) + = parse_url_components(web.url, ec); + TORRENT_ASSERT(!ec); + + if (m_port == -1 && protocol == "http") + m_port = 80; + +#ifdef TORRENT_USE_OPENSSL + if (protocol == "https") + { + m_ssl = true; + if (m_port == -1) m_port = 443; + } +#endif + + if (!m_basic_auth.empty()) + m_basic_auth = base64encode(m_basic_auth); + + m_server_string = "URL seed @ "; + m_server_string += m_host; + } + + void web_connection_base::start() + { + set_upload_only(true); + if (is_disconnecting()) return; + peer_connection::start(); + } + + web_connection_base::~web_connection_base() + {} + + void web_connection_base::on_connected() + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // this is always a seed + incoming_have_all(); + + // it is always possible to request pieces + incoming_unchoke(); + + reset_recv_buffer(t->block_size() + 1024); + } + + void web_connection_base::add_headers(std::string& request + , proxy_settings const& ps, bool using_proxy) const + { + request += "Host: "; + request += m_host; + if (m_first_request || m_ses.settings().always_send_user_agent) { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (!m_external_auth.empty()) { + request += "\r\nAuthorization: "; + request += m_external_auth; + } else if (!m_basic_auth.empty()) { + request += "\r\nAuthorization: Basic "; + request += m_basic_auth; + } + if (ps.type == proxy_settings::http_pw) { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(ps.username + ":" + ps.password); + } + for (web_seed_entry::headers_t::const_iterator it = m_extra_headers.begin(); + it != m_extra_headers.end(); ++it) { + request += "\r\n"; + request += it->first; + request += ": "; + request += it->second; + } + if (using_proxy) { + request += "\r\nProxy-Connection: keep-alive"; + } + if (m_first_request || using_proxy) { + request += "\r\nConnection: keep-alive"; + } + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void web_connection_base::get_specific_peer_info(peer_info& p) const + { + if (is_interesting()) p.flags |= peer_info::interesting; + if (is_choked()) p.flags |= peer_info::choked; + if (!is_connecting() && m_server_string.empty()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_server_string; + } + + bool web_connection_base::in_handshake() const + { + return m_server_string.empty(); + } + + void web_connection_base::on_sent(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + m_statistics.sent_bytes(0, bytes_transferred); + } + + +#if TORRENT_USE_INVARIANT_CHECKS + void web_connection_base::check_invariant() const + { +/* + TORRENT_ASSERT(m_num_pieces == std::count( + m_have_piece.begin() + , m_have_piece.end() + , true)); +*/ } +#endif + +} + diff --git a/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp b/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp new file mode 100644 index 0000000000..bf3a6e79fc --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/web_peer_connection.cpp @@ -0,0 +1,1050 @@ +/* + +Copyright (c) 2003-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" +#include "libtorrent/peer_info.hpp" + +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + enum + { + request_size_overhead = 5000 + }; + + web_peer_connection::web_peer_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , web_seed_entry& web) + : web_connection_base(ses, t, s, remote, web) + , m_url(web.url) + , m_web(&web) + , m_received_body(0) + , m_range_pos(0) + , m_block_pos(0) + , m_chunk_pos(0) + , m_partial_chunk_header(0) + , m_num_responses(0) + { + INVARIANT_CHECK; + + if (!ses.settings().report_web_seed_downloads) + ignore_stats(true); + + shared_ptr tor = t.lock(); + TORRENT_ASSERT(tor); + + // we always prefer downloading 1 MiB chunks + // from web seeds, or whole pieces if pieces + // are larger than a MiB + int preferred_size = 1024 * 1024; + + // if the web server is known not to support keep-alive. + // request even larger blocks at a time + if (!web.supports_keepalive) preferred_size *= 4; + + prefer_whole_pieces((std::max)(preferred_size / tor->torrent_file().piece_length(), 1)); + + // we want large blocks as well, so + // we can request more bytes at once + // this setting will merge adjacent requests + // into single larger ones + request_large_blocks(true); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** web_peer_connection %s", web.url.c_str()); +#endif + } + + void web_peer_connection::on_connected() + { + incoming_have_all(); + if (m_web->restart_request.piece != -1) + { + // increase the chances of requesting the block + // we have partial data for already, to finish it + incoming_suggest(m_web->restart_request.piece); + } + web_connection_base::on_connected(); + } + + void web_peer_connection::disconnect(error_code const& ec, int error) + { + if (is_disconnecting()) return; + + boost::shared_ptr t = associated_torrent().lock(); + + if (!m_requests.empty() && !m_file_requests.empty() + && !m_piece.empty() && m_web) + { +#if 0 + std::cerr << this << " SAVE-RESTART-DATA: data: " << m_piece.size() + << " req: " << m_requests.front().piece + << " off: " << m_requests.front().start + << std::endl; +#endif + m_web->restart_request = m_requests.front(); + if (!m_web->restart_piece.empty()) + { + // we're about to replace a different restart piece + // buffer. So it was wasted download + if (t) t->add_redundant_bytes(m_web->restart_piece.size() + , torrent::piece_closing); + } + m_web->restart_piece.swap(m_piece); + + // we have to do this to not count this data as redundant. The + // upper layer will call downloading_piece_progress and assume + // it's all wasted download. Since we're saving it here, it isn't. + m_requests.clear(); + m_block_pos = 0; + } + + if (m_web && !m_web->supports_keepalive && error == 0) + { + // if the web server doesn't support keepalive and we were + // disconnected as a graceful EOF, reconnect right away + if (t) t->session().m_io_service.post( + boost::bind(&torrent::maybe_connect_web_seeds, t)); + } + peer_connection::disconnect(ec, error); + if (t) t->disconnect_web_seed(this); + } + + boost::optional + web_peer_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + piece_block_progress ret; + + ret.piece_index = m_requests.front().piece; + ret.bytes_downloaded = m_block_pos % t->block_size(); + // this is used to make sure that the block_index stays within + // bounds. If the entire piece is downloaded, the block_index + // would otherwise point to one past the end + int correction = m_block_pos ? -1 : 0; + ret.block_index = (m_requests.front().start + m_block_pos + correction) / t->block_size(); + TORRENT_ASSERT(ret.block_index < int(piece_block::invalid.block_index)); + TORRENT_ASSERT(ret.piece_index < int(piece_block::invalid.piece_index)); + + ret.full_block_bytes = t->block_size(); + const int last_piece = t->torrent_file().num_pieces() - 1; + if (ret.piece_index == last_piece && ret.block_index + == t->torrent_file().piece_size(last_piece) / t->block_size()) + ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); + return ret; + } + + void web_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + + bool single_file_request = t->torrent_file().num_files() == 1; + + if (!single_file_request) + { + // handle incorrect .torrent files which are multi-file + // but have web seeds not ending with a slash + if (m_path.empty() || m_path[m_path.size() - 1] != '/') m_path += "/"; + if (m_url.empty() || m_url[m_url.size() - 1] != '/') m_url += "/"; + } + else + { + // handle .torrent files that don't include the filename in the url + if (m_path.empty()) m_path += "/" + t->torrent_file().name(); + else if (m_path[m_path.size() - 1] == '/') + { + std::string tmp = t->torrent_file().files().at(0).path; +#ifdef TORRENT_WINDOWS + convert_path_to_posix(tmp); +#endif + m_path += tmp; + } + else if (!m_url.empty() && m_url[m_url.size() - 1] == '/') + { + std::string tmp = t->torrent_file().files().at(0).path; +#ifdef TORRENT_WINDOWS + convert_path_to_posix(tmp); +#endif + m_url += tmp; + } + } + + torrent_info const& info = t->torrent_file(); + peer_request req = r; + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + const int piece_size = t->torrent_file().piece_length(); + peer_request pr; + while (size > 0) + { + int request_offset = r.start + r.length - size; + pr.start = request_offset % piece_size; + pr.length = (std::min)(block_size, size); + pr.piece = r.piece + request_offset / piece_size; + m_requests.push_back(pr); + size -= pr.length; + if (m_web->restart_request == m_requests.front()) + { + m_piece.swap(m_web->restart_piece); + m_block_pos += m_piece.size(); + peer_request& front = m_requests.front(); + TORRENT_ASSERT(front.length > int(m_piece.size())); + +#if 0 + std::cerr << this << " RESTART-DATA: data: " << m_piece.size() + << " req: ( " << front.piece << ", " << front.start + << ", " << (front.start + front.length - 1) << ")" + << std::endl; +#endif + + req.start += m_piece.size(); + req.length -= m_piece.size(); + + // just to keep the accounting straight for the upper layer. + // it doesn't know we just re-wrote the request + incoming_piece_fragment(m_piece.size()); + m_web->restart_request.piece = -1; + } + +#if 0 + std::cerr << this << " REQ: p: " << pr.piece << " " << pr.start << std::endl; +#endif + } + + proxy_settings const& ps = m_ses.proxy(); + bool using_proxy = (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) && !m_ssl; + + if (single_file_request) + { + request += "GET "; + // do not encode single file paths, they are + // assumed to be encoded in the torrent file + request += using_proxy ? m_url : m_path; + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\nRange: bytes="; + request += to_string(size_type(req.piece) * info.piece_length() + + req.start).elems; + request += "-"; + request += to_string(size_type(req.piece) * info.piece_length() + + req.start + req.length - 1).elems; + request += "\r\n\r\n"; + m_first_request = false; + m_file_requests.push_back(0); + } + else + { + std::vector files = info.orig_files().map_block( + req.piece, req.start, req.length); + + for (std::vector::iterator i = files.begin(); + i != files.end(); ++i) + { + file_slice const& f = *i; + if (info.orig_files().pad_file_at(f.file_index)) + { + m_file_requests.push_back(f.file_index); + continue; + } + request += "GET "; + if (using_proxy) + { + // m_url is already a properly escaped URL + // with the correct slashes. Don't encode it again + request += m_url; + std::string path = info.orig_files().file_path(f.file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + request += escape_path(path.c_str(), path.length()); + } + else + { + // m_path is already a properly escaped URL + // with the correct slashes. Don't encode it again + request += m_path; + + std::string path = info.orig_files().file_path(f.file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + request += escape_path(path.c_str(), path.length()); + } + request += " HTTP/1.1\r\n"; + add_headers(request, ps, using_proxy); + request += "\r\nRange: bytes="; + request += to_string(f.offset).elems; + request += "-"; + request += to_string(f.offset + f.size - 1).elems; + request += "\r\n\r\n"; + m_first_request = false; + +#if 0 + std::cerr << this << " SEND-REQUEST: f: " << f.file_index + << " s: " << f.offset + << " e: " << (f.offset + f.size - 1) << std::endl; +#endif + TORRENT_ASSERT(f.file_index >= 0); + m_file_requests.push_back(f.file_index); + } + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("==> %s", request.c_str()); +#endif + + // in case the first file on this series of requests is a padfile + // we need to handle it right now, and pretend that we got a response + // with zeros. + buffer::const_interval recv_buffer = receive_buffer(); + handle_padfile(recv_buffer); + if (associated_torrent().expired()) return; + + send_buffer(request.c_str(), request.size(), message_type_request); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + namespace + { + bool range_contains(peer_request const& range, peer_request const& req, int piece_size) + { + size_type range_start = size_type(range.piece) * piece_size + range.start; + size_type req_start = size_type(req.piece) * piece_size + req.start; + return range_start <= req_start + && range_start + range.length >= req_start + req.length; + } + } + + bool web_peer_connection::maybe_harvest_block() + { + peer_request const& front_request = m_requests.front(); + + if (int(m_piece.size()) < front_request.length) return false; + TORRENT_ASSERT(int(m_piece.size()) == front_request.length); + + // each call to incoming_piece() may result in us becoming + // a seed. If we become a seed, all seeds we're connected to + // will be disconnected, including this web seed. We need to + // check for the disconnect condition after the call. + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + buffer::const_interval recv_buffer = receive_buffer(); + + incoming_piece(front_request, &m_piece[0]); + m_requests.pop_front(); + if (associated_torrent().expired()) return false; + TORRENT_ASSERT(m_block_pos >= front_request.length); + m_block_pos -= front_request.length; + cut_receive_buffer(m_body_start, t->block_size() + request_size_overhead); + m_body_start = 0; + recv_buffer = receive_buffer(); +// TORRENT_ASSERT(m_received_body <= range_end - range_start); + m_piece.clear(); + TORRENT_ASSERT(m_piece.empty()); + return true; + } + + bool web_peer_connection::received_invalid_data(int index, bool single_peer) + { + if (!single_peer) return peer_connection::received_invalid_data(index, single_peer); + + // when a web seed fails a hash check, do the following: + // 1. if the whole piece only overlaps a single file, mark that file as not + // have for this peer + // 2. if the piece overlaps more than one file, mark the piece as not have + // for this peer + // 3. if it's a single file torrent, just ban it right away + // this handles the case where web seeds may have some files updated but not other + + boost::shared_ptr t = associated_torrent().lock(); + file_storage const& fs = t->torrent_file().files(); + + // single file torrent + if (fs.num_files() == 1) return peer_connection::received_invalid_data(index, single_peer); + + std::vector files = fs.map_block(index, 0, fs.piece_size(index)); + + if (files.size() == 1) + { + // assume the web seed has a different copy of this specific file + // than what we expect, and pretend not to have it. + int fi = files[0].file_index; + int first_piece = int(fs.file_offset(fi) / fs.piece_length()); + // one past last piece + int end_piece = int((fs.file_offset(fi) + fs.file_size(fi) + 1) / fs.piece_length()); + for (int i = first_piece; i < end_piece; ++i) + incoming_dont_have(i); + } + else + { + incoming_dont_have(index); + } + + peer_connection::received_invalid_data(index, single_peer); + + // if we don't think we have any of the files, allow banning the web seed + if (num_have_pieces() == 0) return true; + + // don't disconnect, we won't request anything from this file again + return false; + } + + void web_peer_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + bytes_transferred < size_t(INT_MAX)); + int dl_target = m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + bytes_transferred; +#endif + + if (error) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** web_peer_connection error: %s", error.message().c_str()); +#endif +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + for (;;) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + + int payload; + int protocol; + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + bool failed = false; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, failed); + m_statistics.received_bytes(0, protocol); + TORRENT_ASSERT(int(bytes_transferred) >= protocol); + bytes_transferred -= protocol; + + if (failed) + { + m_statistics.received_bytes(0, bytes_transferred); +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** %s", std::string(recv_buffer.begin, recv_buffer.end).c_str()); +#endif + disconnect(errors::http_parse_error, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + TORRENT_ASSERT(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + break; + } + + if (!m_parser.header_finished()) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + int(bytes_transferred) + == dl_target); +#endif + break; + } + + m_body_start = m_parser.body_start(); + m_received_body = 0; + } + + // we just completed reading the header + if (!header_finished) + { + ++m_num_responses; + + if (m_parser.connection_close()) + { + incoming_choke(); + if (m_num_responses == 1) + m_web->supports_keepalive = false; + } + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** STATUS: %d %s", m_parser.status_code(), m_parser.message().c_str()); + std::multimap const& headers = m_parser.headers(); + for (std::multimap::const_iterator i = headers.begin() + , end(headers.end()); i != end; ++i) + peer_log(" %s: %s", i->first.c_str(), i->second.c_str()); +#endif + // if the status code is not one of the accepted ones, abort + if (!is_ok_status(m_parser.status_code())) + { + // TODO: 3 just make this peer not have the pieces + // associated with the file we just requested. Only + // when it doesn't have any of the file do the following + int retry_time = atoi(m_parser.header("retry-after").c_str()); + if (retry_time <= 0) retry_time = m_ses.settings().urlseed_wait_retry; + // temporarily unavailable, retry later + t->retry_web_seed(this, retry_time); + std::string error_msg = to_string(m_parser.status_code()).elems + + (" " + m_parser.message()); + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), m_url + , error_msg)); + } + m_statistics.received_bytes(0, bytes_transferred); + disconnect(error_code(m_parser.status_code(), get_http_category()), 1); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + if (is_redirect(m_parser.status_code())) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + m_statistics.received_bytes(0, bytes_transferred); + + if (location.empty()) + { + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::missing_location, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + // add the redirected url and remove the current one + if (!single_file_request) + { + TORRENT_ASSERT(!m_file_requests.empty()); + int file_index = m_file_requests.front(); + +// TODO: 2 create a mapping of file-index to redirection URLs. Use that to form +// URLs instead. Support to reconnect to a new server without destructing this +// peer_connection + torrent_info const& info = t->torrent_file(); + std::string path = info.orig_files().file_path(file_index); +#ifdef TORRENT_WINDOWS + convert_path_to_posix(path); +#endif + path = escape_path(path.c_str(), path.length()); + size_t i = location.rfind(path); + if (i == std::string::npos) + { + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::invalid_redirection, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + location.resize(i); + } + else + { + location = resolve_redirect_location(m_url, location); + } + t->add_web_seed(location, web_seed_entry::url_seed + , m_external_auth, m_extra_headers); + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::redirecting, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + std::string const& server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + + m_body_start = m_parser.body_start(); + m_received_body = 0; + m_range_pos = 0; + } + + recv_buffer.begin += m_body_start; + + // we only received the header, no data + if (recv_buffer.left() == 0) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + break; + } + + size_type range_start; + size_type range_end; + if (m_parser.status_code() == 206) + { + boost::tie(range_start, range_end) = m_parser.content_range(); + if (range_start < 0 || range_end < range_start) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::invalid_range); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + // the http range is inclusive + range_end++; + } + else + { + range_start = 0; + range_end = m_parser.content_length(); + if (range_end == -1) + { + m_statistics.received_bytes(0, bytes_transferred); + // we should not try this server again. + t->remove_web_seed(this); + m_web = NULL; + disconnect(errors::no_content_length, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + } + + // ========================= + // === CHUNKED ENCODING === + // ========================= + while (m_parser.chunked_encoding() + && m_chunk_pos >= 0 + && m_chunk_pos < recv_buffer.left()) + { + int header_size = 0; + size_type chunk_size = 0; + buffer::const_interval chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.begin[0] == '\r' || is_hex(chunk_start.begin, 1)); + bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); + if (!ret) + { + TORRENT_ASSERT(int(bytes_transferred) >= chunk_start.left() - m_partial_chunk_header); + bytes_transferred -= chunk_start.left() - m_partial_chunk_header; + m_statistics.received_bytes(0, chunk_start.left() - m_partial_chunk_header); + m_partial_chunk_header = chunk_start.left(); + if (bytes_transferred == 0) return; + break; + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** parsed chunk: %d header_size: %d", chunk_size, header_size); +#endif + TORRENT_ASSERT(int(bytes_transferred) >= header_size - m_partial_chunk_header); + bytes_transferred -= header_size - m_partial_chunk_header; + m_statistics.received_bytes(0, header_size - m_partial_chunk_header); + m_partial_chunk_header = 0; + TORRENT_ASSERT(chunk_size != 0 || chunk_start.left() <= header_size || chunk_start.begin[header_size] == 'H'); + // cut out the chunk header from the receive buffer + TORRENT_ASSERT(m_body_start + m_chunk_pos < INT_MAX); + cut_receive_buffer(header_size, t->block_size() + request_size_overhead, int(m_body_start + m_chunk_pos)); + recv_buffer = receive_buffer(); + recv_buffer.begin += m_body_start; + m_chunk_pos += chunk_size; + if (chunk_size == 0) + { +#ifdef TORRENT_DEBUG + chunk_start = recv_buffer; + chunk_start.begin += m_chunk_pos; + TORRENT_ASSERT(chunk_start.left() == 0 || chunk_start.begin[0] == 'H'); +#endif + m_chunk_pos = -1; + } + // if all of hte receive buffer was just consumed as chunk + // header, we're done + if (bytes_transferred == 0) return; + } + } + + if (m_requests.empty() || m_file_requests.empty()) + { + m_statistics.received_bytes(0, bytes_transferred); + disconnect(errors::http_error, 2); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + return; + } + + size_type left_in_response = range_end - range_start - m_range_pos; + int payload_transferred = int((std::min)(left_in_response, size_type(bytes_transferred))); + + torrent_info const& info = t->torrent_file(); + + peer_request front_request = m_requests.front(); + + TORRENT_ASSERT(m_block_pos >= 0); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** payload_transferred: %d [ %d:%d = %d ]" + , payload_transferred, front_request.piece + , front_request.start, front_request.length); +#endif + m_statistics.received_bytes(payload_transferred, 0); + TORRENT_ASSERT(int(bytes_transferred) >= payload_transferred); + bytes_transferred -= payload_transferred; + m_range_pos += payload_transferred; + m_block_pos += payload_transferred; + if (m_range_pos > range_end - range_start) m_range_pos = range_end - range_start; + + int file_index = m_file_requests.front(); + peer_request in_range = info.orig_files().map_file(file_index, range_start + , int(range_end - range_start)); + + // request start + size_type rs = size_type(in_range.piece) * info.piece_length() + in_range.start; + // request end + size_type re = rs + in_range.length; + // file start + size_type fs = size_type(front_request.piece) * info.piece_length() + front_request.start; + + // the http response body consists of 3 parts + // 1. the middle of a block or the ending of a block + // 2. a number of whole blocks + // 3. the start of a block + // in that order, these parts are parsed. + + bool range_overlaps_request = re >= fs + int(m_piece.size()); + + if (!range_overlaps_request) + { + incoming_piece_fragment((std::min)(payload_transferred + , front_request.length - m_block_pos)); + m_statistics.received_bytes(0, bytes_transferred); + // this means the end of the incoming request ends _before_ the + // first expected byte (fs + m_piece.size()) + + disconnect(errors::invalid_range, 2); + return; + } + + // if the request is contained in the range (i.e. the entire request + // fits in the range) we should not start a partial piece, since we soon + // will receive enough to call incoming_piece() and pass the read buffer + // directly (in the next loop below). + if (range_overlaps_request + && !range_contains(in_range, front_request, info.piece_length())) + { + // the start of the next block to receive is stored + // in m_piece. We need to append the rest of that + // block from the http receive buffer and then + // (if it completed) call incoming_piece() with + // m_piece as buffer. + + int piece_size = int(m_piece.size()); + int copy_size = (std::min)((std::min)(front_request.length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + if (copy_size > m_chunk_pos && m_chunk_pos > 0) copy_size = m_chunk_pos; + if (copy_size > 0) + { + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + TORRENT_ASSERT(int(m_piece.size()) <= front_request.length); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= copy_size); + m_chunk_pos -= copy_size; + } + TORRENT_ASSERT(m_received_body <= range_end - range_start); + TORRENT_ASSERT(int(m_piece.size()) <= front_request.length); + incoming_piece_fragment(copy_size); + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + } + + if (maybe_harvest_block()) + recv_buffer = receive_buffer(); + if (associated_torrent().expired()) return; + } + + // report all received blocks to the bittorrent engine + while (!m_requests.empty() + && range_contains(in_range, m_requests.front(), info.piece_length()) + && m_block_pos >= m_requests.front().length) + { + peer_request r = m_requests.front(); + TORRENT_ASSERT(recv_buffer.left() >= r.length); + + incoming_piece_fragment(r.length); + incoming_piece(r, recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("<== POP REQUEST [ piece: %d start: %d len: %d ]" + , r.piece, r.start, r.length); +#endif + m_requests.pop_front(); + if (associated_torrent().expired()) return; + TORRENT_ASSERT(m_block_pos >= r.length); + m_block_pos -= r.length; + m_received_body += r.length; + TORRENT_ASSERT(receive_buffer().begin + m_body_start == recv_buffer.begin); + TORRENT_ASSERT(m_received_body <= range_end - range_start); + cut_receive_buffer(m_body_start + r.length, t->block_size() + request_size_overhead); + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= r.length); + m_chunk_pos -= r.length; + } + m_body_start = 0; + recv_buffer = receive_buffer(); + } + + if (!m_requests.empty()) + { + if (in_range.start + in_range.length < m_requests.front().start + m_requests.front().length + && (m_received_body + recv_buffer.left() >= range_end - range_start)) + { + int piece_size = int(m_piece.size()); + int copy_size = (std::min)((std::min)(m_requests.front().length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + TORRENT_ASSERT(copy_size >= 0); + if (copy_size > 0) + { + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + incoming_piece_fragment(copy_size); + TORRENT_ASSERT(int(m_piece.size()) == m_received_in_piece); + } + TORRENT_ASSERT(m_received_body == range_end - range_start); + } + } + + TORRENT_ASSERT(m_received_body <= range_end - range_start); + // if we're in chunked encoding mode, we have to wait for the complete + // tail header before we can consider have received the block, otherwise + // we'll get out of sync with the next http response. m_chunk_pos is set + // to -1 when the tail header has been received + if (m_received_body == range_end - range_start + && (!m_parser.chunked_encoding() || m_chunk_pos == -1)) + { + int size_to_cut = recv_buffer.begin - receive_buffer().begin; + + TORRENT_ASSERT(receive_buffer().left() < size_to_cut + 1 + || receive_buffer()[size_to_cut] == 'H'); + + cut_receive_buffer(size_to_cut, t->block_size() + request_size_overhead); + if (m_chunk_pos > 0) + { + TORRENT_ASSERT(m_chunk_pos >= size_to_cut); + m_chunk_pos -= size_to_cut; + } + recv_buffer = receive_buffer(); + m_file_requests.pop_front(); + m_parser.reset(); + m_body_start = 0; + m_received_body = 0; + m_chunk_pos = 0; + m_partial_chunk_header = 0; + + handle_padfile(recv_buffer); + if (associated_torrent().expired()) return; + continue; + } + + if (bytes_transferred == 0 || payload_transferred == 0) + { +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() + == dl_target); +#endif + break; + } + TORRENT_ASSERT(payload_transferred > 0); + } + TORRENT_ASSERT(bytes_transferred == 0); +#ifdef TORRENT_DEBUG + TORRENT_ASSERT(m_statistics.last_payload_downloaded() + + m_statistics.last_protocol_downloaded() == dl_target); +#endif + } + + void web_peer_connection::get_specific_peer_info(peer_info& p) const + { + web_connection_base::get_specific_peer_info(p); + p.flags |= peer_info::local_connection; + p.connection_type = peer_info::web_seed; + } + + void web_peer_connection::handle_padfile(buffer::const_interval& recv_buffer) + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + torrent_info const& info = t->torrent_file(); + + while (!m_file_requests.empty() + && info.orig_files().pad_file_at(m_file_requests.front())) + { + // the next file is a pad file. We didn't actually send + // a request for this since it most likely doesn't exist on + // the web server anyway. Just pretend that we received a + // bunch of zeroes here and pop it again + int file_index = m_file_requests.front(); + m_file_requests.pop_front(); + size_type file_size = info.orig_files().file_size(file_index); + + peer_request front_request = m_requests.front(); + + TORRENT_ASSERT(m_block_pos < front_request.length); + int pad_size = int((std::min)(file_size, size_type(front_request.length - m_block_pos))); + + // insert zeroes to represent the pad file + m_piece.resize(m_piece.size() + size_t(pad_size), 0); + m_block_pos += pad_size; + incoming_piece_fragment(pad_size); + + if (maybe_harvest_block()) + recv_buffer = receive_buffer(); + if (associated_torrent().expired()) return; + } + } +} + diff --git a/apps/Launcher/ext/libtorrent/src/xml_parse.cpp b/apps/Launcher/ext/libtorrent/src/xml_parse.cpp new file mode 100644 index 0000000000..83f3447014 --- /dev/null +++ b/apps/Launcher/ext/libtorrent/src/xml_parse.cpp @@ -0,0 +1,218 @@ +/* + +Copyright (c) 2007-2014, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#include "libtorrent/xml_parse.hpp" + +namespace libtorrent +{ + + TORRENT_EXTRA_EXPORT void xml_parse(char* p, char* end + , boost::function callback) + { + for(;p != end; ++p) + { + char const* start = p; + char const* val_start = 0; + int token; + // look for tag start + for(; p != end && *p != '<'; ++p); + + if (p != start) + { + if (p != end) + { + TORRENT_ASSERT(*p == '<'); + *p = 0; + } + token = xml_string; + callback(token, start, val_start); + if (p != end) *p = '<'; + } + + if (p == end) break; + + // skip '<' + ++p; + if (p != end && p+8 < end && string_begins_no_case("![CDATA[", p)) + { + // CDATA. match '![CDATA[' + p += 8; + start = p; + while (p != end && !string_begins_no_case("]]>", p-2)) ++p; + + // parse error + if (p == end) + { + token = xml_parse_error; + start = "unexpected end of file"; + callback(token, start, val_start); + break; + } + + token = xml_string; + char tmp = p[-2]; + p[-2] = 0; + callback(token, start, val_start); + p[-2] = tmp; + continue; + } + + // parse the name of the tag. + for (start = p; p != end && *p != '>' && !is_space(*p); ++p); + + char* tag_name_end = p; + + // skip the attributes for now + for (; p != end && *p != '>'; ++p); + + // parse error + if (p == end) + { + token = xml_parse_error; + start = "unexpected end of file"; + callback(token, start, val_start); + break; + } + + TORRENT_ASSERT(*p == '>'); + // save the character that terminated the tag name + // it could be both '>' and ' '. + char save = *tag_name_end; + *tag_name_end = 0; + + char* tag_end = p; + if (*start == '/') + { + ++start; + token = xml_end_tag; + callback(token, start, val_start); + } + else if (*(p-1) == '/') + { + *(p-1) = 0; + token = xml_empty_tag; + callback(token, start, val_start); + *(p-1) = '/'; + tag_end = p - 1; + } + else if (*start == '?' && *(p-1) == '?') + { + *(p-1) = 0; + ++start; + token = xml_declaration_tag; + callback(token, start, val_start); + *(p-1) = '?'; + tag_end = p - 1; + } + else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p-2, "--", 2) == 0) + { + start += 3; + *(p-2) = 0; + token = xml_comment; + callback(token, start, val_start); + *(p-2) = '-'; + tag_end = p - 2; + continue; + } + else + { + token = xml_start_tag; + callback(token, start, val_start); + } + + *tag_name_end = save; + + // parse attributes + for (char* i = tag_name_end; i < tag_end; ++i) + { + // find start of attribute name + for (; i != tag_end && is_space(*i); ++i); + if (i == tag_end) break; + start = i; + // find end of attribute name + for (; i != tag_end && *i != '=' && !is_space(*i); ++i); + char* name_end = i; + + // look for equality sign + for (; i != tag_end && *i != '='; ++i); + + // no equality sign found. Report this as xml_tag_content + // instead of a series of key value pairs + if (i == tag_end) + { + char tmp = *i; + *i = 0; // null terminate the content string + token = xml_tag_content; + val_start = 0; + callback(token, start, val_start); + *i = tmp; + break; + } + + ++i; + for (; i != tag_end && is_space(*i); ++i); + // check for parse error (values must be quoted) + if (i == tag_end || (*i != '\'' && *i != '\"')) + { + token = xml_parse_error; + val_start = 0; + start = "unquoted attribute value"; + callback(token, start, val_start); + break; + } + char quote = *i; + ++i; + val_start = i; + for (; i != tag_end && *i != quote; ++i); + // parse error (missing end quote) + if (i == tag_end) + { + token = xml_parse_error; + val_start = 0; + start = "missing end quote on attribute"; + callback(token, start, val_start); + break; + } + save = *i; + *i = 0; + *name_end = 0; + token = xml_attribute; + callback(token, start, val_start); + *name_end = '='; + *i = save; + } + } + } + +} + diff --git a/apps/Launcher/infowidget.cpp b/apps/Launcher/infowidget.cpp new file mode 100644 index 0000000000..45395903bc --- /dev/null +++ b/apps/Launcher/infowidget.cpp @@ -0,0 +1,116 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "infowidget.h" + +#include "syncwidget.h" + +#include +#include +#include + +InfoWidget::InfoWidget(QString name, int totalBytes) + : QGroupBox(nullptr) + , _name(nullptr) + , _bytes(nullptr) + , _progress(nullptr) + , _messagesLeft(nullptr) + , _messagesRight(nullptr) + , _totalBytes(totalBytes) +{ + setFixedHeight(100); + + QGridLayout* layout = new QGridLayout; + layout->setVerticalSpacing(0); + layout->setHorizontalSpacing(10); + layout->setContentsMargins(0, 0, 0, 0); + + _name = new QLabel(name); + layout->addWidget(_name, 0, 0); + + _bytes = new QLabel(""); + layout->addWidget(_bytes, 1, 0); + + _progress = new QProgressBar; + layout->addWidget(_progress, 1, 1); + + _messagesLeft = new QLabel(""); + _messagesRight = new QLabel(""); + + layout->addWidget(_messagesLeft, 2, 0, 1, 2); + layout->addWidget(_messagesRight, 2, 0, 1, 2, Qt::AlignRight); + + setLayout(layout); +} + +void InfoWidget::update(openspace::DownloadManager::FileFuture* f) { + _bytes->setText( + QString("%1 / %2") + .arg(f->currentSize) + .arg(f->totalSize) + ); + _progress->setValue(static_cast(f->progress * 100)); + + if (f->errorMessage.empty()) { + QString t = "Time remaining %1 s"; + _messagesLeft->setText(t.arg(static_cast(f->secondsRemaining))); + } + else { + _messagesLeft->setText(QString::fromStdString(f->errorMessage)); + } +} + +void InfoWidget::update(libtorrent::torrent_status s) { + _bytes->setText( + QString("%1 / %2") + .arg(s.total_wanted_done) + .arg(s.total_wanted) + ); + float progress = static_cast(s.total_wanted_done) / s.total_wanted; + _progress->setValue(static_cast(progress * 100)); + + if (s.error.empty()) { + int bytesPerSecond = s.download_rate; + long long remainingBytes = s.total_wanted - s.total_wanted_done; + if (bytesPerSecond > 0 && remainingBytes > 0) { + float seconds = static_cast(remainingBytes) / bytesPerSecond; + + QString left = "Time remaining %1 s"; + _messagesLeft->setText(left.arg(static_cast(seconds))); + + QString right = "Download Rate: %1 KiB/s"; + _messagesRight->setText(right.arg(bytesPerSecond / 1024)); + } + else + _messagesLeft->setText(""); + } + else { + _messagesLeft->setText(QString::fromStdString(s.error)); + } + +} + +void InfoWidget::error(QString message) { + _messagesLeft->setText(message); +} diff --git a/apps/Launcher/infowidget.h b/apps/Launcher/infowidget.h new file mode 100644 index 0000000000..e2d42f9e4a --- /dev/null +++ b/apps/Launcher/infowidget.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __INFOWIDGET_H__ +#define __INFOWIDGET_H__ + +//#include +#include + +#include + +#include + +class QLabel; +class QProgressBar; + +class InfoWidget : public QGroupBox { +Q_OBJECT +public: + InfoWidget(QString name, int totalBytes = -1); + + void update(openspace::DownloadManager::FileFuture* f); + void update(libtorrent::torrent_status s); + + void error(QString message); + +private: + InfoWidget(const InfoWidget& rhs) = delete; + + QLabel* _name; + QLabel* _bytes; + QProgressBar* _progress; + QLabel* _messagesLeft; + QLabel* _messagesRight; + + int _totalBytes; +}; + +#endif // __INFOWIDGET_H__ diff --git a/apps/Launcher/mainwindow.cpp b/apps/Launcher/mainwindow.cpp index 138a083a13..115b823dd1 100644 --- a/apps/Launcher/mainwindow.cpp +++ b/apps/Launcher/mainwindow.cpp @@ -24,6 +24,18 @@ #include "mainwindow.h" +#include "shortcutwidget.h" +#include "syncwidget.h" + +#include +#include + +#include +#include +#include +#include +#include + #include #include #include @@ -39,13 +51,24 @@ namespace { const QString NewsURL = "http://openspace.itn.liu.se/news.txt"; - const QString ModulesDirectory = "../data/scene"; // temporary ---abock + const std::string _configurationFile = "openspace.cfg"; #ifdef WIN32 const QString OpenSpaceExecutable = "OpenSpace.exe"; #else const QString OpenSpaceExecutable = "OpenSpace"; #endif + + class QLog : public ghoul::logging::Log { + public: + void log( + ghoul::logging::LogManager::LogLevel level, + const std::string& category, + const std::string& message + ) { + //qDebug() << QString::fromStdString(category) << ": " << QString::fromStdString(message); + } + }; } MainWindow::MainWindow() @@ -141,10 +164,32 @@ void MainWindow::initialize() { this, SLOT(newsNetworkError()) ); - _shortcutWidget.hide(); - _syncWidget.hide(); + _shortcutWidget = new ShortcutWidget(this, Qt::Popup | Qt::Dialog); + _shortcutWidget->setWindowModality(Qt::WindowModal); + _shortcutWidget->hide(); - QDir d(ModulesDirectory); + _syncWidget = new SyncWidget(this, Qt::Popup | Qt::Dialog); + _syncWidget->setWindowModality(Qt::WindowModal); + _syncWidget->hide(); + + ghoul::logging::LogManager::initialize(ghoul::logging::LogManager::LogLevel::Debug); + LogMgr.addLog(new ghoul::logging::ConsoleLog); + LogMgr.addLog(new ghoul::logging::HTMLLog("LauncherLog.html")); + LogMgr.addLog(new QLog); + + std::string configurationFile = _configurationFile; + bool found = openspace::OpenSpaceEngine::findConfiguration(configurationFile); + if (!found) { + } + + _configuration = new openspace::ConfigurationManager; + _configuration->loadFromFile(configurationFile); + + + QString modulesDirectory = QString::fromStdString( + absPath("${SCENE}") + ); + QDir d(modulesDirectory); d.setFilter(QDir::Files); QFileInfoList list = d.entryInfoList(); @@ -152,21 +197,19 @@ void MainWindow::initialize() { _sceneFiles.insert(i.fileName(), i.absoluteFilePath()); _scenes->addItem(i.fileName()); } + _syncWidget->setSceneFiles(_sceneFiles); } void MainWindow::shortcutButtonPressed() { - _shortcutWidget.show(); + _shortcutWidget->show(); } void MainWindow::syncButtonPressed() { - QString currentScene = _scenes->currentText(); - _syncWidget.setSceneFile(_sceneFiles[currentScene]); - _syncWidget.show(); + _syncWidget->show(); } void MainWindow::startButtonPressed() { - QProcess* p = new QProcess(this); - p->start(OpenSpaceExecutable); + QProcess::startDetached(OpenSpaceExecutable); } void MainWindow::newsNetworkError() { @@ -181,277 +224,3 @@ void MainWindow::newsReadyRead() { _informationWidget->setText(news); _newsReply->deleteLater(); } - -//MainWindow::MainWindow() -// : QWidget(nullptr) -// , _configurationWidget(nullptr) -// , _timeControlWidget(nullptr) -// , _informationWidget(nullptr) -// , _timelineWidget(nullptr) -// , _socket(nullptr) -//{ -// setWindowTitle("OpenSpace Timeline"); -// -// _configurationWidget = new ConfigurationWidget(this); -// _configurationWidget->setMinimumWidth(350); -// _timeControlWidget = new ControlWidget(this); -// _timeControlWidget->setMinimumWidth(350); -// _informationWidget = new InformationWidget(this); -// _informationWidget->setMinimumWidth(350); -// _timelineWidget = new TimelineWidget(this); -// -// QGridLayout* layout = new QGridLayout; -// layout->addWidget(_configurationWidget, 0, 0); -// layout->addWidget(_timeControlWidget, 1, 0); -// layout->addWidget(_informationWidget, 2, 0); -// layout->addWidget(_timelineWidget, 0, 1, 3, 1); -// -// layout->setColumnStretch(1, 5); -// -// -// QObject::connect( -// _configurationWidget, SIGNAL(connect(QString, QString)), -// this, SLOT(onConnect(QString, QString)) -// ); -// QObject::connect( -// _configurationWidget, SIGNAL(disconnect()), -// this, SLOT(onDisconnect()) -// ); -// -// QObject::connect( -// _timeControlWidget, SIGNAL(scriptActivity(QString)), -// this, SLOT(sendScript(QString)) -// ); -// -// setLayout(layout); -// -// _configurationWidget->socketDisconnected(); -// _timeControlWidget->socketDisconnected(); -// _informationWidget->socketDisconnected(); -// _timelineWidget->socketDisconnected(); -//} -// -//MainWindow::~MainWindow() { -// delete _socket; -//} -// -//void MainWindow::onConnect(QString host, QString port) { -// delete _socket; -// -// _socket = new QTcpSocket(this); -// QObject::connect(_socket, SIGNAL(readyRead()), SLOT(readTcpData())); -// QObject::connect(_socket, SIGNAL(connected()), SLOT(onSocketConnected())); -// QObject::connect(_socket, SIGNAL(disconnected()), SLOT(onSocketDisconnected())); -// -// _socket->connectToHost(host, port.toUInt()); -//} -// -//void MainWindow::onDisconnect() { -// delete _socket; -// _socket = nullptr; -//} -// -//void MainWindow::readTcpData() { -// static const uint16_t MessageTypeStatus = 0; -// static const uint16_t MessageTypePlayBookHongKang = 2; -// static const uint16_t MessageTypePlayBookLabel = 3; -// -// QByteArray data = _socket->readAll(); -// -// if (QString(data) == "Connected to SGCT!\r\n") -// return; -// if (QString(data) == "OK\r\n") -// return; -// -// QByteArray messageTypeData = data.left(2); -// union { -// uint16_t value; -// std::array data; -// } messageType; -// std::memcpy(messageType.data.data(), messageTypeData.data(), sizeof(uint16_t)); -// -// switch (messageType.value) { -// case MessageTypeStatus: -// break; -// case MessageTypePlayBookHongKang: -// qDebug() << "Hong Kang Playbook received"; -// break; -// case MessageTypePlayBookLabel: -// qDebug() << "Label Playbook received"; -// break; -// default: -// qDebug() << "Unknown message of type '" << messageType.value << "'"; -// } -// -// switch (messageType.value) { -// case MessageTypeStatus: -// { -// if (_hasHongKangTimeline && _hasLabelTimeline) -// handleStatusMessage(data.mid(2)); -// break; -// } -// case MessageTypePlayBookHongKang: -// case MessageTypePlayBookLabel: -// { -// const char* payloadDebug = data.mid(2).data(); -// -// size_t beginning = 0; -// uint32_t size = readFromBuffer(data.mid(2).data(), beginning); -// -// //qDebug() << "Begin reading data"; -// while (_socket->waitForReadyRead() && data.size() < int(size)) { -// //qDebug() << "."; -// data = data.append(_socket->readAll()); -// //data = data.append(_socket->read(int(size) - data.size())); -// QThread::msleep(50); -// } -// //qDebug() << "Finished reading data. Handling playbook"; -// -// handlePlaybook(data.mid(2)); -// -// //qDebug() << "Finished handling playbook"; -// -// if (messageType.value == MessageTypePlayBookHongKang) -// _hasHongKangTimeline = true; -// if (messageType.value == MessageTypePlayBookLabel) -// _hasLabelTimeline = true; -// -// if (_hasHongKangTimeline && _hasLabelTimeline) { -// fullyConnected(); -// } -// -// break; -// } -// default: -// qDebug() << QString(data); -// } -// -//} -// -//void MainWindow::handleStatusMessage(QByteArray data) { -// const char* buffer = data.data(); -// -// union { -// double value; -// std::array buffer; -// } et; -// std::memmove(et.buffer.data(), buffer, sizeof(double)); -// -// std::vector timeString(24); -// std::memmove(timeString.data(), buffer + sizeof(double), 24); -// -// union { -// double value; -// std::array buffer; -// } delta; -// std::memmove(delta.buffer.data(), buffer + sizeof(double) + 24, sizeof(double)); -// -// _timeControlWidget->update( -// QString::fromStdString(std::string(timeString.begin(), timeString.end())), -// QString::number(delta.value) -// ); -// _timelineWidget->setCurrentTime(std::string(timeString.begin(), timeString.end()), et.value); -//} -// -//std::vector instrumentsFromId(uint16_t instrumentId, std::map instrumentMap) { -// std::vector results; -// for (int i = 0; i < 16; ++i) { -// uint16_t testValue = 1 << i; -// if ((testValue & instrumentId) != 0) { -// std::string t = instrumentMap.at(testValue); -// if (t.empty()) -// qDebug() << "Empty instrument"; -// results.push_back(t); -// } -// } -// return results; -//} -// -//void MainWindow::handlePlaybook(QByteArray data) { -// char* buffer = data.data(); -// size_t currentReadLocation = 0; -// -// uint32_t totalData = readFromBuffer(buffer, currentReadLocation); -// -// uint8_t nTargets = readFromBuffer(buffer, currentReadLocation); -// qDebug() << "Targets: " << nTargets; -// std::map targetMap; -// for (uint8_t i = 0; i < nTargets; ++i) { -// uint8_t id = readFromBuffer(buffer, currentReadLocation); -// std::string value = readFromBuffer(buffer, currentReadLocation); -// qDebug() << QString::fromStdString(value); -// targetMap[id] = value; -// } -// -// uint8_t nInstruments = readFromBuffer(buffer, currentReadLocation); -// qDebug() << "Instruments: " << nInstruments; -// std::map instrumentMap; -// for (uint8_t i = 0; i < nInstruments; ++i) { -// uint16_t id = readFromBuffer(buffer, currentReadLocation); -// std::string value = readFromBuffer(buffer, currentReadLocation); -// qDebug() << QString::fromStdString(value); -// instrumentMap[id] = value; -// } -// -// uint32_t nImages = readFromBuffer(buffer, currentReadLocation); -// std::vector images; -// for (uint32_t i = 0; i < nImages; ++i) { -// Image image; -// image.beginning = readFromBuffer(buffer, currentReadLocation); -// image.ending = readFromBuffer(buffer, currentReadLocation); -// -// image.beginningString = readFromBuffer(buffer, currentReadLocation); -// image.endingString = readFromBuffer(buffer, currentReadLocation); -// -// uint8_t targetId = readFromBuffer(buffer, currentReadLocation); -// uint16_t instrumentId = readFromBuffer(buffer, currentReadLocation); -// image.target = targetMap[targetId]; -// image.instruments = instrumentsFromId(instrumentId, instrumentMap); -// if (image.instruments.empty()) -// qDebug() << "Instruments were empty"; -// images.push_back(image); -// } -// -// _timelineWidget->setData(std::move(images), std::move(targetMap), std::move(instrumentMap)); -// -//} -// -//void MainWindow::sendScript(QString script) { -// if (_socket) { -// _socket->write(("0" + script + "\r\n").toLatin1()); -// //QByteArray data = (QString("0") + script).toLocal8Bit(); -// //qDebug() << data; -// //_socket->write(data); -// //QThread::msleep(25); -// } -// //_socket->write(("0" + script + "\r\n").toLatin1()); -// //_socket->write(("0" + script + "\0").toLatin1()); -//} -// -//void MainWindow::onSocketConnected() { -// _socket->write(QString("1\r\n").toLatin1()); -// //_socket->write(QString("1").toLatin1()); -// -//} -// -//void MainWindow::onSocketDisconnected() { -// _configurationWidget->socketDisconnected(); -// _timeControlWidget->socketDisconnected(); -// _informationWidget->socketDisconnected(); -// _timelineWidget->socketDisconnected(); -// -// _informationWidget->logInformation("Disconnected."); -//} -// -//std::string MainWindow::nextTarget() const { -// return _timelineWidget->nextTarget(); -//} -// -//void MainWindow::fullyConnected() { -// _informationWidget->logInformation("Connected to " + _socket->peerName() + " on port " + QString::number(_socket->peerPort()) + "."); -// -// _configurationWidget->socketConnected(); -// _timeControlWidget->socketConnected(); -// _informationWidget->socketConnected(); -// _timelineWidget->socketConnected(); -//} diff --git a/apps/Launcher/mainwindow.h b/apps/Launcher/mainwindow.h index 109bc4b0b4..f131a8aa25 100644 --- a/apps/Launcher/mainwindow.h +++ b/apps/Launcher/mainwindow.h @@ -27,9 +27,6 @@ #include -#include "shortcutwidget.h" -#include "syncwidget.h" - #include #include #include @@ -38,6 +35,13 @@ class QComboBox; class QNetworkAccessManager; +class ShortcutWidget; +class SyncWidget; + +namespace openspace { + class ConfigurationManager; +} + class MainWindow : public QWidget { Q_OBJECT public: @@ -62,11 +66,11 @@ private: QComboBox* _scenes; QMap _sceneFiles; - ShortcutWidget _shortcutWidget; - SyncWidget _syncWidget; + ShortcutWidget* _shortcutWidget; + SyncWidget* _syncWidget; - QNetworkAccessManager _networkManager; + openspace::ConfigurationManager* _configuration; }; //class MainWindow : public QWidget { diff --git a/apps/Launcher/shortcutwidget.cpp b/apps/Launcher/shortcutwidget.cpp index 899b60c82e..280da6b81f 100644 --- a/apps/Launcher/shortcutwidget.cpp +++ b/apps/Launcher/shortcutwidget.cpp @@ -24,6 +24,6 @@ #include "shortcutwidget.h" -ShortcutWidget::ShortcutWidget(QWidget* parent) - : QWidget(parent) +ShortcutWidget::ShortcutWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f) {} diff --git a/apps/Launcher/shortcutwidget.h b/apps/Launcher/shortcutwidget.h index 5a06a0d388..d488281861 100644 --- a/apps/Launcher/shortcutwidget.h +++ b/apps/Launcher/shortcutwidget.h @@ -30,7 +30,7 @@ class ShortcutWidget : public QWidget { Q_OBJECT public: - ShortcutWidget(QWidget* parent); + ShortcutWidget(QWidget* parent, Qt::WindowFlags f = 0); }; #endif // __SHORTCUTWIDGET_H__ diff --git a/apps/Launcher/syncwidget.cpp b/apps/Launcher/syncwidget.cpp index 30babbe899..7f6540f696 100644 --- a/apps/Launcher/syncwidget.cpp +++ b/apps/Launcher/syncwidget.cpp @@ -24,97 +24,580 @@ #include "syncwidget.h" +#include "infowidget.h" + + #include +#include +#include #include #include -#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include +#include +#include + +#include +#include +#include +#include namespace { + const std::string _loggerCat = "SyncWidget"; + + const int nColumns = 3; + + const int DownloadApplicationVersion = 1; + + const std::string FileDownloadKey = "FileDownload"; + const std::string FileRequestKey = "FileRequest"; + const std::string TorrentFilesKey = "TorrentFiles"; + + const std::string UrlKey = "URL"; + const std::string FileKey = "File"; + const std::string DestinationKey = "Destination"; + const std::string IdentifierKey = "Identifier"; + const std::string VersionKey = "Version"; + + const bool OverwriteFiles = false; + const bool CleanInfoWidgets = true; } -SyncWidget::SyncWidget(QWidget* parent) - : QWidget(parent) +SyncWidget::SyncWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f) + , _sceneLayout(nullptr) + , _session(new libtorrent::session) { setFixedSize(500, 500); - ghoul::initialize(); + QBoxLayout* layout = new QVBoxLayout; + { + QGroupBox* sceneBox = new QGroupBox; + _sceneLayout = new QGridLayout; + sceneBox->setLayout(_sceneLayout); + layout->addWidget(sceneBox); + } + { + QPushButton* syncButton = new QPushButton("Synchronize Scenes"); + QObject::connect( + syncButton, SIGNAL(clicked(bool)), + this, SLOT(syncButtonPressed()) + ); + layout->addWidget(syncButton); + } + + { + QScrollArea* area = new QScrollArea; + area->setWidgetResizable(true); + + QWidget* w = new QWidget; + area->setWidget(w); + + _downloadLayout = new QVBoxLayout(w); + _downloadLayout->setMargin(0); + _downloadLayout->setSpacing(0); + _downloadLayout->addStretch(100); + + layout->addWidget(area); + } + + setLayout(layout); + + ghoul::initialize(); + openspace::DownloadManager::initialize("http://openspace.itn.liu.se/data/request", DownloadApplicationVersion); + + libtorrent::error_code ec; + _session->listen_on(std::make_pair(20285, 20285), ec); + if (ec) { + LFATAL("Failed to open socket: " << ec.message()); + return; + } + _session->start_upnp(); + _session->start_dht(); + + _session->add_dht_router({ "dht.transmissionbt.com", 6881}); + _session->add_dht_router({ "router.bittorrent.com", 6881}); + _session->add_dht_router({ "router.utorrent.com", 6881 }); + _session->add_dht_router({ "router.bitcomet.com", 6881 }); + + QTimer* timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(handleTimer())); + timer->start(100); + + _mutex.clear(); } -void SyncWidget::setSceneFile(QString scene) { - clear(); +SyncWidget::~SyncWidget() { + openspace::DownloadManager::deinitialize(); + ghoul::deinitialize(); + delete _session; +} - qDebug() << scene; +void SyncWidget::setSceneFiles(QMap sceneFiles) { + _sceneFiles = std::move(sceneFiles); + QStringList keys = _sceneFiles.keys(); + for (int i = 0; i < keys.size(); ++i) { + const QString& sceneName = keys[i]; - ghoul::Dictionary sceneDictionary; - ghoul::lua::loadDictionaryFromFile( - scene.toStdString(), - sceneDictionary - ); + QCheckBox* checkbox = new QCheckBox(sceneName); - ghoul::Dictionary modules; - bool success = sceneDictionary.getValue("Modules", modules); - qDebug() << success; + checkbox->setChecked(true); - QStringList modulesList; - for (int i = 1; i < modules.size(); ++i) { - std::string module = modules.value(std::to_string(i)); - modulesList.append(QString::fromStdString(module)); - } - qDebug() << modulesList; - - QDir sceneDir(scene); - sceneDir.cdUp(); - for (QString module : modulesList) { - QString moduleFile = sceneDir.absoluteFilePath(module + "/" + module + ".mod"); - QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); - - qDebug() << module; - qDebug() << moduleFile << QFileInfo(moduleFile).exists(); - qDebug() << dataFile << QFileInfo(dataFile).exists(); - - if (QFileInfo(dataFile).exists()) { - ghoul::Dictionary dataDictionary; - ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); - - ghoul::Dictionary directFiles; - ghoul::Dictionary torrentFiles; - - bool found = dataDictionary.getValue("Files", directFiles); - if (found) { - QStringList files; - for (int i = 1; i < directFiles.size(); ++i) { - std::string f = directFiles.value(std::to_string(i)); - files.append(QString::fromStdString(f)); - } - handleDirectFiles(module, files); - } - - found = dataDictionary.getValue("Torrents", torrentFiles); - if (found) { - QStringList torrents; - for (int i = 1; i < torrentFiles.size(); ++i) { - std::string f = torrentFiles.value(std::to_string(i)); - torrents.append(QString::fromStdString(f)); - } - handleTorrentFiles(module, torrents); - } - } + _sceneLayout->addWidget(checkbox, i / nColumns, i % nColumns); } } void SyncWidget::clear() { + for (openspace::DownloadManager::FileFuture* f : _futures) + f->abortDownload = true; + + using libtorrent::torrent_handle; + for (QMap::iterator i = _torrentInfoWidgetMap.begin(); + i != _torrentInfoWidgetMap.end(); + ++i) + { + delete i.value(); + } + _torrentInfoWidgetMap.clear(); + _session->abort(); + _directFiles.clear(); + _fileRequests.clear(); + _torrentFiles.clear(); } -void SyncWidget::handleDirectFiles(QString module, QStringList files) { - qDebug() << files; +void SyncWidget::handleDirectFiles() { + LDEBUG("Direct Files"); + for (const DirectFile& f : _directFiles) { + LDEBUG(f.url.toStdString() << " -> " << f.destination.toStdString()); + + openspace::DownloadManager::FileFuture* future = DlManager.downloadFile( + f.url.toStdString(), + absPath("${SCENE}/" + f.module.toStdString() + "/" + f.destination.toStdString()), + OverwriteFiles + ); + if (future) { + InfoWidget* w = new InfoWidget(f.destination); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + + _futures.push_back(future); + _futureInfoWidgetMap[future] = w; + } + } } -void SyncWidget::handleTorrentFiles(QString module, QStringList torrents) { - qDebug() << torrents; +void SyncWidget::handleFileRequest() { + LDEBUG("File Requests"); + for (const FileRequest& f : _fileRequests) { + LDEBUG(f.identifier.toStdString() << " (" << f.version << ") -> " << f.destination.toStdString()); + + ghoul::filesystem::Directory d = FileSys.currentDirectory(); + std::string thisDirectory = absPath("${SCENE}/" + f.module.toStdString() + "/"); + FileSys.setCurrentDirectory(thisDirectory); + + + std::string identifier = f.identifier.toStdString(); + std::string path = absPath(f.destination.toStdString()); + int version = f.version; + + DlManager.downloadRequestFilesAsync( + identifier, + path, + version, + OverwriteFiles, + std::bind(&SyncWidget::handleFileFutureAddition, this, std::placeholders::_1) + ); + + FileSys.setCurrentDirectory(d); + } +} + +void SyncWidget::handleTorrentFiles() { + LDEBUG("Torrent Files"); + for (const TorrentFile& f : _torrentFiles) { + LDEBUG(f.file.toStdString() << " -> " << f.destination.toStdString()); + + ghoul::filesystem::Directory d = FileSys.currentDirectory(); + std::string thisDirectory = absPath("${SCENE}/" + f.module.toStdString() + "/"); + FileSys.setCurrentDirectory(thisDirectory); + + QString file = QString::fromStdString(absPath(f.file.toStdString())); + + if (!QFileInfo(file).exists()) { + LERROR(file.toStdString() << " does not exist"); + continue; + } + + libtorrent::error_code ec; + libtorrent::add_torrent_params p; + + //if (f.destination.isEmpty()) + //p.save_path = absPath(fullPath(f.module, ".").toStdString()); + //else + //p.save_path = + p.save_path = absPath(f.destination.toStdString()); + + p.ti = new libtorrent::torrent_info(file.toStdString(), ec); + p.name = f.file.toStdString(); + p.storage_mode = libtorrent::storage_mode_allocate; + p.auto_managed = true; + if (ec) { + LERROR(f.file.toStdString() << ": " << ec.message()); + continue; + } + libtorrent::torrent_handle h = _session->add_torrent(p, ec); + if (ec) { + LERROR(f.file.toStdString() << ": " << ec.message()); + continue; + } + + InfoWidget* w = new InfoWidget(f.file, h.status().total_wanted); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + _torrentInfoWidgetMap[h] = w; + + FileSys.setCurrentDirectory(d); + } +} + +void SyncWidget::syncButtonPressed() { + clear(); + + for (const QString& scene : selectedScenes()) { + LDEBUG(scene.toStdString()); + ghoul::Dictionary sceneDictionary; + ghoul::lua::loadDictionaryFromFile( + scene.toStdString(), + sceneDictionary + ); + + ghoul::Dictionary modules; + bool success = sceneDictionary.getValue("Modules", modules); + + QStringList modulesList; + for (int i = 1; i <= modules.size(); ++i) { + std::string module = modules.value(std::to_string(i)); + modulesList.append(QString::fromStdString(module)); + } + modulesList.append("common"); + + QDir sceneDir(scene); + sceneDir.cdUp(); + for (QString module : modulesList) { + QString dataFile = sceneDir.absoluteFilePath(module + "/" + module + ".data"); + + if (QFileInfo(dataFile).exists()) { + ghoul::Dictionary dataDictionary; + ghoul::lua::loadDictionaryFromFile(dataFile.toStdString(), dataDictionary); + + ghoul::Dictionary directDownloadFiles; + ghoul::Dictionary fileRequests; + ghoul::Dictionary torrentFiles; + + bool found = dataDictionary.getValue(FileDownloadKey, directDownloadFiles); + if (found) { + for (int i = 1; i <= directDownloadFiles.size(); ++i) { + if (!directDownloadFiles.hasKeyAndValue(std::to_string(i))) { + LERROR(dataFile.toStdString() << ": " << FileDownloadKey << " is not a dictionary"); + continue; + } + ghoul::Dictionary d = directDownloadFiles.value(std::to_string(i)); + if (!d.hasKeyAndValue(UrlKey)) { + LERROR(dataFile.toStdString() << ": No " << UrlKey); + continue; + } + std::string url = d.value(UrlKey); + + std::string dest = ""; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); + + _directFiles.append({ + module, + QString::fromStdString(url), + QString::fromStdString(dest) + }); + } + } + + found = dataDictionary.getValue(FileRequestKey, fileRequests); + if (found) { + for (int i = 1; i <= fileRequests.size(); ++i) { + ghoul::Dictionary d = fileRequests.value(std::to_string(i)); + + if (!d.hasKeyAndValue(IdentifierKey)) { + LERROR(dataFile.toStdString() << ": No " << IdentifierKey); + continue; + } + std::string url = d.value(IdentifierKey); + + std::string dest = ""; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); + + if (!d.hasKeyAndValue(VersionKey)) { + LERROR(dataFile.toStdString() << ": No " << VersionKey); + continue; + } + int version = static_cast(d.value(VersionKey)); + + _fileRequests.append({ + module, + QString::fromStdString(url), + QString::fromStdString(dest), + version + }); + } + } + + found = dataDictionary.getValue(TorrentFilesKey, torrentFiles); + if (found) { + for (int i = 1; i <= torrentFiles.size(); ++i) { + ghoul::Dictionary d = torrentFiles.value(std::to_string(i)); + + if (!d.hasKeyAndValue(FileKey)) { + LERROR(dataFile.toStdString() << ": No " << FileKey); + continue; + } + std::string file = d.value(FileKey); + + std::string dest; + if (d.hasKeyAndValue(DestinationKey)) + dest = d.value(DestinationKey); + else + dest = ""; + + _torrentFiles.append({ + module, + QString::fromStdString(file), + QString::fromStdString(dest) + }); + } + } + } + } + } + + //// Make the lists unique + { + auto equal = [](const DirectFile& lhs, const DirectFile& rhs) -> bool { + return lhs.module == rhs.module && lhs.url == rhs.url && lhs.destination == rhs.destination; + }; + + QList files; + for (const DirectFile& f : _directFiles) { + bool found = false; + for (const DirectFile& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _directFiles = files; + } + { + auto equal = [](const FileRequest& lhs, const FileRequest& rhs) -> bool { + return + lhs.module == rhs.module && + lhs.identifier == rhs.identifier && + lhs.destination == rhs.destination && + lhs.version == rhs.version; + }; + + QList files; + for (const FileRequest& f : _fileRequests) { + bool found = false; + for (const FileRequest& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _fileRequests = files; + } + { + auto equal = [](const TorrentFile& lhs, const TorrentFile& rhs) -> bool { + return + lhs.module == rhs.module && + lhs.file == rhs.file && + lhs.destination == rhs.destination; + }; + + QList files; + for (const TorrentFile& f : _torrentFiles) { + bool found = false; + for (const TorrentFile& g : files) { + if (equal(g, f)) { + found = true; + break; + } + } + + if (!found) + files.append(f); + } + + _torrentFiles = files; + } + + handleDirectFiles(); + handleFileRequest(); + handleTorrentFiles(); +} + +QStringList SyncWidget::selectedScenes() const { + QStringList result; + int nChildren = _sceneLayout->count(); + for (int i = 0; i < nChildren; ++i) { + QWidget* w = _sceneLayout->itemAt(i)->widget(); + QCheckBox* c = static_cast(w); + if (c->isChecked()) { + QString t = c->text(); + result.append(_sceneFiles[t]); + } + } + std::string scenes; + for (QString s : result) + scenes += s.toStdString() + "; "; + LDEBUG("Downloading scenes: " << scenes); + return result; +} + +void SyncWidget::handleTimer() { + using namespace libtorrent; + using FileFuture = openspace::DownloadManager::FileFuture; + + std::vector toRemove; + for (FileFuture* f : _futures) { + InfoWidget* w = _futureInfoWidgetMap[f]; + + if (CleanInfoWidgets && (f->isFinished || f->isAborted)) { + toRemove.push_back(f); + _downloadLayout->removeWidget(w); + _futureInfoWidgetMap.erase(f); + delete w; + } + else + w->update(f); + } + + for (FileFuture* f : toRemove) { + _futures.erase(std::remove(_futures.begin(), _futures.end(), f), _futures.end()); + delete f; + } + + while (_mutex.test_and_set()) {} + for (openspace::DownloadManager::FileFuture* f : _futuresToAdd) { + InfoWidget* w = new InfoWidget(QString::fromStdString(f->filePath), -1); + _downloadLayout->insertWidget(_downloadLayout->count() - 1, w); + + _futureInfoWidgetMap[f] = w; + _futures.push_back(f); + } + _futuresToAdd.clear(); + _mutex.clear(); + + + std::vector handles = _session->get_torrents(); + for (torrent_handle h : handles) { + torrent_status s = h.status(); + InfoWidget* w = _torrentInfoWidgetMap[h]; + + if (w) + w->update(s); + + if (CleanInfoWidgets && (s.state == torrent_status::finished || s.state == torrent_status::seeding)) { + _torrentInfoWidgetMap.remove(h); + delete w; + } + } + + // Only close every torrent if all torrents are finished + bool allSeeding = true; + for (torrent_handle h : handles) { + torrent_status s = h.status(); + allSeeding &= (s.state == torrent_status::seeding); + } + + if (allSeeding) { + for (torrent_handle h : handles) + _session->remove_torrent(h); + } + + + + + //_session->post_torrent_updates(); + //libtorrent::session_settings settings = _session->settings(); + + //qDebug() << "Session"; + //qDebug() << "nPeers: " << _session->status().num_peers; + //qDebug() << "DHT: " << _session->is_dht_running(); + //qDebug() << "Incoming TCP" << settings.enable_incoming_tcp; + //qDebug() << "Outgoing TCP" << settings.enable_outgoing_tcp; + //qDebug() << "Incoming UTP" << settings.enable_incoming_utp; + //qDebug() << "Outgoing UTP" << settings.enable_outgoing_utp; + //qDebug() << "==="; + + //qDebug() << "Alerts"; + //std::deque alerts; + //_session->pop_alerts(&alerts); + //for (alert* a : alerts) { + // qDebug() << QString::fromStdString(a->message()); + + // //if (a->category() == alert::status_notification) { + // // state_update_alert* sua = static_cast(a); + // // for (torrent_status s ) + // //} + //} + //qDebug() << "==="; + + + // qDebug() << "Name: " << QString::fromStdString(h.name()); + // //torrent_status s = h.status(); + + // qDebug() << "Error: " << QString::fromStdString(s.error); + + // qDebug() << "Total Wanted: " << s.total_wanted; + // qDebug() << "Total Wanted Done: " << s.total_wanted_done; + // qDebug() << "Has Incoming: " << s.has_incoming; + // qDebug() << "Connect Candidates: " << s.connect_candidates; + // qDebug() << "Last Seen Complete: " << s.last_seen_complete; + // qDebug() << "List Peers: " << s.list_peers; + // qDebug() << "List Seeds: " << s.list_seeds; + // qDebug() << "Num Pieces: " << s.num_pieces; + // qDebug() << "Download Rate: " << s.download_rate; + // qDebug() << "List Seeds: " << s.list_seeds; + // qDebug() << "Paused: " << s.paused; + // qDebug() << "Progress: " << s.progress; + + // qDebug() << ""; +} + +void SyncWidget::handleFileFutureAddition( + const std::vector& futures) +{ + while (_mutex.test_and_set()) {} + _futuresToAdd.insert(_futuresToAdd.end(), futures.begin(), futures.end()); + _mutex.clear(); } diff --git a/apps/Launcher/syncwidget.h b/apps/Launcher/syncwidget.h index ef206914f4..e1e560b021 100644 --- a/apps/Launcher/syncwidget.h +++ b/apps/Launcher/syncwidget.h @@ -27,16 +27,83 @@ #include +#include + +#include + +#include + +#include +//#include + +class QBoxLayout; +class QGridLayout; + +class InfoWidget; + +namespace libtorrent { + class session; + struct torrent_handle; +} + class SyncWidget : public QWidget { +Q_OBJECT public: - SyncWidget(QWidget* parent); + SyncWidget(QWidget* parent, Qt::WindowFlags f = 0); + ~SyncWidget(); - void setSceneFile(QString scene); + void setSceneFiles(QMap sceneFiles); + +private slots: + void syncButtonPressed(); + void handleTimer(); private: + struct DirectFile { + QString module; + QString url; + QString destination; + }; + + struct FileRequest { + QString module; + QString identifier; + QString destination; + int version; + }; + + struct TorrentFile { + QString module; + QString file; + QString destination; + }; + void clear(); - void handleDirectFiles(QString module, QStringList files); - void handleTorrentFiles(QString module, QStringList torrents); + QStringList selectedScenes() const; + + void handleFileFutureAddition(const std::vector& futures); + + void handleDirectFiles(); + void handleFileRequest(); + void handleTorrentFiles(); + + QMap _sceneFiles; + QString _modulesDirectory; + QGridLayout* _sceneLayout; + QBoxLayout* _downloadLayout; + + libtorrent::session* _session; + QMap _torrentInfoWidgetMap; + + QList _directFiles; + QList _fileRequests; + QList _torrentFiles; + + std::vector _futures; + std::map _futureInfoWidgetMap; + + std::vector _futuresToAdd; + std::atomic_flag _mutex; }; #endif // __SYNCWIDGET_H__ diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 0edbe2edc9..2854a29e94 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -53,7 +53,7 @@ std::pair supportedOpenGLVersion () { //On OS X we need to explicitly set the version and specify that we are using CORE profile //to be able to use glGetIntegerv(GL_MAJOR_VERSION, &major) and glGetIntegerv(GL_MINOR_VERSION, &minor) //explicitly setting to OGL 3.3 CORE works since all Mac's now support at least 3.3 -#if __APPLE__ +#ifdef __APPLE__ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); @@ -73,12 +73,6 @@ std::pair supportedOpenGLVersion () { return { major, minor }; } -//temporary post-FX functions, TODO make a more permanent solution to this @JK -void postFXPass(); -void setupPostFX(); -GLint _postFXTexLoc; -GLint _postFXOpacityLoc; - #include namespace { @@ -195,9 +189,6 @@ void mainInitFunc() { std::cin.ignore(100); exit(EXIT_FAILURE); } - - //temporary post-FX solution, TODO add a more permanent solution @JK - setupPostFX(); } void mainPreSyncFunc() { @@ -272,26 +263,3 @@ void mainLogCallback(const char* msg){ // Remove the trailing \n that is passed along LINFOC("SGCT", message.substr(0, std::max(message.size() - 1, 0))); } - -void postFXPass(){ - glUniform1i(_postFXTexLoc, 0); - if (OsEng.isMaster()) - glUniform1f(_postFXOpacityLoc, 1.f); - else - glUniform1f(_postFXOpacityLoc, OsEng.renderEngine()->globalBlackOutFactor()); -} - -void setupPostFX(){ -#ifndef __APPLE__ - sgct::PostFX fx[1]; - sgct::ShaderProgram *shader; - fx[0].init("OpacityControl", absPath("${SHADERS}/postFX_vs.glsl"), absPath("${SHADERS}/postFX_fs.glsl")); - fx[0].setUpdateUniformsFunction(postFXPass); - shader = fx[0].getShaderProgram(); - shader->bind(); - _postFXTexLoc = shader->getUniformLocation("Tex"); - _postFXOpacityLoc = shader->getUniformLocation("Opacity"); - shader->unbind(); - _sgctEngine->addPostFX(fx[0]); -#endif -} diff --git a/apps/TimelineView/CMakeLists.txt b/apps/TimelineView/CMakeLists.txt index 284192421e..c3fc56f1ba 100644 --- a/apps/TimelineView/CMakeLists.txt +++ b/apps/TimelineView/CMakeLists.txt @@ -45,6 +45,7 @@ set(HEADER_FILES find_package(Qt5Widgets) find_package(Qt5Network) +set(MOC_FILES "") qt5_wrap_cpp(MOC_FILES ${HEADER_FILES}) add_executable(${APPLICATION_NAME} MACOSX_BUNDLE ${SOURCE_FILES} ${HEADER_FILES} ${MOC_FILES}) diff --git a/data b/data index f3928948f2..2acb59c5fc 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f3928948f25ac520240a4c7a6ac280b278ddf3b7 +Subproject commit 2acb59c5fc3ac88a473aee3337ef41ec216131d6 diff --git a/ext/curl/include/curl/Makefile.am b/ext/curl/include/curl/Makefile.am new file mode 100644 index 0000000000..86e8b78344 --- /dev/null +++ b/ext/curl/include/curl/Makefile.am @@ -0,0 +1,53 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h curlbuild.h curlrules.h + +pkgincludedir= $(includedir)/curl + +# curlbuild.h does not exist in the git tree. When the original libcurl +# source code distribution archive file is created, curlbuild.h.dist is +# renamed to curlbuild.h and included in the tarball so that it can be +# used directly on non-configure systems. +# +# The distributed curlbuild.h will be overwritten on configure systems +# when the configure script runs, with one that is suitable and specific +# to the library being configured and built. +# +# curlbuild.h.in is the distributed template file from which the configure +# script creates curlbuild.h at library configuration time, overwiting the +# one included in the distribution archive. +# +# curlbuild.h.dist is not included in the source code distribution archive. + +EXTRA_DIST = curlbuild.h.in + +DISTCLEANFILES = curlbuild.h + +checksrc: + @@PERL@ $(top_srcdir)/lib/checksrc.pl -Wcurlbuild.h -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) $(EXTRA_DIST) + +if CURLDEBUG +# for debug builds, we scan the sources on all regular make invokes +all-local: checksrc +endif diff --git a/ext/curl/include/curl/Makefile.in b/ext/curl/include/curl/Makefile.in new file mode 100644 index 0000000000..fa07d3258c --- /dev/null +++ b/ext/curl/include/curl/Makefile.in @@ -0,0 +1,692 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/curl +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/curlbuild.h.in $(top_srcdir)/mkinstalldirs \ + $(pkginclude_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/curl-compilers.m4 \ + $(top_srcdir)/m4/curl-confopts.m4 \ + $(top_srcdir)/m4/curl-functions.m4 \ + $(top_srcdir)/m4/curl-openssl.m4 \ + $(top_srcdir)/m4/curl-override.m4 \ + $(top_srcdir)/m4/curl-reentrant.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/xc-am-iface.m4 \ + $(top_srcdir)/m4/xc-cc-check.m4 \ + $(top_srcdir)/m4/xc-lt-iface.m4 \ + $(top_srcdir)/m4/xc-translit.m4 \ + $(top_srcdir)/m4/xc-val-flgs.m4 \ + $(top_srcdir)/m4/zz40-xc-ovr.m4 \ + $(top_srcdir)/m4/zz50-xc-ovr.m4 \ + $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/lib/curl_config.h curlbuild.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgincludedir)" +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)curlbuild.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkgincludedir = $(includedir)/curl +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ +CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ +CURLVERSION = @CURLVERSION@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ +CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ +CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ +CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ +CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ +CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ +CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ +CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ +CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_SHARED = @ENABLE_SHARED@ +ENABLE_STATIC = @ENABLE_STATIC@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ +HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ +IDN_ENABLED = @IDN_ENABLED@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCURL_LIBS = @LIBCURL_LIBS@ +LIBMETALINK_CPPFLAGS = @LIBMETALINK_CPPFLAGS@ +LIBMETALINK_LDFLAGS = @LIBMETALINK_LDFLAGS@ +LIBMETALINK_LIBS = @LIBMETALINK_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MANOPT = @MANOPT@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NROFF = @NROFF@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSL_ENABLED = @SSL_ENABLED@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SUPPORT_FEATURES = @SUPPORT_FEATURES@ +SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ +USE_ARES = @USE_ARES@ +USE_AXTLS = @USE_AXTLS@ +USE_CYASSL = @USE_CYASSL@ +USE_DARWINSSL = @USE_DARWINSSL@ +USE_GNUTLS = @USE_GNUTLS@ +USE_GNUTLS_NETTLE = @USE_GNUTLS_NETTLE@ +USE_LIBRTMP = @USE_LIBRTMP@ +USE_LIBSSH2 = @USE_LIBSSH2@ +USE_NGHTTP2 = @USE_NGHTTP2@ +USE_NSS = @USE_NSS@ +USE_OPENLDAP = @USE_OPENLDAP@ +USE_POLARSSL = @USE_POLARSSL@ +USE_SCHANNEL = @USE_SCHANNEL@ +USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ +USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libext = @libext@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +pkginclude_HEADERS = \ + curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ + typecheck-gcc.h curlbuild.h curlrules.h + + +# curlbuild.h does not exist in the git tree. When the original libcurl +# source code distribution archive file is created, curlbuild.h.dist is +# renamed to curlbuild.h and included in the tarball so that it can be +# used directly on non-configure systems. +# +# The distributed curlbuild.h will be overwritten on configure systems +# when the configure script runs, with one that is suitable and specific +# to the library being configured and built. +# +# curlbuild.h.in is the distributed template file from which the configure +# script creates curlbuild.h at library configuration time, overwiting the +# one included in the distribution archive. +# +# curlbuild.h.dist is not included in the source code distribution archive. +EXTRA_DIST = curlbuild.h.in +DISTCLEANFILES = curlbuild.h +all: curlbuild.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/curl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/curl/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +curlbuild.h: stamp-h2 + @test -f $@ || rm -f stamp-h2 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h2 + +stamp-h2: $(srcdir)/curlbuild.h.in $(top_builddir)/config.status + @rm -f stamp-h2 + cd $(top_builddir) && $(SHELL) ./config.status include/curl/curlbuild.h + +distclean-hdr: + -rm -f curlbuild.h stamp-h2 + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@CURLDEBUG_FALSE@all-local: +all-am: Makefile $(HEADERS) curlbuild.h all-local +installdirs: + for dir in "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkgincludeHEADERS + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ + clean-generic clean-libtool cscopelist-am ctags ctags-am \ + distclean distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkgincludeHEADERS \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pkgincludeHEADERS + + +checksrc: + @@PERL@ $(top_srcdir)/lib/checksrc.pl -Wcurlbuild.h -D$(top_srcdir)/include/curl $(pkginclude_HEADERS) $(EXTRA_DIST) + +# for debug builds, we scan the sources on all regular make invokes +@CURLDEBUG_TRUE@all-local: checksrc + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ext/curl/include/curl/curl.h b/ext/curl/include/curl/curl.h new file mode 100644 index 0000000000..ae1b0e4dbc --- /dev/null +++ b/ext/curl/include/curl/curl.h @@ -0,0 +1,2362 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * http://curl.haxx.se/libcurl/ + * + * curl-library mailing list subscription and unsubscription web interface: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + */ + +#include "curlver.h" /* libcurl version defines */ +#include "curlbuild.h" /* libcurl build definitions */ +#include "curlrules.h" /* libcurl rules enforcement */ + +/* + * Define WIN32 when build target is Win32 API + */ + +#if (defined(_WIN32) || defined(__WIN32__)) && \ + !defined(WIN32) && !defined(__SYMBIAN32__) +#define WIN32 +#endif + +#include +#include + +#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) +/* Needed for __FreeBSD_version symbol definition */ +#include +#endif + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || defined(__LWIP_OPT_H__)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#include +#endif +#endif + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on systems that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) +#include +#endif + +#if !defined(WIN32) && !defined(_WIN32_WCE) +#include +#endif + +#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#include +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURL; + +/* + * libcurl external API function linkage decorations. + */ + +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +# define CURL_EXTERN +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#if defined(WIN32) && !defined(__LWIP_OPT_H__) +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ +#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ +#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ +#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer + do not free in formfree */ +#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer + do not free in formfree */ +#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ +#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ +#define HTTPPOST_CALLBACK (1<<6) /* upload file contents by using the + regular read callback to get the data + and pass the given pointer as custom + pointer */ + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ +}; + +/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered + deprecated but was the only choice up until 7.31.0 */ +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in + 7.32.0, it avoids floating point and provides more detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. The practical minimum is about + 400 bytes since libcurl uses a buffer of this size as a scratch area + (unrelated to network send operations). */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + + + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE = 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Content of this structure depends on information which is known and is + achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man + page for callbacks returning this structure -- some fields are mandatory, + some others are optional. The FLAG field has special meaning. */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; + unsigned int perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + unsigned int flags; + + /* used internally */ + char * b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task */ +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called before + download of an individual chunk started. Note that parameter "remains" works + only for FTP wildcard downloading (for now), otherwise is not used */ +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task */ + +/* If splitting of data transfer is enabled this callback is called after + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we don't need "transfer_info" parameter in this + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ +typedef int (*curl_fnmatch_callback)(void *ptr, + const char *pattern, + const char *string); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT + +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED + +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + +#endif /*!CURL_NO_OLDIES*/ + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +#define CURL_ERROR_SIZE 256 + +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS +}; + +struct curl_khkey { + const char *key; /* points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum curl_khtype keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now so + this causes a CURLE_DEFER error but otherwise the + connection will be left intact etc */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed from app */ + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /*!CURL_NO_OLDIES*/ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif + +#ifdef CURL_ISOCPP +#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(WRITEDATA, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "user:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(READDATA, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(HEADERDATA, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, OBJECTPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, OBJECTPOINT, 173), + CINIT(PASSWORD, OBJECTPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, OBJECTPOINT, 175), + CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, OBJECTPOINT, 186), + + /* set the SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CINIT(DNS_SERVERS, OBJECTPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of miliseconds. */ + CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + + /* Set TCP keepalive */ + CINIT(TCP_KEEPALIVE, LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CINIT(TCP_KEEPIDLE, LONG, 214), + CINIT(TCP_KEEPINTVL, LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CINIT(SSL_OPTIONS, LONG, 216), + + /* Set the SMTP auth originator */ + CINIT(MAIL_AUTH, OBJECTPOINT, 217), + + /* Enable/disable SASL initial response */ + CINIT(SASL_IR, LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CINIT(XOAUTH2_BEARER, OBJECTPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_INTERFACE, OBJECTPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP4, OBJECTPOINT, 222), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP6, OBJECTPOINT, 223), + + /* Set authentication options directly */ + CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, OBJECTPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, OBJECTPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2.0 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + libcurl, see lib/README.curlx for details */ +CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); +CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); + +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + * + * This function is not thread-safe! + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_POLARSSL = 6, + CURLSSLBACKEND_CYASSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_DARWINSSL = 9, + CURLSSLBACKEND_AXTLS = 10 +} curl_sslbackend; + +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_SLIST + 43, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 43 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + +typedef void CURLSH; + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_FOURTH, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_FOURTH + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is suported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ + + /* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + +#endif /* __CURL_CURL_H */ diff --git a/ext/curl/include/curl/curlbuild.h b/ext/curl/include/curl/curlbuild.h new file mode 100644 index 0000000000..f09419a843 --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h @@ -0,0 +1,585 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * See file include/curl/curlbuild.h.in, run configure, and forget + * that this file exists it is only used for non-configure systems. + * But you can keep reading if you want ;-) + * + */ + +/* ================================================================ */ +/* NOTES FOR NON-CONFIGURE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * Try to keep one section per platform, compiler and architecture, + * otherwise, if an existing section is reused for a different one and + * later on the original is adjusted, probably the piggybacking one can + * be adversely changed. + * + * In order to differentiate between platforms/compilers/architectures + * use only compiler built in predefined preprocessor symbols. + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a + * 64-bit wide signed integral data type. The width of this data type + * must remain constant and independent of any possible large file + * support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a + * 32-bit wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This + * rule shall only be violated if off_t is the only 64-bit data type + * available and the size of off_t is independent of large file support + * settings. Keep your build on the safe side avoiding an off_t gating. + * If you have a 64-bit off_t then take for sure that another 64-bit + * data type exists, dig deeper and you will find it. + * + * NOTE 3: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.dist or + * at file include/curl/curlbuild.h, this is due to the following reason: + * file include/curl/curlbuild.h.dist is renamed to include/curl/curlbuild.h + * when the libcurl source code distribution archive file is created. + * + * File include/curl/curlbuild.h.dist is not included in the distribution + * archive. File include/curl/curlbuild.h is not present in the git tree. + * + * The distributed include/curl/curlbuild.h file is only intended to be used + * on systems which can not run the also distributed configure script. + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + * If you check out from git on a non-configure platform, you must run the + * appropriate buildconf* script to set up curlbuild.h and other local files. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR NON-CONFIGURE SYSTEMS ONLY */ +/* ================================================================ */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SALFORDC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__TURBOC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__LCC__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MWERKS__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(_WIN32_WCE) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MINGW32__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURL_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURL_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURL_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURL_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_SIZEOF_LONG 8 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_OFF_T "%I64d" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 4 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) +# if defined(__ILP32__) || \ + defined(__i386__) || defined(__ppc__) || defined(__arm__) || defined(__sparc__) +# define CURL_SIZEOF_LONG 4 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_FORMAT_OFF_T "%lld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) +# define CURL_SIZEOF_LONG 8 +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_FORMAT_OFF_T "%ld" +# define CURL_SIZEOF_CURL_OFF_T 8 +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_SIZEOF_CURL_SOCKLEN_T 4 +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +# error "Unknown non-configure build target!" + Error Compilation_aborted_Unknown_non_configure_build_target +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlbuild.h.cmake b/ext/curl/include/curl/curlbuild.h.cmake new file mode 100644 index 0000000000..60bc7a70ec --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h.cmake @@ -0,0 +1,197 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.in or + * at file include/curl/curlbuild.h, this is due to the following reason: + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +#error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +#error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +#error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +#error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +#error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +#error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +#error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +#error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +#error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +#error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */ +/* ================================================================ */ + +/* Configure process defines this to 1 when it finds out that system */ +/* header file ws2tcpip.h must be included by the external interface. */ +#cmakedefine CURL_PULL_WS2TCPIP_H +#ifdef CURL_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/types.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_TYPES_H +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file stdint.h must be included by the external interface. */ +#cmakedefine CURL_PULL_STDINT_H +#ifdef CURL_PULL_STDINT_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file inttypes.h must be included by the external interface. */ +#cmakedefine CURL_PULL_INTTYPES_H +#ifdef CURL_PULL_INTTYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/socket.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_SOCKET_H +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/poll.h must be included by the external interface. */ +#cmakedefine CURL_PULL_SYS_POLL_H +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* The size of `long', as computed by sizeof. */ +#define CURL_SIZEOF_LONG ${CURL_SIZEOF_LONG} + +/* Integral data type used for curl_socklen_t. */ +#define CURL_TYPEOF_CURL_SOCKLEN_T ${CURL_TYPEOF_CURL_SOCKLEN_T} + +/* The size of `curl_socklen_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_SOCKLEN_T ${CURL_SIZEOF_CURL_SOCKLEN_T} + +/* Data type definition of curl_socklen_t. */ +typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; + +/* Signed integral data type used for curl_off_t. */ +#define CURL_TYPEOF_CURL_OFF_T ${CURL_TYPEOF_CURL_OFF_T} + +/* Data type definition of curl_off_t. */ +typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; + +/* curl_off_t formatting string directive without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_T "${CURL_FORMAT_CURL_OFF_T}" + +/* unsigned curl_off_t formatting string without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_TU "${CURL_FORMAT_CURL_OFF_TU}" + +/* curl_off_t formatting string directive with "%" conversion specifier. */ +#define CURL_FORMAT_OFF_T "${CURL_FORMAT_OFF_T}" + +/* The size of `curl_off_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_OFF_T ${CURL_SIZEOF_CURL_OFF_T} + +/* curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_T ${CURL_SUFFIX_CURL_OFF_T} + +/* unsigned curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_TU ${CURL_SUFFIX_CURL_OFF_TU} + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlbuild.h.in b/ext/curl/include/curl/curlbuild.h.in new file mode 100644 index 0000000000..e29f195d25 --- /dev/null +++ b/ext/curl/include/curl/curlbuild.h.in @@ -0,0 +1,197 @@ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.in or + * at file include/curl/curlbuild.h, this is due to the following reason: + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +#error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +#error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +#error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +#error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +#error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +#error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +#error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +#error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +#error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +#error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */ +/* ================================================================ */ + +/* Configure process defines this to 1 when it finds out that system */ +/* header file ws2tcpip.h must be included by the external interface. */ +#undef CURL_PULL_WS2TCPIP_H +#ifdef CURL_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/types.h must be included by the external interface. */ +#undef CURL_PULL_SYS_TYPES_H +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file stdint.h must be included by the external interface. */ +#undef CURL_PULL_STDINT_H +#ifdef CURL_PULL_STDINT_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file inttypes.h must be included by the external interface. */ +#undef CURL_PULL_INTTYPES_H +#ifdef CURL_PULL_INTTYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/socket.h must be included by the external interface. */ +#undef CURL_PULL_SYS_SOCKET_H +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/poll.h must be included by the external interface. */ +#undef CURL_PULL_SYS_POLL_H +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* The size of `long', as computed by sizeof. */ +#undef CURL_SIZEOF_LONG + +/* Integral data type used for curl_socklen_t. */ +#undef CURL_TYPEOF_CURL_SOCKLEN_T + +/* The size of `curl_socklen_t', as computed by sizeof. */ +#undef CURL_SIZEOF_CURL_SOCKLEN_T + +/* Data type definition of curl_socklen_t. */ +typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; + +/* Signed integral data type used for curl_off_t. */ +#undef CURL_TYPEOF_CURL_OFF_T + +/* Data type definition of curl_off_t. */ +typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; + +/* curl_off_t formatting string directive without "%" conversion specifier. */ +#undef CURL_FORMAT_CURL_OFF_T + +/* unsigned curl_off_t formatting string without "%" conversion specifier. */ +#undef CURL_FORMAT_CURL_OFF_TU + +/* curl_off_t formatting string directive with "%" conversion specifier. */ +#undef CURL_FORMAT_OFF_T + +/* The size of `curl_off_t', as computed by sizeof. */ +#undef CURL_SIZEOF_CURL_OFF_T + +/* curl_off_t constant suffix. */ +#undef CURL_SUFFIX_CURL_OFF_T + +/* unsigned curl_off_t constant suffix. */ +#undef CURL_SUFFIX_CURL_OFF_TU + +#endif /* __CURL_CURLBUILD_H */ diff --git a/ext/curl/include/curl/curlrules.h b/ext/curl/include/curl/curlrules.h new file mode 100644 index 0000000000..7c2ede35b6 --- /dev/null +++ b/ext/curl/include/curl/curlrules.h @@ -0,0 +1,262 @@ +#ifndef __CURL_CURLRULES_H +#define __CURL_CURLRULES_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* ================================================================ */ +/* COMPILE TIME SANITY CHECKS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * All checks done in this file are intentionally placed in a public + * header file which is pulled by curl/curl.h when an application is + * being built using an already built libcurl library. Additionally + * this file is also included and used when building the library. + * + * If compilation fails on this file it is certainly sure that the + * problem is elsewhere. It could be a problem in the curlbuild.h + * header file, or simply that you are using different compilation + * settings than those used to build the library. + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * Do not deactivate any check, these are done to make sure that the + * library is properly built and used. + * + * You can find further help on the libcurl development mailing list: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * NOTE 2 + * ------ + * + * Some of the following compile time checks are based on the fact + * that the dimension of a constant array can not be a negative one. + * In this way if the compile time verification fails, the compilation + * will fail issuing an error. The error description wording is compiler + * dependent but it will be quite similar to one of the following: + * + * "negative subscript or subscript is too large" + * "array must have at least one element" + * "-1 is an illegal array size" + * "size of array is negative" + * + * If you are building an application which tries to use an already + * built libcurl library and you are getting this kind of errors on + * this file, it is a clear indication that there is a mismatch between + * how the library was built and how you are trying to use it for your + * application. Your already compiled or binary library provider is the + * only one who can give you the details you need to properly use it. + */ + +/* + * Verify that some macros are actually defined. + */ + +#ifndef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_LONG_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing +#endif + +#ifndef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_is_missing +#endif + +/* + * Macros private to this header file. + */ + +#define CurlchkszEQ(t, s) sizeof(t) == s ? 1 : -1 + +#define CurlchkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1 + +/* + * Verify that the size previously defined and expected for long + * is the same as the one reported by sizeof() at compile time. + */ + +typedef char + __curl_rule_01__ + [CurlchkszEQ(long, CURL_SIZEOF_LONG)]; + +/* + * Verify that the size previously defined and expected for + * curl_off_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_02__ + [CurlchkszEQ(curl_off_t, CURL_SIZEOF_CURL_OFF_T)]; + +/* + * Verify at compile time that the size of curl_off_t as reported + * by sizeof() is greater or equal than the one reported for long + * for the current compilation. + */ + +typedef char + __curl_rule_03__ + [CurlchkszGE(curl_off_t, long)]; + +/* + * Verify that the size previously defined and expected for + * curl_socklen_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_04__ + [CurlchkszEQ(curl_socklen_t, CURL_SIZEOF_CURL_SOCKLEN_T)]; + +/* + * Verify at compile time that the size of curl_socklen_t as reported + * by sizeof() is greater or equal than the one reported for int for + * the current compilation. + */ + +typedef char + __curl_rule_05__ + [CurlchkszGE(curl_socklen_t, int)]; + +/* ================================================================ */ +/* EXTERNALLY AND INTERNALLY VISIBLE DEFINITIONS */ +/* ================================================================ */ + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define __CURL_OFF_T_C_HLPR2(x) x +# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +/* + * Get rid of macros private to this header file. + */ + +#undef CurlchkszEQ +#undef CurlchkszGE + +/* + * Get rid of macros not intended to exist beyond this point. + */ + +#undef CURL_PULL_WS2TCPIP_H +#undef CURL_PULL_SYS_TYPES_H +#undef CURL_PULL_SYS_SOCKET_H +#undef CURL_PULL_SYS_POLL_H +#undef CURL_PULL_STDINT_H +#undef CURL_PULL_INTTYPES_H + +#undef CURL_TYPEOF_CURL_SOCKLEN_T +#undef CURL_TYPEOF_CURL_OFF_T + +#ifdef CURL_NO_OLDIES +#undef CURL_FORMAT_OFF_T /* not required since 7.19.0 - obsoleted in 7.20.0 */ +#endif + +#endif /* __CURL_CURLRULES_H */ diff --git a/ext/curl/include/curl/curlver.h b/ext/curl/include/curl/curlver.h new file mode 100644 index 0000000000..7976c1fcf1 --- /dev/null +++ b/ext/curl/include/curl/curlver.h @@ -0,0 +1,69 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "1996 - 2015 Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.42.1" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 42 +#define LIBCURL_VERSION_PATCH 1 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. +*/ +#define LIBCURL_VERSION_NUM 0x072a01 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in git, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date should follow this template: + * + * "Mon Feb 12 11:35:33 UTC 2007" + */ +#define LIBCURL_TIMESTAMP "Wed Apr 29 06:07:13 UTC 2015" + +#endif /* __CURL_CURLVER_H */ diff --git a/ext/curl/include/curl/easy.h b/ext/curl/include/curl/easy.h new file mode 100644 index 0000000000..c1e3e76096 --- /dev/null +++ b/ext/curl/include/curl/easy.h @@ -0,0 +1,102 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistent connections cannot + * be transferred. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/curl/include/curl/mprintf.h b/ext/curl/include/curl/mprintf.h new file mode 100644 index 0000000000..c6b0d7679a --- /dev/null +++ b/ext/curl/include/curl/mprintf.h @@ -0,0 +1,74 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include /* needed for FILE */ + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef _MPRINTF_REPLACE +# undef printf +# undef fprintf +# undef sprintf +# undef vsprintf +# undef snprintf +# undef vprintf +# undef vfprintf +# undef vsnprintf +# undef aprintf +# undef vaprintf +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define sprintf curl_msprintf +# define vsprintf curl_mvsprintf +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/ext/curl/include/curl/multi.h b/ext/curl/include/curl/multi.h new file mode 100644 index 0000000000..3c4acb0f6e --- /dev/null +++ b/ext/curl/include/curl/multi.h @@ -0,0 +1,399 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURLM; + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* Based on poll(2) structure and values. + * We don't use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; /* not supported yet */ +}; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CINIT(MAXCONNECTS, LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CINIT(MAX_HOST_CONNECTIONS, LONG, 7), + + /* maximum number of requests in a pipeline */ + CINIT(MAX_PIPELINE_LENGTH, LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10), + + /* a list of site names(+port) that are blacklisted from + pipelining */ + CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11), + + /* a list of server types that are blacklisted from + pipelining */ + CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/ext/curl/include/curl/stdcheaders.h b/ext/curl/include/curl/stdcheaders.h new file mode 100644 index 0000000000..ad82ef6335 --- /dev/null +++ b/ext/curl/include/curl/stdcheaders.h @@ -0,0 +1,33 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +size_t fread (void *, size_t, size_t, FILE *); +size_t fwrite (const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif /* __STDC_HEADERS_H */ diff --git a/ext/curl/include/curl/typecheck-gcc.h b/ext/curl/include/curl/typecheck-gcc.h new file mode 100644 index 0000000000..69d41a20d1 --- /dev/null +++ b/ext/curl/include/curl/typecheck-gcc.h @@ -0,0 +1,610 @@ +#ifndef __CURL_TYPECHECK_GCC_H +#define __CURL_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(_curl_is_sometype_option(_curl_opt)) + * if(!_curl_is_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define _curl_is_sometype_option, _curl_is_sometype and + * _curl_easy_setopt_err_sometype below + * + * NOTE: We use two nested 'if' statements here instead of the && operator, in + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * when compiling with -Wlogical-op. + * + * To add an option that uses the same type as an existing option, you'll just + * need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ +__extension__ ({ \ + __typeof__ (option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(_curl_is_long_option(_curl_opt)) \ + if(!_curl_is_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(_curl_is_off_t_option(_curl_opt)) \ + if(!_curl_is_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(_curl_is_string_option(_curl_opt)) \ + if(!_curl_is_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(_curl_is_write_cb_option(_curl_opt)) \ + if(!_curl_is_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!_curl_is_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!_curl_is_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!_curl_is_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!_curl_is_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!_curl_is_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!_curl_is_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!_curl_is_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(_curl_is_conv_cb_option(_curl_opt)) \ + if(!_curl_is_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!_curl_is_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(_curl_is_cb_data_option(_curl_opt)) \ + if(!_curl_is_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!_curl_is_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!_curl_is_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(_curl_is_postfields_option(_curl_opt)) \ + if(!_curl_is_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!_curl_is_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if(_curl_is_slist_option(_curl_opt)) \ + if(!_curl_is_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!_curl_is_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ +}) + +/* wraps curl_easy_getinfo() with typechecking */ +/* FIXME: don't allow const pointers */ +#define curl_easy_getinfo(handle, info, arg) \ +__extension__ ({ \ + __typeof__ (info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(_curl_is_string_info(_curl_info)) \ + if(!_curl_is_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(_curl_is_long_info(_curl_info)) \ + if(!_curl_is_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(_curl_is_double_info(_curl_info)) \ + if(!_curl_is_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(_curl_is_slist_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ +}) + +/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), + * for now just make sure that the functions are called with three + * arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define _CURL_WARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } + +_CURL_WARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string (char* or char[]) argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a FILE* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a void* or char* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a struct curl_httppost* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a struct curl_slist* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +_CURL_WARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to char * for this info") +_CURL_WARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +_CURL_WARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to struct curl_slist * for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you don't + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define _curl_is_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define _curl_is_off_t_option(option) \ + ((option) > CURLOPTTYPE_OFF_T) + +/* evaluates to true if option takes a char* argument */ +#define _curl_is_string_option(option) \ + ((option) == CURLOPT_URL || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define _curl_is_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define _curl_is_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define _curl_is_cb_data_option(option) \ + ((option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define _curl_is_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define _curl_is_slist_option(option) \ + ((option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_MAIL_RCPT || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define _curl_is_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) + +/* evaluates to true if info expects a pointer to long argument */ +#define _curl_is_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define _curl_is_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define _curl_is_slist_info(info) \ + (CURLINFO_SLIST < (info)) + + +/* typecheck helpers -- check whether given expression has requested type*/ + +/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true iff expr is a pointer */ +#define _curl_is_any_ptr(expr) \ + (sizeof(expr) == sizeof(void*)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define _curl_is_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define _curl_is_ptr(expr, type) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define _curl_is_arr(expr, type) \ + (_curl_is_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define _curl_is_string(expr) \ + (_curl_is_arr((expr), char) || \ + _curl_is_arr((expr), signed char) || \ + _curl_is_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define _curl_is_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define _curl_is_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define _curl_is_error_buffer(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define _curl_is_cb_data(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_ptr((expr), FILE)) +#else /* be less strict */ +#define _curl_is_cb_data(expr) \ + _curl_is_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define _curl_is_FILE(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *)) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define _curl_is_postfields(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_arr((expr), char)) + +/* FIXME: the whole callback checking is messy... + * The idea is to tolerate char vs. void and const vs. not const + * pointers in arguments at least + */ +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define _curl_callback_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func), type*)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define _curl_is_read_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fread)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_read_callback) || \ + _curl_callback_compatible((expr), _curl_read_callback1) || \ + _curl_callback_compatible((expr), _curl_read_callback2) || \ + _curl_callback_compatible((expr), _curl_read_callback3) || \ + _curl_callback_compatible((expr), _curl_read_callback4) || \ + _curl_callback_compatible((expr), _curl_read_callback5) || \ + _curl_callback_compatible((expr), _curl_read_callback6)) +typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void*); +typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE*); +typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void*); +typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define _curl_is_write_cb(expr) \ + (_curl_is_read_cb(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fwrite)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_write_callback) || \ + _curl_callback_compatible((expr), _curl_write_callback1) || \ + _curl_callback_compatible((expr), _curl_write_callback2) || \ + _curl_callback_compatible((expr), _curl_write_callback3) || \ + _curl_callback_compatible((expr), _curl_write_callback4) || \ + _curl_callback_compatible((expr), _curl_write_callback5) || \ + _curl_callback_compatible((expr), _curl_write_callback6)) +typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void*); +typedef size_t (_curl_write_callback2)(const char *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE*); +typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void*); +typedef size_t (_curl_write_callback5)(const void *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define _curl_is_ioctl_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ioctl_callback) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void*); +typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void*); +typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void*); +typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void*); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define _curl_is_sockopt_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_sockopt_callback) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback2)) +typedef int (_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define _curl_is_opensocket_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_opensocket_callback) ||\ + _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define _curl_is_progress_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_progress_callback) || \ + _curl_callback_compatible((expr), _curl_progress_callback1) || \ + _curl_callback_compatible((expr), _curl_progress_callback2)) +typedef int (_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define _curl_is_debug_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_debug_callback) || \ + _curl_callback_compatible((expr), _curl_debug_callback1) || \ + _curl_callback_compatible((expr), _curl_debug_callback2) || \ + _curl_callback_compatible((expr), _curl_debug_callback3) || \ + _curl_callback_compatible((expr), _curl_debug_callback4) || \ + _curl_callback_compatible((expr), _curl_debug_callback5) || \ + _curl_callback_compatible((expr), _curl_debug_callback6) || \ + _curl_callback_compatible((expr), _curl_debug_callback7) || \ + _curl_callback_compatible((expr), _curl_debug_callback8)) +typedef int (_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); +typedef int (_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define _curl_is_ssl_ctx_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ssl_ctx_callback) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback4)(CURL *, const void *, const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we're included before OpenSSL headers... + */ +typedef CURLcode (_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, + const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define _curl_is_conv_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_conv_callback) || \ + _curl_callback_compatible((expr), _curl_conv_callback1) || \ + _curl_callback_compatible((expr), _curl_conv_callback2) || \ + _curl_callback_compatible((expr), _curl_conv_callback3) || \ + _curl_callback_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define _curl_is_seek_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_seek_callback) || \ + _curl_callback_compatible((expr), _curl_seek_callback1) || \ + _curl_callback_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* __CURL_TYPECHECK_GCC_H */ diff --git a/ext/curl/lib/libcurl.dll b/ext/curl/lib/libcurl.dll new file mode 100644 index 0000000000..1c912ca6ad Binary files /dev/null and b/ext/curl/lib/libcurl.dll differ diff --git a/ext/curl/lib/libcurl_imp.lib b/ext/curl/lib/libcurl_imp.lib new file mode 100644 index 0000000000..917274da36 Binary files /dev/null and b/ext/curl/lib/libcurl_imp.lib differ diff --git a/ext/ghoul b/ext/ghoul index eb66778e0a..99a25fcc8c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit eb66778e0aaf1809fff1a1476872594a24ac0ac3 +Subproject commit 99a25fcc8c05d6448b32409462a25d9cd9432637 diff --git a/include/openspace/abuffer/abuffer.h b/include/openspace/abuffer/abuffer.h index f8e1550a29..6252ffc06c 100644 --- a/include/openspace/abuffer/abuffer.h +++ b/include/openspace/abuffer/abuffer.h @@ -55,7 +55,7 @@ public: ABuffer(); virtual ~ABuffer(); - virtual void resolve(); + virtual void resolve(float blackoutFactor); virtual bool initialize() = 0; virtual bool reinitialize(); @@ -74,7 +74,7 @@ public: protected: virtual bool reinitializeInternal() = 0; - bool initializeABuffer(); + virtual bool initializeABuffer(); void generateShaderSource(); bool updateShader(); @@ -86,8 +86,6 @@ protected: unsigned int _width, _height, _totalPixels; -private: - void updateDimensions(); GLuint _screenQuad; diff --git a/include/openspace/abuffer/abufferframebuffer.h b/include/openspace/abuffer/abufferframebuffer.h index c348c777f6..13b3f18f30 100644 --- a/include/openspace/abuffer/abufferframebuffer.h +++ b/include/openspace/abuffer/abufferframebuffer.h @@ -34,15 +34,19 @@ public: ABufferFramebuffer(); virtual ~ABufferFramebuffer(); - virtual bool initialize(); + bool initialize(); - virtual void clear(); - virtual void preRender(); - virtual void postRender(); + void clear(); + void preRender(); + void postRender(); + void resolve(float blackoutFactor); std::vector pixelData(); protected: - virtual bool reinitializeInternal(); + bool reinitializeInternal(); + + bool initializeABuffer(); + private: diff --git a/include/openspace/engine/configurationmanager.h b/include/openspace/engine/configurationmanager.h index 2013bb3aa6..c87c9183ab 100644 --- a/include/openspace/engine/configurationmanager.h +++ b/include/openspace/engine/configurationmanager.h @@ -49,7 +49,9 @@ public: static const std::string KeyLogLevel; static const std::string KeyLogImmediateFlush; static const std::string KeyLogs; + static const std::string KeyCapabilitiesVerbosity; static const std::string KeyDisableMasterRendering; + static const std::string KeyDownloadRequestURL; bool loadFromFile(const std::string& filename); diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h new file mode 100644 index 0000000000..1cb8a9d751 --- /dev/null +++ b/include/openspace/engine/downloadmanager.h @@ -0,0 +1,103 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __DOWNLOADMANAGER_H__ +#define __DOWNLOADMANAGER_H__ + +#include +#include +#include + +#include +#include +#include + +namespace openspace { + +// Multithreaded +class DownloadManager : public ghoul::Singleton { +public: + struct FileFuture { + // Since the FileFuture object will be used from multiple threads, we have to be + // careful about the access pattern, that is, no values should be read and written + // by both the DownloadManager and the outside threads. + FileFuture(std::string file); + + // Values that are written by the DownloadManager to be consumed by others + long long currentSize; + long long totalSize; + float progress; // [0,1] + float secondsRemaining; + bool isFinished; + bool isAborted; + std::string filePath; + std::string errorMessage; + + // Values set by others to be consumed by the DownloadManager + bool abortDownload; + }; + + typedef std::function DownloadProgressCallback; + typedef std::function DownloadFinishedCallback; + typedef std::function&)> AsyncDownloadFinishedCallback; + + DownloadManager(std::string requestURL, int applicationVersion); + + // callers responsibility to delete + // callbacks happen on a different thread + FileFuture* downloadFile( + const std::string& url, + const ghoul::filesystem::File& file, + bool overrideFile = true, + DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + DownloadProgressCallback progressCallback = DownloadProgressCallback() + ); + + std::vector downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles = true, + DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), + DownloadProgressCallback progressCallback = DownloadProgressCallback() + ); + + void downloadRequestFilesAsync( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles, + AsyncDownloadFinishedCallback callback + ); + +private: + std::string _requestURL; + int _applicationVersion; +}; + +#define DlManager (openspace::DownloadManager::ref()) + +} // namespace openspace + +#endif // __DOWNLOADMANAGER_H__ diff --git a/include/openspace/engine/moduleengine.h b/include/openspace/engine/moduleengine.h index 229af21f84..2ad6954846 100644 --- a/include/openspace/engine/moduleengine.h +++ b/include/openspace/engine/moduleengine.h @@ -33,6 +33,9 @@ class OpenSpaceModule; class ModuleEngine { public: + bool create(); + bool destroy(); + bool initialize(); bool deinitialize(); diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 4056dc2e56..cde17ace1b 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -46,6 +46,14 @@ class ScreenLog; class RenderEngine { public: + enum class ABufferImplementation { + FrameBuffer = 0, + SingleLinked, + Fixed, + Dynamic, + Invalid + }; + static const std::string PerformanceMeasurementSharedData; RenderEngine(); @@ -57,7 +65,8 @@ public: Scene* scene(); Camera* camera() const; - ABuffer* abuffer() const; + ABuffer* aBuffer() const; + ABufferImplementation aBufferImplementation() const; // sgct wrapped functions bool initializeGL(); @@ -108,12 +117,14 @@ public: } _onScreenInformation; private: + ABufferImplementation aBufferFromString(const std::string& impl); + void storePerformanceMeasurements(); Camera* _mainCamera; Scene* _sceneGraph; ABuffer* _abuffer; - int _abufferImplementation; + ABufferImplementation _abufferImplementation; ScreenLog* _log; bool _showInfo; diff --git a/include/openspace/util/openspacemodule.h b/include/openspace/util/openspacemodule.h index 7f2d17c7d7..3a4ade1eaa 100644 --- a/include/openspace/util/openspacemodule.h +++ b/include/openspace/util/openspacemodule.h @@ -31,19 +31,21 @@ namespace openspace { class OpenSpaceModule { public: - OpenSpaceModule() = default; + OpenSpaceModule(std::string name); virtual ~OpenSpaceModule() = default; + virtual bool create(); + virtual bool destroy(); + virtual bool initialize(); virtual bool deinitialize(); std::string name() const; protected: - void setName(std::string name); std::string modulePath() const; - std::string _name; + const std::string _name; }; } // namespace openspace diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index a0d2a1001c..4ffe0383c3 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -31,6 +31,7 @@ #include #include +#include #include @@ -42,646 +43,630 @@ namespace openspace { -class SpiceManager { +class SpiceManager : public ghoul::Singleton { + friend class ghoul::Singleton; + public: - typedef std::array TransformMatrix; - typedef unsigned int KernelIdentifier; - - static const KernelIdentifier KernelFailed = KernelIdentifier(-1); + typedef std::array TransformMatrix; + typedef unsigned int KernelIdentifier; + + static const KernelIdentifier KernelFailed = KernelIdentifier(-1); - /** - * Initializer that initializes the static member. - */ - static void initialize(); + /** + * Loads one or more SPICE kernels into a program. The provided path can either be a + * binary, text-kernel, or meta-kernel which gets loaded into the kernel pool. The + * loading is done by passing the filePath to the furnsh_c + * function. http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/furnsh_c.html + * \param filePath The path to the kernel that should be loaded + * \return The loaded kernel's unique identifier that can be used to unload the kernel + */ + KernelIdentifier loadKernel(const std::string& filePath); - /** - * Deinitializes the SpiceManager and unloads all kernels which have been loaded using - * this manager. - */ - static void deinitialize(); + /** + * Function to find and store the intervals covered by a ck file, this is done + * by using mainly the ckcov_c and ckobj_c functions. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckobj_c.html , + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckcov_c.html + * \param filePath The path to the kernel that should be examined + * \return true if the operation was successful + */ + bool findCkCoverage(const std::string& path); - /** - * Returns the reference to the singleton SpiceManager object that must have been - * initialized by a call to the initialize method earlier. - * \return The SpiceManager singleton - */ - static SpiceManager& ref(); - - /** - * Loads one or more SPICE kernels into a program. The provided path can either be a - * binary, text-kernel, or meta-kernel which gets loaded into the kernel pool. The - * loading is done by passing the filePath to the furnsh_c - * function. http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/furnsh_c.html - * \param filePath The path to the kernel that should be loaded - * \return The loaded kernel's unique identifier that can be used to unload the kernel - */ - KernelIdentifier loadKernel(const std::string& filePath); + /** + * Function to find and store the intervals covered by a spk file, this is done + * by using mainly the spkcov_c and spkobj_c functions. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkobj_c.html , + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcov_c.html + * \param filePath The path to the kernel that should be examined + * \return true if the operation was successful + */ + bool findSpkCoverage(const std::string& path); + + /** + * \return true if SPK kernels have been loaded to cover target + * for time et + * \param target, the body to be examined + * \param et, the time when body is possibly covered + */ - /** - * Function to find and store the intervals covered by a ck file, this is done - * by using mainly the ckcov_c and ckobj_c functions. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckobj_c.html , - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ckcov_c.html - * \param filePath The path to the kernel that should be examined - * \return true if the operation was successful - */ - bool findCkCoverage(const std::string& path); + bool hasSpkCoverage(std::string target, double& et) const; - /** - * Function to find and store the intervals covered by a spk file, this is done - * by using mainly the spkcov_c and spkobj_c functions. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkobj_c.html , - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcov_c.html - * \param filePath The path to the kernel that should be examined - * \return true if the operation was successful - */ - bool findSpkCoverage(const std::string& path); - - /** - * \return true if SPK kernels have been loaded to cover target - * for time et - * \param target, the body to be examined - * \param et, the time when body is possibly covered - */ + /** + * \return true if CK kernels have been loaded to cover frame + * for time et + * \param frame, the frame to be examined + * \param et, the time when frame is possibly covered + */ + bool hasCkCoverage(std::string frame, double& et) const; + + /** + * Unloads a SPICE kernel identified by the kernelId which was returned + * by the loading call to loadKernel. The unloading is done by calling the + * unload_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. + * \param kernelId The unique identifier that was returned from the call to + * loadKernel which loaded the kernel + */ + void unloadKernel(KernelIdentifier kernelId); - bool hasSpkCoverage(std::string target, double& et) const; - - /** - * \return true if CK kernels have been loaded to cover frame - * for time et - * \param frame, the frame to be examined - * \param et, the time when frame is possibly covered - */ - bool hasCkCoverage(std::string frame, double& et) const; - - /** - * Unloads a SPICE kernel identified by the kernelId which was returned - * by the loading call to loadKernel. The unloading is done by calling the - * unload_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. - * \param kernelId The unique identifier that was returned from the call to - * loadKernel which loaded the kernel - */ - void unloadKernel(KernelIdentifier kernelId); - - /** - * Unloads a SPICE kernel identified by the filePath which was used in - * the loading call to loadKernel. The unloading is done by calling the - * unload_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. - * \param filePath The path of the kernel that should be unloaded, it has to refer to - * a file that was loaded in using the loadKernel method - */ - void unloadKernel(const std::string& filePath); - - /** - * Determines whether values exist for some item for any body, - * identified by it's naifId, in the kernel pool by passing it to the - * bodfnd_c function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html - * For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param naifId NAIF ID code of body + /** + * Unloads a SPICE kernel identified by the filePath which was used in + * the loading call to loadKernel. The unloading is done by calling the + * unload_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html. + * \param filePath The path of the kernel that should be unloaded, it has to refer to + * a file that was loaded in using the loadKernel method + */ + void unloadKernel(const std::string& filePath); + + /** + * Determines whether values exist for some item for any body, + * identified by it's naifId, in the kernel pool by passing it to the + * bodfnd_c function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html + * For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param naifId NAIF ID code of body * \param item The item to find - * \return true if the function succeeded, false otherwise - */ - bool hasValue(int naifId, const std::string& item) const; + * \return true if the function succeeded, false otherwise + */ + bool hasValue(int naifId, const std::string& item) const; - /** - * Determines whether values exist for some item for any - * body in the kernel pool by passing it to the bodfnd_c - * function. - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html - * \param body The name of the body that should be sampled + /** + * Determines whether values exist for some item for any + * body in the kernel pool by passing it to the bodfnd_c + * function. + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodfnd_c.html + * \param body The name of the body that should be sampled * \param item The item to find - * \return true if the function succeeded, false otherwise - */ - bool hasValue(const std::string& body, const std::string& item) const; + * \return true if the function succeeded, false otherwise + */ + bool hasValue(const std::string& body, const std::string& item) const; - /** - * Returns the NAIF ID for a specific body. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. The - * id will only be set if the retrieval was successful, otherwise it will - * remain unchanged. - * \param body The body name that should be retrieved - * \param id The ID of the body will be stored in this variable. The - * value will only be changed if the retrieval was successful - * \return true if the body was found, false - * otherwise - */ - bool getNaifId(const std::string& body, int& id) const; + /** + * Returns the NAIF ID for a specific body. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. The + * id will only be set if the retrieval was successful, otherwise it will + * remain unchanged. + * \param body The body name that should be retrieved + * \param id The ID of the body will be stored in this variable. The + * value will only be changed if the retrieval was successful + * \return true if the body was found, false + * otherwise + */ + bool getNaifId(const std::string& body, int& id) const; - /** - * Returns the NAIF ID for a specific frame using namfrm_c(), see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/namfrm_c.html. - * The id will only be set if the retrieval was successful, - * otherwise it will remain unchanged. - * \param frame The frame name that should be retrieved - * \param id The ID of the frame will be stored in this variable. The - * value will only be changed if the retrieval was successful - * \return true if the frame was found, false - * otherwise - */ - bool getFrameId(const std::string& frame, int& id) const; + /** + * Returns the NAIF ID for a specific frame using namfrm_c(), see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/namfrm_c.html. + * The id will only be set if the retrieval was successful, + * otherwise it will remain unchanged. + * \param frame The frame name that should be retrieved + * \param id The ID of the frame will be stored in this variable. The + * value will only be changed if the retrieval was successful + * \return true if the frame was found, false + * otherwise + */ + bool getFrameId(const std::string& frame, int& id) const; - /** - * Retrieves a single value for a certain body. This method - * succeeds iff body is the name of a valid body, value - * is a value associated with the body, and the value consists of only a single - * double value. If all conditions are true, the value is retrieved using - * the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * this body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved value - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, double& v) const; + /** + * Retrieves a single value for a certain body. This method + * succeeds iff body is the name of a valid body, value + * is a value associated with the body, and the value consists of only a single + * double value. If all conditions are true, the value is retrieved using + * the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * this body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved value + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, double& v) const; - /** - * Retrieves a value with three components for a certain - * body. This method succeeds iff body is the name of a - * valid body, value is a value associated with the body, and the value - * consists of three double values. If all conditions are true, the value - * is retrieved using the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * the body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, glm::dvec3& v) const; + /** + * Retrieves a value with three components for a certain + * body. This method succeeds iff body is the name of a + * valid body, value is a value associated with the body, and the value + * consists of three double values. If all conditions are true, the value + * is retrieved using the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * the body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, glm::dvec3& v) const; - /** - * Retrieves a value with four components for a certain - * body. This method succeeds iff body is the name of a - * valid body, value is a value associated with the body, and the value - * consists of four double values. If all conditions are true, the value - * is retrieved using the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The name of the body whose value should be retrieved or the NAIF ID of - * the body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, glm::dvec4& v) const; + /** + * Retrieves a value with four components for a certain + * body. This method succeeds iff body is the name of a + * valid body, value is a value associated with the body, and the value + * consists of four double values. If all conditions are true, the value + * is retrieved using the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The name of the body whose value should be retrieved or the NAIF ID of + * the body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, glm::dvec4& v) const; - /** - * Retrieves a value with an arbitrary number of components for a certain - * body. This method succeeds body is a valid body, - * value is a value associated with the body, and the value consists of a - * number of double values. The requested number is equal to the - * size of the passed vector v which means that this vector - * has to be preallocated. If all conditions are true, the value is retrieved using - * the method bodvrd_c and stored in v - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of - * the conditions is false an error is logged and the value v is - * unchanged. For a description on NAIF IDs, see - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. - * \param body The body whose information should be retrieved or the NAIF ID of that - * body - * \param value The value of that should be retrieved, this value is case-sensitive - * \param v The destination for the retrieved values. The size of this vector - * determines how many values will be retrieved - * \return true if the body named a valid body, - * value is a valid item for the body and the retrieved - * value is only a single value. false otherwise - */ - bool getValue(const std::string& body, const std::string& value, - std::vector& v) const; + /** + * Retrieves a value with an arbitrary number of components for a certain + * body. This method succeeds body is a valid body, + * value is a value associated with the body, and the value consists of a + * number of double values. The requested number is equal to the + * size of the passed vector v which means that this vector + * has to be preallocated. If all conditions are true, the value is retrieved using + * the method bodvrd_c and stored in v + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/bodvrd_c.html. If one of + * the conditions is false an error is logged and the value v is + * unchanged. For a description on NAIF IDs, see + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html. + * \param body The body whose information should be retrieved or the NAIF ID of that + * body + * \param value The value of that should be retrieved, this value is case-sensitive + * \param v The destination for the retrieved values. The size of this vector + * determines how many values will be retrieved + * \return true if the body named a valid body, + * value is a valid item for the body and the retrieved + * value is only a single value. false otherwise + */ + bool getValue(const std::string& body, const std::string& value, + std::vector& v) const; - bool spacecraftClockToET(const std::string& craftIdCode, double& craftTicks, double& et); + bool spacecraftClockToET(const std::string& craftIdCode, double& craftTicks, double& et); - /** - * Converts the timeString representing a date to a double precision + /** + * Converts the timeString representing a date to a double precision * value representing the ephemerisTime; that is the number of TDB - * seconds past the J2000 epoch. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html. If an error - * occurs, an error is logged, the method returns false and the - * ephemerisTime remains unchanged. - * \param timeString A string representing the time to be converted - * \param ephemerisTime The destination for the converted time; the number of TDB - * seconds past the J2000 epoch, representing the passed epochString - * \return true if the epochString is a valid string and - * the conversion succeeded, false otherwise - */ - bool getETfromDate(const std::string& timeString, double& ephemerisTime) const; + * seconds past the J2000 epoch. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html. If an error + * occurs, an error is logged, the method returns false and the + * ephemerisTime remains unchanged. + * \param timeString A string representing the time to be converted + * \param ephemerisTime The destination for the converted time; the number of TDB + * seconds past the J2000 epoch, representing the passed epochString + * \return true if the epochString is a valid string and + * the conversion succeeded, false otherwise + */ + bool getETfromDate(const std::string& timeString, double& ephemerisTime) const; - /** - * Converts the passed ephemerisTime into a human-readable - * date string with a specific format. For details on the - * formatting, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html. In case of - * an error, date will not be modified, an error will be logged and the - * method returns false. - * \param ephemerisTime The ephemeris time, that is the number of TDB seconds past the - * J2000 epoch - * \param date The destination for the converted date. This will only be changed if - * the conversion succeeded - * \param format The format string describing the output format for the - * date - * \return true if the conversion succeeded, false otherwise - */ - bool getDateFromET(double ephemerisTime, std::string& date, - const std::string& format = "YYYY MON DDTHR:MN:SC.### ::RND") const; + /** + * Converts the passed ephemerisTime into a human-readable + * date string with a specific format. For details on the + * formatting, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html. In case of + * an error, date will not be modified, an error will be logged and the + * method returns false. + * \param ephemerisTime The ephemeris time, that is the number of TDB seconds past the + * J2000 epoch + * \param date The destination for the converted date. This will only be changed if + * the conversion succeeded + * \param format The format string describing the output format for the + * date + * \return true if the conversion succeeded, false otherwise + */ + bool getDateFromET(double ephemerisTime, std::string& date, + const std::string& format = "YYYY MON DDTHR:MN:SC.### ::RND") const; - /** - * Returns the position of a target body relative to an - * observer in a specific referenceFrame, optionally - * corrected for light time (planetary aberration) and stellar aberration - * (aberrationCorrection). For further details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html. For more - * information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param target The target body name or the target body's NAIF ID - * \param observer The observing body name or the observing body's NAIF ID - * \param referenceFrame The reference frame of the output position vector - * \param aberrationCorrection The aberration correction flag out of the list of - * values (NONE, LT, LT+S, CN, - * CN+S for the reception case or XLT, XLT+S, - * XCN, or XCN+S for the transmission case. - * \param ephemerisTime The time at which the position is to be queried - * \param position The output containing the position of the target; if the method - * fails, the target position is unchanged - * \param lightTime If the aberrationCorrection is different from - * NONE, this variable will contain the one-way light time between the - * observer and the target. If the method fails, the lightTime is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getTargetPosition(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - glm::dvec3& position, - double& lightTime) const; + /** + * Returns the position of a target body relative to an + * observer in a specific referenceFrame, optionally + * corrected for light time (planetary aberration) and stellar aberration + * (aberrationCorrection). For further details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html. For more + * information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param target The target body name or the target body's NAIF ID + * \param observer The observing body name or the observing body's NAIF ID + * \param referenceFrame The reference frame of the output position vector + * \param aberrationCorrection The aberration correction flag out of the list of + * values (NONE, LT, LT+S, CN, + * CN+S for the reception case or XLT, XLT+S, + * XCN, or XCN+S for the transmission case. + * \param ephemerisTime The time at which the position is to be queried + * \param position The output containing the position of the target; if the method + * fails, the target position is unchanged + * \param lightTime If the aberrationCorrection is different from + * NONE, this variable will contain the one-way light time between the + * observer and the target. If the method fails, the lightTime is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getTargetPosition(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + glm::dvec3& position, + double& lightTime) const; - bool getTargetPosition(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - psc& position, - double& lightTime) const; + bool getTargetPosition(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + psc& position, + double& lightTime) const; - /** - * If a position is requested for an uncovered time in the SPK kernels, - * this function will insert a position in modelPosition. - * If the coverage has not yet started, the first position will be retrieved, - * If the coverage has ended, the last position will be retrieved - * If time is in a coverage gap, the position will be interpolated - * \param time, for which an estimated position is desirable - * \param target, the body which is missing SPK data for this time - * \param origin, the observer, the position will be retrieved in relation to this body - * \param modelPosition, the position of the body, passed by reference - * \return true if an estimated position is found - */ - bool getEstimatedPosition(const double time, const std::string target, const std::string origin, psc& modelPosition) const; + /** + * If a position is requested for an uncovered time in the SPK kernels, + * this function will insert a position in modelPosition. + * If the coverage has not yet started, the first position will be retrieved, + * If the coverage has ended, the last position will be retrieved + * If time is in a coverage gap, the position will be interpolated + * \param time, for which an estimated position is desirable + * \param target, the body which is missing SPK data for this time + * \param origin, the observer, the position will be retrieved in relation to this body + * \param modelPosition, the position of the body, passed by reference + * \return true if an estimated position is found + */ + bool getEstimatedPosition(const double time, const std::string target, const std::string origin, psc& modelPosition) const; - /** - * This helper method converts a 3 dimensional vector from one reference frame to another. - * \param v The vector to be converted - * \param from The frame to be converted from - * \param to The frame to be converted to - * \param ephemerisTime Time at which to get rotational matrix that transforms vector - * \return true if the conversion succeeded, false otherwise - */ - bool frameConversion(glm::dvec3& v, const std::string& from, const std::string& to, double ephemerisTime) const; + /** + * This helper method converts a 3 dimensional vector from one reference frame to another. + * \param v The vector to be converted + * \param from The frame to be converted from + * \param to The frame to be converted to + * \param ephemerisTime Time at which to get rotational matrix that transforms vector + * \return true if the conversion succeeded, false otherwise + */ + bool frameConversion(glm::dvec3& v, const std::string& from, const std::string& to, double ephemerisTime) const; - /** - * Finds the projection of one vector onto another vector. - * All vectors are 3-dimensional. - * \param v1 The vector to be projected. - * \param v2 The vector onto which v1 is to be projected. - * \return The projection of v1 onto v2. - */ - glm::dvec3 orthogonalProjection(glm::dvec3& v1, glm::dvec3& v2); + /** + * Finds the projection of one vector onto another vector. + * All vectors are 3-dimensional. + * \param v1 The vector to be projected. + * \param v2 The vector onto which v1 is to be projected. + * \return The projection of v1 onto v2. + */ + glm::dvec3 orthogonalProjection(glm::dvec3& v1, glm::dvec3& v2); - /** - * Given an observer and a direction vector defining a ray, compute - * the surface intercept of the ray on a target body at a specified - * epoch, optionally corrected for light time and stellar - * aberration. - * \param target Name of target body. - * \param observer Name of observing body. - * \param fovFrame Reference frame of ray's direction vector. - * \param bodyFixedFrame Body-fixed, body-centered target body frame. - * \param method Computation method. - * \param aberrationCorrection Aberration correction. - * \param ephemerisTime Epoch in ephemeris seconds past J2000 TDB. - * \param targetEpoch Intercept epoch. - * \param directionVector Ray's direction vector. - * \param surfaceIntercept Surface intercept point on the target body. - * \param surfaceVector Vector from observer to intercept point. - * \param isVisible Flag indicating whether intercept was found. - * \return true if not error occurred, false otherwise - * For further details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sincpt_c.html - */ - bool getSurfaceIntercept(const std::string& target, - const std::string& observer, - const std::string& fovFrame, - const std::string& bodyFixedFrame, - const std::string& method, - const std::string& aberrationCorrection, - double ephemerisTime, - double& targetEpoch, - glm::dvec3& directionVector, - glm::dvec3& surfaceIntercept, - glm::dvec3& surfaceVector, + /** + * Given an observer and a direction vector defining a ray, compute + * the surface intercept of the ray on a target body at a specified + * epoch, optionally corrected for light time and stellar + * aberration. + * \param target Name of target body. + * \param observer Name of observing body. + * \param fovFrame Reference frame of ray's direction vector. + * \param bodyFixedFrame Body-fixed, body-centered target body frame. + * \param method Computation method. + * \param aberrationCorrection Aberration correction. + * \param ephemerisTime Epoch in ephemeris seconds past J2000 TDB. + * \param targetEpoch Intercept epoch. + * \param directionVector Ray's direction vector. + * \param surfaceIntercept Surface intercept point on the target body. + * \param surfaceVector Vector from observer to intercept point. + * \param isVisible Flag indicating whether intercept was found. + * \return true if not error occurred, false otherwise + * For further details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sincpt_c.html + */ + bool getSurfaceIntercept(const std::string& target, + const std::string& observer, + const std::string& fovFrame, + const std::string& bodyFixedFrame, + const std::string& method, + const std::string& aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& directionVector, + glm::dvec3& surfaceIntercept, + glm::dvec3& surfaceVector, bool& isVisible ) const; - /** - * Determine if a specified ephemeris object is within the - * field-of-view (FOV) of a specified instrument at a given time. - * \param instrument Name or ID code string of the instrument. - * \param target Name or ID code string of the target. - * \param observer Name or ID code string of the observer. - * \param aberrationCorrection Aberration correction method. - * \param method Type of shape model used for the target. - * \param referenceFrame Body-fixed, body-centered frame for target body. - * \param targetEpoch Time of the observation (seconds past J2000). - * \param isVisible true if the target is visible - * \return The success of the function - * For further detail, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/fovtrg_c.html - */ - bool targetWithinFieldOfView(const std::string& instrument, - const std::string& target, - const std::string& observer, - const std::string& method, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double& targetEpoch, + /** + * Determine if a specified ephemeris object is within the + * field-of-view (FOV) of a specified instrument at a given time. + * \param instrument Name or ID code string of the instrument. + * \param target Name or ID code string of the target. + * \param observer Name or ID code string of the observer. + * \param aberrationCorrection Aberration correction method. + * \param method Type of shape model used for the target. + * \param referenceFrame Body-fixed, body-centered frame for target body. + * \param targetEpoch Time of the observation (seconds past J2000). + * \param isVisible true if the target is visible + * \return The success of the function + * For further detail, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/fovtrg_c.html + */ + bool targetWithinFieldOfView(const std::string& instrument, + const std::string& target, + const std::string& observer, + const std::string& method, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double& targetEpoch, bool& isVisible ) const; - /** - * This method performs the same computation as the function its overloading - * with the exception that in doing so it assumes the inertial bodyfixed frame - * is that of 'IAU' type, allowing the client to omitt the - * referenceFrame for planetary objects. - */ - bool targetWithinFieldOfView(const std::string& instrument, - const std::string& target, - const std::string& observer, - const std::string& method, - const std::string& aberrationCorrection, - double& targetEpoch, + /** + * This method performs the same computation as the function its overloading + * with the exception that in doing so it assumes the inertial bodyfixed frame + * is that of 'IAU' type, allowing the client to omitt the + * referenceFrame for planetary objects. + */ + bool targetWithinFieldOfView(const std::string& instrument, + const std::string& target, + const std::string& observer, + const std::string& method, + const std::string& aberrationCorrection, + double& targetEpoch, bool& isVisible ) const; - /** - * Returns the state vector (position and velocity) of a - * target body relative to an observer in a specific - * referenceFrame, optionally corrected for light time (planetary - * aberration) and stellar aberration (aberrationCorrection). For further - * details, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezr_c.html. For more - * information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param target The target body name or the target body's NAIF ID - * \param observer The observing body name or the observing body's NAIF ID - * \param referenceFrame The reference frame of the output position vector - * \param aberrationCorrection The aberration correction flag out of the list of - * values (NONE, LT, LT+S, CN, - * CN+S for the reception case or XLT, XLT+S, - * XCN, or XCN+S for the transmission case. - * \param ephemerisTime The time at which the position is to be queried - * \param position The output containing the position of the target; if the method - * fails, the position is unchanged - * \param velocity The output containing the velocity of the target; if the method - * fails, the velocity is unchanged - * \param lightTime If the aberrationCorrection is different from - * NONE, this variable will contain the one-way light time between the - * observer and the target.If the method fails, the lightTime is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getTargetState(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - glm::dvec3& position, - glm::dvec3& velocity, - double& lightTime) const; + /** + * Returns the state vector (position and velocity) of a + * target body relative to an observer in a specific + * referenceFrame, optionally corrected for light time (planetary + * aberration) and stellar aberration (aberrationCorrection). For further + * details, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezr_c.html. For more + * information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param target The target body name or the target body's NAIF ID + * \param observer The observing body name or the observing body's NAIF ID + * \param referenceFrame The reference frame of the output position vector + * \param aberrationCorrection The aberration correction flag out of the list of + * values (NONE, LT, LT+S, CN, + * CN+S for the reception case or XLT, XLT+S, + * XCN, or XCN+S for the transmission case. + * \param ephemerisTime The time at which the position is to be queried + * \param position The output containing the position of the target; if the method + * fails, the position is unchanged + * \param velocity The output containing the velocity of the target; if the method + * fails, the velocity is unchanged + * \param lightTime If the aberrationCorrection is different from + * NONE, this variable will contain the one-way light time between the + * observer and the target.If the method fails, the lightTime is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getTargetState(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + glm::dvec3& position, + glm::dvec3& velocity, + double& lightTime) const; - bool getTargetState(const std::string& target, - const std::string& observer, - const std::string& referenceFrame, - const std::string& aberrationCorrection, - double ephemerisTime, - PowerScaledCoordinate& position, - PowerScaledCoordinate& velocity, - double& lightTime) const; + bool getTargetState(const std::string& target, + const std::string& observer, + const std::string& referenceFrame, + const std::string& aberrationCorrection, + double ephemerisTime, + PowerScaledCoordinate& position, + PowerScaledCoordinate& velocity, + double& lightTime) const; - /** - * Returns the state transformation matrix used to convert from one frame to another - * at a specified ephemerisTime. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sxform_c.html. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTime The time at which the transformation matrix is to be queried - * \param transformationMatrix The output containing the TransformMatrix containing - * the transformation matrix that defines the transformation from the - * sourceFrame to the destinationFrame. If the method fails - * the transformationMatrix is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getStateTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTime, - TransformMatrix& transformationMatrix) const; + /** + * Returns the state transformation matrix used to convert from one frame to another + * at a specified ephemerisTime. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sxform_c.html. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTime The time at which the transformation matrix is to be queried + * \param transformationMatrix The output containing the TransformMatrix containing + * the transformation matrix that defines the transformation from the + * sourceFrame to the destinationFrame. If the method fails + * the transformationMatrix is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getStateTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTime, + TransformMatrix& transformationMatrix) const; - /** - * Returns the matrix that transforms position vectors from one reference frame to - * another at a specified ephemerisTime. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTime The time at which the transformation matrix is to be queried + /** + * Returns the matrix that transforms position vectors from one reference frame to + * another at a specified ephemerisTime. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/pxform_c.html. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTime The time at which the transformation matrix is to be queried * \param transformationMatrix The output containing the transformation matrix that - * defines the transformation from the sourceFrame to the - * destinationFrame. If the method fails the - * transformationMatrix is unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getPositionTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTime, - glm::dmat3& transformationMatrix) const; + * defines the transformation from the sourceFrame to the + * destinationFrame. If the method fails the + * transformationMatrix is unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getPositionTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTime, + glm::dmat3& transformationMatrix) const; - /** - * The following overloaded function performs similar to its default - the exception being - * that it computes transformationMatrix with respect to local time offset - * between an observer and its target. This allows for the accountance of light travel of - * photons, e.g to account for instrument pointing offsets due to said phenomenon. - * \param sourceFrame The name of the source reference frame - * \param destinationFrame The name of the destination reference frame - * \param ephemerisTimeFrom Recorded/observed observation time - * \param ephemerisTimeTo Emission local target-time - * \param transformationMatrix The output containing the transformation matrix that - */ + /** + * The following overloaded function performs similar to its default - the exception being + * that it computes transformationMatrix with respect to local time offset + * between an observer and its target. This allows for the accountance of light travel of + * photons, e.g to account for instrument pointing offsets due to said phenomenon. + * \param sourceFrame The name of the source reference frame + * \param destinationFrame The name of the destination reference frame + * \param ephemerisTimeFrom Recorded/observed observation time + * \param ephemerisTimeTo Emission local target-time + * \param transformationMatrix The output containing the transformation matrix that + */ - bool getPositionTransformMatrix(const std::string& sourceFrame, - const std::string& destinationFrame, - double ephemerisTimeFrom, - double ephemerisTimeTo, - glm::dmat3& transformationMatrix) const; + bool getPositionTransformMatrix(const std::string& sourceFrame, + const std::string& destinationFrame, + double ephemerisTimeFrom, + double ephemerisTimeTo, + glm::dmat3& transformationMatrix) const; - /** - * If a transform matrix is requested for an uncovered time in the CK kernels, - * this function will insert a transform matrix in positionMatrix. - * If the coverage has not yet started, the first transform matrix will be retrieved, - * If the coverage has ended, the last transform matrix will be retrieved - * If time is in a coverage gap, the transform matrix will be interpolated - * \param time, for which an estimated transform matrix is desirable - * \param fromFrame, the transform matrix will be retrieved in relation to this frame - * \param toFrame, the frame missing CK data for this time - * \param positionMatrix, the estimated transform matrix of the frame, passed by reference - * \return true if an estimated transform matrix is found - */ - bool getEstimatedTransformMatrix(const double time, const std::string fromFrame, const std::string toFrame, glm::dmat3& positionMatrix) const; + /** + * If a transform matrix is requested for an uncovered time in the CK kernels, + * this function will insert a transform matrix in positionMatrix. + * If the coverage has not yet started, the first transform matrix will be retrieved, + * If the coverage has ended, the last transform matrix will be retrieved + * If time is in a coverage gap, the transform matrix will be interpolated + * \param time, for which an estimated transform matrix is desirable + * \param fromFrame, the transform matrix will be retrieved in relation to this frame + * \param toFrame, the frame missing CK data for this time + * \param positionMatrix, the estimated transform matrix of the frame, passed by reference + * \return true if an estimated transform matrix is found + */ + bool getEstimatedTransformMatrix(const double time, const std::string fromFrame, const std::string toFrame, glm::dmat3& positionMatrix) const; - /** - * Applies the transformationMatrix retrieved from - * getStateTransformMatrix to the position and velocity. The - * position and velocity parameters are used as input and - * output. - * \param position The position that should be transformed. The transformed position - * will be stored back in this parameter - * \param velocity The velocity that should be transformed. The transformed velocity - * will be stored back in this parameter - * \param transformationMatrix The 6x6 transformation matrix retrieved from - * getStateTransformMatrix that is used to transform the position and - * velocity vectors - */ - void applyTransformationMatrix(glm::dvec3& position, - glm::dvec3& velocity, - const TransformMatrix& transformationMatrix); - - /** - * This routine returns the field-of-view (FOV) parameters for a specified - * instrument. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * \param instrument The name of the instrument for which the FOV is to be retrieved - * \param fovShape The output containing the rough shape of the returned FOV. If the - * method fails, this value remains unchanged - * \param frameName The output containing the name of the frame in which the FOV - * bounds are computed. If the method fails, this value remains unchanged - * \param boresightVector The output containing the boresight, that is the vector for - * the center direction of the FOV. If the method fails, this value remains unchanged - * \param bounds The output containing the values defining the bounds of the FOV as - * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * If the method fails, this value remains unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getFieldOfView(const std::string& instrument, - std::string& fovShape, - std::string& frameName, - glm::dvec3& boresightVector, - std::vector& bounds) const; + /** + * Applies the transformationMatrix retrieved from + * getStateTransformMatrix to the position and velocity. The + * position and velocity parameters are used as input and + * output. + * \param position The position that should be transformed. The transformed position + * will be stored back in this parameter + * \param velocity The velocity that should be transformed. The transformed velocity + * will be stored back in this parameter + * \param transformationMatrix The 6x6 transformation matrix retrieved from + * getStateTransformMatrix that is used to transform the position and + * velocity vectors + */ + void applyTransformationMatrix(glm::dvec3& position, + glm::dvec3& velocity, + const TransformMatrix& transformationMatrix); + + /** + * This routine returns the field-of-view (FOV) parameters for a specified + * instrument. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * \param instrument The name of the instrument for which the FOV is to be retrieved + * \param fovShape The output containing the rough shape of the returned FOV. If the + * method fails, this value remains unchanged + * \param frameName The output containing the name of the frame in which the FOV + * bounds are computed. If the method fails, this value remains unchanged + * \param boresightVector The output containing the boresight, that is the vector for + * the center direction of the FOV. If the method fails, this value remains unchanged + * \param bounds The output containing the values defining the bounds of the FOV as + * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * If the method fails, this value remains unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getFieldOfView(const std::string& instrument, + std::string& fovShape, + std::string& frameName, + glm::dvec3& boresightVector, + std::vector& bounds) const; - /** - * This routine returns the field-of-view (FOV) parameters for a specified - * instrument. For further details, please refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * \param instrument The NAIF id of the instrument for which the FOV is to be - * retrieved. For more information on NAIF IDs, refer to - * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html - * \param fovShape The output containing the rough shape of the returned FOV. If the - * method fails, this value remains unchanged - * \param frameName The output containing the name of the frame in which the FOV - * bounds are computed. If the method fails, this value remains unchanged - * \param boresightVector The output containing the boresight, that is the vector for - * the center direction of the FOV. If the method fails, this value remains unchanged - * \param bounds The output containing the values defining the bounds of the FOV as - * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. - * If the method fails, this value remains unchanged - * \return true if the function was successful, false - * otherwise - */ - bool getFieldOfView(int instrument, std::string& fovShape, std::string& frameName, - glm::dvec3& boresightVector, std::vector& bounds) const; - - /** - This routine computes a set of points on the umbral or penumbral terminator of - a specified target body, where SPICE models the target shape as an ellipsoid. - \param numberOfPoints - number of points along terminator returned by this method - \param terminatorType - is a string indicating the type of terminator to compute: - umbral or penumbral. The umbral terminator is the boundary of the portion of the - ellipsoid surface in total shadow. The penumbral terminator is the boundary of - the portion of the surface that is completely illuminated. Note that in astronomy - references, the unqualified word "terminator" refers to the umbral terminator. - Here, the unqualified word refers to either type of terminator. - \param lightSource - name of body acting as light source - \param observer - name of bodserving body - \param target - name of target body - \param frame - name of the reference frame relative to which the output terminator - points are expressed. - \param aberrationCorrection - correction for light time and/or stellar aberration - \param ephemerisTime - the epoch of participation of the observer - \param targetEpoch - is the "target epoch.", time it takes for - \param observerPosition - is the vector from the target body at targetEpoch - \param terminatorPoints - an array of points on the umbral or penumbral terminator - of the ellipsoid, as specified by the input argument `numberOfPoints' - For further, more specific details please refer to - http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/edterm_c.html - */ - bool getTerminatorEllipse(const int numberOfPoints, - const std::string terminatorType, - const std::string lightSource, - const std::string observer, - const std::string target, - const std::string frame, - const std::string aberrationCorrection, - double ephemerisTime, - double& targetEpoch, - glm::dvec3& observerPosition, - std::vector& terminatorPoints); + /** + * This routine returns the field-of-view (FOV) parameters for a specified + * instrument. For further details, please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * \param instrument The NAIF id of the instrument for which the FOV is to be + * retrieved. For more information on NAIF IDs, refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/naif_ids.html + * \param fovShape The output containing the rough shape of the returned FOV. If the + * method fails, this value remains unchanged + * \param frameName The output containing the name of the frame in which the FOV + * bounds are computed. If the method fails, this value remains unchanged + * \param boresightVector The output containing the boresight, that is the vector for + * the center direction of the FOV. If the method fails, this value remains unchanged + * \param bounds The output containing the values defining the bounds of the FOV as + * explained by http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/getfov_c.html. + * If the method fails, this value remains unchanged + * \return true if the function was successful, false + * otherwise + */ + bool getFieldOfView(int instrument, std::string& fovShape, std::string& frameName, + glm::dvec3& boresightVector, std::vector& bounds) const; + + /** + * This routine computes a set of points on the umbral or penumbral terminator of + * a specified target body, where SPICE models the target shape as an ellipsoid. + * \param numberOfPoints - number of points along terminator returned by this method + * \param terminatorType - is a string indicating the type of terminator to compute: + * umbral or penumbral. The umbral terminator is the boundary of the portion of the + * ellipsoid surface in total shadow. The penumbral terminator is the boundary of + * the portion of the surface that is completely illuminated. Note that in astronomy + * references, the unqualified word "terminator" refers to the umbral terminator. + * Here, the unqualified word refers to either type of terminator. + * \param lightSource - name of body acting as light source + * \param observer - name of bodserving body + * \param target - name of target body + * \param frame - name of the reference frame relative to which the output terminator + * points are expressed. + * \param aberrationCorrection - correction for light time and/or stellar aberration + * \param ephemerisTime - the epoch of participation of the observer + * \param targetEpoch - is the "target epoch.", time it takes for + * \param observerPosition - is the vector from the target body at targetEpoch + * \param terminatorPoints - an array of points on the umbral or penumbral terminator + * of the ellipsoid, as specified by the input argument `numberOfPoints' + * For further, more specific details please refer to + * http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/edterm_c.html + */ + bool getTerminatorEllipse(const int numberOfPoints, + const std::string terminatorType, + const std::string lightSource, + const std::string observer, + const std::string target, + const std::string frame, + const std::string aberrationCorrection, + double ephemerisTime, + double& targetEpoch, + glm::dvec3& observerPosition, + std::vector& terminatorPoints); - /** - * This function adds a frame to a body - * \param body - the name of the body - * \param frame - the name of the frame - * \return false if the arguments are empty - */ - bool addFrame(const std::string body, const std::string frame); + /** + * This function adds a frame to a body + * \param body - the name of the body + * \param frame - the name of the frame + * \return false if the arguments are empty + */ + bool addFrame(const std::string body, const std::string frame); - /** - * This function returns the frame of a body if defined, otherwise it returns - * IAU_ + body (most frames are known by the International Astronomical Union) - * \param body - the name of the body - * \return the frame of the body - */ - std::string frameFromBody(const std::string body) const; + /** + * This function returns the frame of a body if defined, otherwise it returns + * IAU_ + body (most frames are known by the International Astronomical Union) + * \param body - the name of the body + * \return the frame of the body + */ + std::string frameFromBody(const std::string body) const; /** * This method checks if one of the previous SPICE methods has failed. If it has, the @@ -693,47 +678,48 @@ public: */ static bool checkForError(std::string errorMessage); - /** - * This method uses the SPICE kernels to get the radii of bodies defined as a - * triaxial ellipsoid. The benefit of this is to be able to create more accurate - * planet shapes, which is desirable when projecting images with SPICE intersection - * methods - * \param planetName - the name of the body, should be recognizable by SPICE - * \param a - equatorial radius 1 - * \param b - equatorial radius 2 - * \param c - polar radius - * \return true if SPICE reports no errors - */ - bool getPlanetEllipsoid(std::string planetName, float &a, float &b, float &c); + /** + * This method uses the SPICE kernels to get the radii of bodies defined as a + * triaxial ellipsoid. The benefit of this is to be able to create more accurate + * planet shapes, which is desirable when projecting images with SPICE intersection + * methods + * \param planetName - the name of the body, should be recognizable by SPICE + * \param a - equatorial radius 1 + * \param b - equatorial radius 2 + * \param c - polar radius + * \return true if SPICE reports no errors + */ + bool getPlanetEllipsoid(std::string planetName, float &a, float &b, float &c); -private: - struct KernelInformation { - std::string path; /// The path from which the kernel was loaded - KernelIdentifier id; /// A unique identifier for each kernel +protected: + struct KernelInformation { + std::string path; /// The path from which the kernel was loaded + KernelIdentifier id; /// A unique identifier for each kernel int refCount; /// How many parts loaded this kernel and are interested in it - }; + }; - SpiceManager() = default; - SpiceManager(const SpiceManager& c) = delete; - SpiceManager& operator=(const SpiceManager& r) = delete; - SpiceManager(SpiceManager&& r) = delete; + SpiceManager(); + SpiceManager(const SpiceManager& c) = delete; + SpiceManager& operator=(const SpiceManager& r) = delete; + SpiceManager(SpiceManager&& r) = delete; + ~SpiceManager(); /// A list of all loaded kernels - std::vector _loadedKernels; - // Map: id, vector of pairs. Pair: Start time, end time; - std::map > > _ckIntervals; - std::map > > _spkIntervals; - std::map > _ckCoverageTimes; - std::map > _spkCoverageTimes; - // Vector of pairs: Body, Frame - std::vector< std::pair > _frameByBody; - - const static bool _showErrors = false; + std::vector _loadedKernels; + // Map: id, vector of pairs. Pair: Start time, end time; + std::map > > _ckIntervals; + std::map > > _spkIntervals; + std::map > _ckCoverageTimes; + std::map > _spkCoverageTimes; + // Vector of pairs: Body, Frame + std::vector< std::pair > _frameByBody; + + const static bool _showErrors = false; /// The last assigned kernel-id, used to determine the next free kernel id - KernelIdentifier _lastAssignedKernel; + KernelIdentifier _lastAssignedKernel; - static SpiceManager* _manager; + static SpiceManager* _manager; }; } // namespace openspace diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index e03cc14326..8280208f98 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -51,12 +51,12 @@ namespace openspace { -BaseModule::BaseModule() { - setName("Base"); -} +BaseModule::BaseModule() + : OpenSpaceModule("Base") +{} -bool BaseModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool BaseModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/base/basemodule.h b/modules/base/basemodule.h index 37dc678bb2..597e6cf2af 100644 --- a/modules/base/basemodule.h +++ b/modules/base/basemodule.h @@ -32,7 +32,7 @@ namespace openspace { class BaseModule : public OpenSpaceModule { public: BaseModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/base/rendering/renderableplanet.cpp b/modules/base/rendering/renderableplanet.cpp index f84d7dc599..ff5fc0228b 100644 --- a/modules/base/rendering/renderableplanet.cpp +++ b/modules/base/rendering/renderableplanet.cpp @@ -231,7 +231,9 @@ void RenderablePlanet::loadTexture() { _texture->uploadTexture(); // Textures of planets looks much smoother with AnisotropicMipMap rather than linear - _texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); } } if (_hasNightTexture) { @@ -242,7 +244,8 @@ void RenderablePlanet::loadTexture() { if (_nightTexture) { LDEBUG("Loaded texture from '" << _nightTexturePath << "'"); _nightTexture->uploadTexture(); - _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); } } } diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index ae65124ca5..18ee8780e4 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -188,7 +188,9 @@ void RenderableSphere::loadTexture() { texture->uploadTexture(); // Textures of planets looks much smoother with AnisotropicMipMap rather than linear - texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); if (_texture) delete _texture; diff --git a/modules/fieldlines/fieldlinesmodule.cpp b/modules/fieldlines/fieldlinesmodule.cpp index 1c72f6d264..4fcba99081 100644 --- a/modules/fieldlines/fieldlinesmodule.cpp +++ b/modules/fieldlines/fieldlinesmodule.cpp @@ -33,12 +33,12 @@ namespace openspace { -FieldlinesModule::FieldlinesModule() { - setName("Fieldlines"); -} +FieldlinesModule::FieldlinesModule() + : OpenSpaceModule("Fieldlines") +{} -bool FieldlinesModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool FieldlinesModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/fieldlines/fieldlinesmodule.h b/modules/fieldlines/fieldlinesmodule.h index 2acd5d2164..8edc1d6705 100644 --- a/modules/fieldlines/fieldlinesmodule.h +++ b/modules/fieldlines/fieldlinesmodule.h @@ -32,7 +32,7 @@ namespace openspace { class FieldlinesModule : public OpenSpaceModule { public: FieldlinesModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/kameleon/CMakeLists.txt b/modules/kameleon/CMakeLists.txt index 724e7a40a9..edc593869b 100644 --- a/modules/kameleon/CMakeLists.txt +++ b/modules/kameleon/CMakeLists.txt @@ -49,8 +49,7 @@ create_new_module( mark_as_advanced(BUILD_SHARED_LIBS) # Change to set instead of option option(KAMELEON_USE_HDF5 "Kameleon use HDF5" OFF) set(KAMELEON_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/kameleon) - set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) - set(BOOST_ROOT "${OPENSPACE_EXT_DIR}/ghoul/ext/boost") + set(KAMELEON_INCLUDES ${KAMELEON_ROOT_DIR}/src) add_subdirectory(${KAMELEON_ROOT_DIR}) target_include_directories(${kameleon_module} SYSTEM PUBLIC ${KAMELEON_INCLUDES}) target_link_libraries(${kameleon_module} ccmc) diff --git a/modules/kameleon/kameleonmodule.cpp b/modules/kameleon/kameleonmodule.cpp index bde173db2b..1000b27184 100644 --- a/modules/kameleon/kameleonmodule.cpp +++ b/modules/kameleon/kameleonmodule.cpp @@ -26,12 +26,12 @@ namespace openspace { -KameleonModule::KameleonModule() { - setName("Kameleon"); -} +KameleonModule::KameleonModule() + : OpenSpaceModule("Kameleon") +{} -bool KameleonModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool KameleonModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/kameleon/kameleonmodule.h b/modules/kameleon/kameleonmodule.h index 19e2b51712..b99b5d3f77 100644 --- a/modules/kameleon/kameleonmodule.h +++ b/modules/kameleon/kameleonmodule.h @@ -32,7 +32,7 @@ namespace openspace { class KameleonModule : public OpenSpaceModule { public: KameleonModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/newhorizons/newhorizonsmodule.cpp b/modules/newhorizons/newhorizonsmodule.cpp index 72a59c096e..a822f91991 100644 --- a/modules/newhorizons/newhorizonsmodule.cpp +++ b/modules/newhorizons/newhorizonsmodule.cpp @@ -47,12 +47,12 @@ namespace openspace { -NewHorizonsModule::NewHorizonsModule() { - setName("NewHorizons"); -} +NewHorizonsModule::NewHorizonsModule() + : OpenSpaceModule("NewHorizons") +{} -bool NewHorizonsModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool NewHorizonsModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/newhorizons/newhorizonsmodule.h b/modules/newhorizons/newhorizonsmodule.h index ebbc4ef3d6..6ca4b49cda 100644 --- a/modules/newhorizons/newhorizonsmodule.h +++ b/modules/newhorizons/newhorizonsmodule.h @@ -32,7 +32,7 @@ namespace openspace { class NewHorizonsModule : public OpenSpaceModule { public: NewHorizonsModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index b3fb865b5b..d4d335b881 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -191,7 +191,9 @@ void RenderablePlaneProjection::loadTexture() { ghoul::opengl::Texture* texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); if (texture) { texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); if (_texture) delete _texture; _texture = texture; diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index e17d4f81bc..d2766bcf03 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -231,7 +231,8 @@ bool RenderablePlanetProjection::initialize() { completeSuccess &= _geometry->initialize(this); - completeSuccess &= auxiliaryRendertarget(); + if (completeSuccess) + completeSuccess &= auxiliaryRendertarget(); return completeSuccess; } @@ -289,7 +290,7 @@ bool RenderablePlanetProjection::deinitialize(){ return true; } bool RenderablePlanetProjection::isReady() const { - return _geometry && _programObject; + return _geometry && _programObject && _texture && _textureWhiteSquare; } void RenderablePlanetProjection::imageProjectGPU(){ @@ -414,7 +415,7 @@ void RenderablePlanetProjection::attitudeParameters(double time){ } -void RenderablePlanetProjection::textureBind(){ +void RenderablePlanetProjection::textureBind() { ghoul::opengl::TextureUnit unit[2]; unit[0].activate(); _texture->bind(); @@ -424,7 +425,7 @@ void RenderablePlanetProjection::textureBind(){ _programObject->setUniform("texture2", unit[1]); } -void RenderablePlanetProjection::project(){ +void RenderablePlanetProjection::project() { for (auto img : _imageTimes){ std::thread t1(&RenderablePlanetProjection::attitudeParameters, this, img.startTime); t1.join(); @@ -484,7 +485,7 @@ void RenderablePlanetProjection::render(const RenderData& data){ } -void RenderablePlanetProjection::update(const UpdateData& data){ +void RenderablePlanetProjection::update(const UpdateData& data) { // set spice-orientation in accordance to timestamp _time = Time::ref().currentTime(); _capture = false; @@ -498,20 +499,22 @@ void RenderablePlanetProjection::update(const UpdateData& data){ _programObject->rebuildFromFile(); } -void RenderablePlanetProjection::loadProjectionTexture(){ +void RenderablePlanetProjection::loadProjectionTexture() { delete _textureProj; _textureProj = nullptr; if (_colorTexturePath.value() != "") { _textureProj = ghoul::io::TextureReader::ref().loadTexture(absPath(_projectionTexturePath)); if (_textureProj) { _textureProj->uploadTexture(); - _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureProj->setFilter(ghoul::opengl::Texture::FilterMode::Linear); _textureProj->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToBorder); } } } -void RenderablePlanetProjection::loadTexture(){ +void RenderablePlanetProjection::loadTexture() { delete _texture; _texture = nullptr; if (_colorTexturePath.value() != "") { diff --git a/modules/newhorizons/util/hongkangparser.cpp b/modules/newhorizons/util/hongkangparser.cpp index e6b3a0cee3..3dcbfb4767 100644 --- a/modules/newhorizons/util/hongkangparser.cpp +++ b/modules/newhorizons/util/hongkangparser.cpp @@ -98,12 +98,12 @@ void findPlaybookSpecifiedTarget(std::string line, std::string& target){ } } -void HongKangParser::create(){ +bool HongKangParser::create(){ if (size_t position = _fileName.find_last_of(".") + 1){ if (position != std::string::npos){ std::string extension = ghoul::filesystem::File(_fileName).fileExtension(); - if (extension == "txt"){// Hong Kang. pre-parsed playbook + if (extension == "txt") {// Hong Kang. pre-parsed playbook LINFO("Using Preparsed Playbook V9H"); std::ifstream file(_fileName , std::ios::binary); if (!file.good()) LERROR("Failed to open txt file '" << _fileName << "'"); @@ -130,8 +130,8 @@ void HongKangParser::create(){ std::string cameraTarget = "VOID"; std::string scannerTarget = "VOID"; - while (!file.eof()){//only while inte do, FIX - std::getline(file, line); + while (std::getline(file, line)) {//only while inte do, FIX + //std::getline(file, line); std::string event = line.substr(0, line.find_first_of(" ")); @@ -249,36 +249,7 @@ void HongKangParser::create(){ } sendPlaybookInformation(PlaybookIdentifierName); - - //std::ofstream myfile; - //myfile.open("HongKangOutput.txt"); - - ////print all - //for (auto target : _subsetMap){ - // std::string min, max; - // SpiceManager::ref().getDateFromET(target.second._range._min, min); - // SpiceManager::ref().getDateFromET(target.second._range._max, max); - - // myfile << std::endl; - // for (auto image : target.second._subset){ - // std::string time_beg; - // std::string time_end; - // SpiceManager::ref().getDateFromET(image.startTime, time_beg); - // SpiceManager::ref().getDateFromET(image.stopTime, time_end); - - // myfile << std::fixed - // << std::setw(10) << time_beg - // << std::setw(10) << time_end - // << std::setw(10) << (int)getMetFromET(image.startTime) - // << std::setw(10) << image.target << std::setw(10); - // for (auto instrument : image.activeInstruments){ - // myfile << " " << instrument; - // } - // myfile << std::endl; - // } - //} - //myfile.close(); - // + return true; } bool HongKangParser::augmentWithSpice(Image& image, diff --git a/modules/newhorizons/util/hongkangparser.h b/modules/newhorizons/util/hongkangparser.h index d74f43db40..e787aafb6d 100644 --- a/modules/newhorizons/util/hongkangparser.h +++ b/modules/newhorizons/util/hongkangparser.h @@ -41,7 +41,7 @@ public: std::string spacecraft, ghoul::Dictionary dictionary, std::vector potentialTargets); - virtual void create(); + bool create() override; // temporary need to figure this out virtual std::map getTranslation(){ return _fileTranslation; }; diff --git a/modules/newhorizons/util/imagesequencer2.cpp b/modules/newhorizons/util/imagesequencer2.cpp index 812b9ce9cd..6e5b4a3543 100644 --- a/modules/newhorizons/util/imagesequencer2.cpp +++ b/modules/newhorizons/util/imagesequencer2.cpp @@ -301,34 +301,31 @@ void ImageSequencer2::sortData(){ } void ImageSequencer2::runSequenceParser(SequenceParser* parser){ - parser->create(); + bool success = parser->create(); + if (!success) + return; // get new data - std::map in1 = parser->getTranslation(); - std::map in2 = parser->getSubsetMap(); - std::vector> in3 = parser->getIstrumentTimes(); - std::vector> in4 = parser->getTargetTimes(); - std::vector in5 = parser->getCaptureProgression(); + std::map translations = parser->getTranslation(); + std::map imageData = parser->getSubsetMap(); + std::vector> instrumentTimes = parser->getIstrumentTimes(); + std::vector> targetTimes = parser->getTargetTimes(); + std::vector captureProgression = parser->getCaptureProgression(); // check for sanity - ghoul_assert(in1.size() > 0, "Sequencer failed to load Translation" ); - ghoul_assert(in2.size() > 0, "Sequencer failed to load Image data" ); - ghoul_assert(in3.size() > 0, "Sequencer failed to load Instrument Switching schedule"); - ghoul_assert(in4.size() > 0, "Sequencer failed to load Target Switching schedule" ); - ghoul_assert(in5.size() > 0, "Sequencer failed to load Capture progression" ); + if (translations.empty() || imageData.empty() || instrumentTimes.empty() || targetTimes.empty() || captureProgression.empty()) + return; - - // append data - _fileTranslation.insert ( in1.begin(), in1.end()); - _subsetMap.insert ( in2.begin(), in2.end()); - _instrumentTimes.insert ( _instrumentTimes.end(), in3.begin(), in3.end()); - _targetTimes.insert ( _targetTimes.end(), in4.begin(), in4.end()); - _captureProgression.insert(_captureProgression.end(), in5.begin(), in5.end()); + _fileTranslation.insert(translations.begin(), translations.end()); + _subsetMap.insert(imageData.begin(), imageData.end()); + _instrumentTimes.insert(_instrumentTimes.end(), instrumentTimes.begin(), instrumentTimes.end()); + _targetTimes.insert(_targetTimes.end(), targetTimes.begin(), targetTimes.end()); + _captureProgression.insert(_captureProgression.end(), captureProgression.begin(), captureProgression.end()); // sorting of data _not_ optional sortData(); // extract payload from _fileTranslation - for (auto t : _fileTranslation){ + for (auto t : _fileTranslation) { if (t.second->getDecoderType() == "CAMERA" || t.second->getDecoderType() == "SCANNER" ){ std::vector spiceIDs = t.second->getTranslation(); @@ -339,4 +336,5 @@ void ImageSequencer2::runSequenceParser(SequenceParser* parser){ } _hasData = true; } + } // namespace openspace diff --git a/modules/newhorizons/util/labelparser.cpp b/modules/newhorizons/util/labelparser.cpp index 7911d90cdb..47392af164 100644 --- a/modules/newhorizons/util/labelparser.cpp +++ b/modules/newhorizons/util/labelparser.cpp @@ -118,7 +118,7 @@ std::string LabelParser::encode(std::string line) { return ""; } -void LabelParser::create(){ +bool LabelParser::create() { auto imageComparer = [](const Image &a, const Image &b)->bool{ return a.startTime < b.startTime; }; @@ -132,7 +132,7 @@ void LabelParser::create(){ ghoul::filesystem::Directory sequenceDir(_fileName, true); if (!FileSys.directoryExists(sequenceDir)) { LERROR("Could not load Label Directory '" << sequenceDir.path() << "'"); - return; + return false; } std::vector sequencePaths = sequenceDir.read(true, false); // check inputs for (auto path : sequencePaths){ @@ -255,6 +255,7 @@ void LabelParser::create(){ ////print all for (auto target : _subsetMap){ _instrumentTimes.push_back(std::make_pair(lblName, _subsetMap[target.first]._range)); + // std::string min, max; // SpiceManager::ref().getDateFromET(target.second._range._min, min); // SpiceManager::ref().getDateFromET(target.second._range._max, max); @@ -281,6 +282,7 @@ void LabelParser::create(){ sendPlaybookInformation(PlaybookIdentifierName); + return true; } void LabelParser::createImage(Image& image, double startTime, double stopTime, std::vector instr, std::string targ, std::string pot) { diff --git a/modules/newhorizons/util/labelparser.h b/modules/newhorizons/util/labelparser.h index 6efa1c77ca..79cbe85e2c 100644 --- a/modules/newhorizons/util/labelparser.h +++ b/modules/newhorizons/util/labelparser.h @@ -38,7 +38,7 @@ public: LabelParser(); LabelParser(const std::string& fileName, ghoul::Dictionary translationDictionary); - virtual void create(); + bool create() override; // temporary need to figure this out std::map getTranslation(){ return _fileTranslation; }; diff --git a/modules/newhorizons/util/sequenceparser.h b/modules/newhorizons/util/sequenceparser.h index 4eb3a42582..b8573dd712 100644 --- a/modules/newhorizons/util/sequenceparser.h +++ b/modules/newhorizons/util/sequenceparser.h @@ -67,7 +67,7 @@ struct ImageSubset { class SequenceParser { public: - virtual void create() = 0; + virtual bool create() = 0; virtual std::map getSubsetMap() final; virtual std::vector> getIstrumentTimes() final; virtual std::vector> getTargetTimes() final; diff --git a/modules/onscreengui/onscreenguimodule.cpp b/modules/onscreengui/onscreenguimodule.cpp index ac024bc300..8518eef832 100644 --- a/modules/onscreengui/onscreenguimodule.cpp +++ b/modules/onscreengui/onscreenguimodule.cpp @@ -26,12 +26,12 @@ namespace openspace { -OnScreenGUIModule::OnScreenGUIModule() { - setName("OnScreenGUI"); -} +OnScreenGUIModule::OnScreenGUIModule() + : OpenSpaceModule("OnScreenGUI") +{} -bool OnScreenGUIModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool OnScreenGUIModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; return true; diff --git a/modules/onscreengui/onscreenguimodule.h b/modules/onscreengui/onscreenguimodule.h index 0479d78b99..affd7bb9d5 100644 --- a/modules/onscreengui/onscreenguimodule.h +++ b/modules/onscreengui/onscreenguimodule.h @@ -32,7 +32,7 @@ namespace openspace { class OnScreenGUIModule : public OpenSpaceModule { public: OnScreenGUIModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/modules/volume/rendering/renderablevolumegl.cpp b/modules/volume/rendering/renderablevolumegl.cpp index 7c45905eb5..de1deeb89f 100644 --- a/modules/volume/rendering/renderablevolumegl.cpp +++ b/modules/volume/rendering/renderablevolumegl.cpp @@ -177,13 +177,13 @@ bool RenderableVolumeGL::initialize() { if(_filename != "") { _volume = loadVolume(_filename, _hintsDictionary); _volume->uploadTexture(); - OsEng.renderEngine()->abuffer()->addVolume(_volumeName, _volume); + OsEng.renderEngine()->aBuffer()->addVolume(_volumeName, _volume); } if(_transferFunctionPath != "") { _transferFunction = loadTransferFunction(_transferFunctionPath); _transferFunction->uploadTexture(); - OsEng.renderEngine()->abuffer()->addTransferFunction(_transferFunctionName, _transferFunction); + OsEng.renderEngine()->aBuffer()->addTransferFunction(_transferFunctionName, _transferFunction); auto textureCallback = [this](const ghoul::filesystem::File& file) { _updateTransferfunction = true; @@ -192,7 +192,7 @@ bool RenderableVolumeGL::initialize() { } // add the sampler and get the ID - _id = OsEng.renderEngine()->abuffer()->addSamplerfile(_samplerFilename); + _id = OsEng.renderEngine()->aBuffer()->addSamplerfile(_samplerFilename); OsEng.configurationManager()->getValue("RaycastProgram", _boxProgram); diff --git a/modules/volume/volumemodule.cpp b/modules/volume/volumemodule.cpp index 08eba0f856..274e408f6f 100644 --- a/modules/volume/volumemodule.cpp +++ b/modules/volume/volumemodule.cpp @@ -33,12 +33,12 @@ namespace openspace { -VolumeModule::VolumeModule() { - setName("Volume"); -} +VolumeModule::VolumeModule() + : OpenSpaceModule("Volume") +{} -bool VolumeModule::initialize() { - bool success = OpenSpaceModule::initialize(); +bool VolumeModule::create() { + bool success = OpenSpaceModule::create(); if (!success) return false; diff --git a/modules/volume/volumemodule.h b/modules/volume/volumemodule.h index 73094ba1d6..1e57079063 100644 --- a/modules/volume/volumemodule.h +++ b/modules/volume/volumemodule.h @@ -32,7 +32,7 @@ namespace openspace { class VolumeModule : public OpenSpaceModule { public: VolumeModule(); - bool initialize() override; + bool create() override; }; } // namespace openspace diff --git a/openspace.cfg b/openspace.cfg index 7b156a7b68..c41cecff48 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -7,7 +7,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${OPENSPACE_DATA}/scene/default_nh.scene", + Scene = "${SCENE}/default_nh.scene", Paths = { SGCT = "${BASE_PATH}/config/sgct", @@ -15,18 +15,18 @@ return { SHADERS = "${BASE_PATH}/shaders", SHADERS_GENERATED = "${SHADERS}/generated", OPENSPACE_DATA = "${BASE_PATH}/data", + SCENE = "${OPENSPACE_DATA}/scene", + SPICE = "${OPENSPACE_DATA}/spice", MODULES = "${BASE_PATH}/modules", TESTDIR = "${BASE_PATH}/tests", CONFIG = "${BASE_PATH}/config", CACHE = "${BASE_PATH}/cache", FONTS = "${OPENSPACE_DATA}/fonts", - PLUTO_KERNELS = "${OPENSPACE_DATA}/spice/Pluto", - JP_KERNELS = "${OPENSPACE_DATA}/spice/JP_KERNELS" }, SpiceKernel = { - Time = "${OPENSPACE_DATA}/spice/naif0010.tls", - LeapSecond = "${OPENSPACE_DATA}/spice/pck00010.tpc", - NewHorizons = "${OPENSPACE_DATA}/spice/nhmeta.tm" + Time = "${SPICE}/naif0010.tls", + LeapSecond = "${SPICE}/pck00010.tpc", + NewHorizons = "${SPICE}/nhmeta.tm" }, Fonts = { Mono = "${FONTS}/Droid_Sans_Mono/DroidSansMono.ttf", @@ -43,7 +43,8 @@ return { ImmediateFlush = true, Logs = { { Type = "HTML", FileName = "${BASE_PATH}/log.html", Append = false } - } + }, + CapabilitiesVerbosity = "Full" }, LuaDocumentationFile = { Type = "text", @@ -53,5 +54,6 @@ return { Type = "text", File = "${BASE_PATH}/Properties.txt" }, - -- RenderingMethod = "ABufferFrameBuffer" + DownloadRequestURL = "http://openspace.itn.liu.se/request.cgi", + RenderingMethod = "ABufferFrameBuffer" } \ No newline at end of file diff --git a/shaders/ABuffer/abufferResolveFragment.glsl b/shaders/ABuffer/abufferResolveFragment.glsl index d9e45834f9..dc6fedcb07 100644 --- a/shaders/ABuffer/abufferResolveFragment.glsl +++ b/shaders/ABuffer/abufferResolveFragment.glsl @@ -56,6 +56,8 @@ const float stepSize = 0.01; const float samplingRate = 1.0; uniform float ALPHA_LIMIT = 0.99; +uniform float blackoutFactor = 0.0; + // Math defintions #define M_E 2.7182818284590452354 @@ -307,7 +309,7 @@ void main() { out_color = vec4(texCoord,0.0,1.0); int frag_count = build_local_fragments_list(); sort_fragments_list(frag_count); - out_color = calculate_final_color(frag_count); + out_color = blackoutFactor * calculate_final_color(frag_count); } // ================================================================================ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8134bd3b4..20f9e3b0b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/abuffer/abuffersinglelinked.cpp ${OPENSPACE_BASE_DIR}/src/abuffer/abuffervisualizer.cpp ${OPENSPACE_BASE_DIR}/src/engine/configurationmanager.cpp + ${OPENSPACE_BASE_DIR}/src/engine/downloadmanager.cpp ${OPENSPACE_BASE_DIR}/src/engine/logfactory.cpp ${OPENSPACE_BASE_DIR}/src/engine/moduleengine.cpp ${OPENSPACE_BASE_DIR}/src/engine/openspaceengine.cpp @@ -91,6 +92,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffersinglelinked.h ${OPENSPACE_BASE_DIR}/include/openspace/abuffer/abuffervisualizer.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/configurationmanager.h + ${OPENSPACE_BASE_DIR}/include/openspace/engine/downloadmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/logfactory.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/moduleengine.h ${OPENSPACE_BASE_DIR}/include/openspace/engine/openspaceengine.h diff --git a/src/abuffer/abuffer.cpp b/src/abuffer/abuffer.cpp index 700413430d..afadda50fa 100644 --- a/src/abuffer/abuffer.cpp +++ b/src/abuffer/abuffer.cpp @@ -55,20 +55,17 @@ namespace openspace { ABuffer::ABuffer() : _validShader(false) , _resolveShader(nullptr) - , _volumeStepFactor(0.0f) + , _volumeStepFactor(0.f) { updateDimensions(); } ABuffer::~ABuffer() { - - if(_resolveShader) - delete _resolveShader; + delete _resolveShader; - for(auto file: _samplerFiles) { + for (auto file: _samplerFiles) delete file; - } } bool ABuffer::initializeABuffer() { @@ -88,14 +85,14 @@ bool ABuffer::initializeABuffer() { if (!_resolveShader) return false; _resolveShader->setProgramObjectCallback(shaderCallback); + // Remove explicit callback and use programobject isDirty instead ---abock -#ifndef __APPLE__ // ============================ // GEOMETRY (quad) // ============================ const GLfloat size = 1.0f; - const GLfloat vertex_data[] = { // square of two triangles (sigh) - // x y z w s t + const GLfloat vertex_data[] = { + // x y s t -size, -size, 0.0f, 1.0f, size, size, 0.0f, 1.0f, -size, size, 0.0f, 1.0f, @@ -111,20 +108,17 @@ bool ABuffer::initializeABuffer() { glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, reinterpret_cast(0)); glEnableVertexAttribArray(0); -#endif return true; } bool ABuffer::reinitialize() { - // set the total resolution for all viewports updateDimensions(); return reinitializeInternal(); } -void ABuffer::resolve() { -#ifndef __APPLE__ - if( ! _validShader) { +void ABuffer::resolve(float blackoutFactor) { + if (!_validShader) { generateShaderSource(); updateShader(); _validShader = true; @@ -134,13 +128,14 @@ void ABuffer::resolve() { return; _resolveShader->activate(); + _resolveShader->setUniform("blackoutFactor", blackoutFactor); int startAt = 0; - for(int i = 0; i < _volumes.size(); ++i) { + for (int i = 0; i < _volumes.size(); ++i) { glActiveTexture(GL_TEXTURE0 + i); _volumes.at(i).second->bind(); startAt = i + 1; } - for(int i = 0; i < _transferFunctions.size(); ++i) { + for (int i = 0; i < _transferFunctions.size(); ++i) { glActiveTexture(GL_TEXTURE0 + startAt + i); _transferFunctions.at(i).second->bind(); } @@ -158,7 +153,6 @@ void ABuffer::resolve() { glDrawArrays(GL_TRIANGLES, 0, 6); _resolveShader->deactivate(); -#endif } void ABuffer::addVolume(const std::string& tag,ghoul::opengl::Texture* volume) { @@ -173,7 +167,6 @@ int ABuffer::addSamplerfile(const std::string& filename) { if( ! FileSys.fileExists(filename)) return -1; -#ifndef __APPLE__ auto fileCallback = [this](const ghoul::filesystem::File& file) { _validShader = false; }; @@ -185,9 +178,6 @@ int ABuffer::addSamplerfile(const std::string& filename) { // ID is one more than "actual" position since ID=0 is considered geometry //return 1 << (_samplers.size()-1); return static_cast(_samplers.size()); -#else - return 0; -#endif } bool ABuffer::updateShader() { @@ -212,8 +202,7 @@ bool ABuffer::updateShader() { } void ABuffer::generateShaderSource() { - - for(int i = 0; i < _samplerFiles.size(); ++i) { + for (int i = 0; i < _samplerFiles.size(); ++i) { std::string line, source = ""; std::ifstream samplerFile(_samplerFiles.at(i)->path()); if(samplerFile.is_open()) { @@ -233,7 +222,6 @@ void ABuffer::generateShaderSource() { } void ABuffer::openspaceHeaders() { - std::ofstream f(absPath(generatedHeadersPath)); f << "#define MAX_VOLUMES " << std::to_string(_samplers.size()) << "\n" << "#define MAX_TF " << _transferFunctions.size() << "\n"; diff --git a/src/abuffer/abufferframebuffer.cpp b/src/abuffer/abufferframebuffer.cpp index e2c13d7cae..007ded525c 100644 --- a/src/abuffer/abufferframebuffer.cpp +++ b/src/abuffer/abufferframebuffer.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -59,10 +60,31 @@ void ABufferFramebuffer::preRender() { void ABufferFramebuffer::postRender() { } + +void ABufferFramebuffer::resolve(float blackoutFactor) { +} std::vector ABufferFramebuffer::pixelData() { return std::vector(); } +bool ABufferFramebuffer::initializeABuffer() { + // ============================ + // SHADERS + // ============================ + auto shaderCallback = [this](ghoul::opengl::ProgramObject* program) { + // Error for visibility in log + _validShader = false; + }; -} // openspace \ No newline at end of file + generateShaderSource(); + _resolveShader = ghoul::opengl::ProgramObject::Build( + "ABufferResolve", + "${SHADERS}/ABuffer/abufferResolveVertex.glsl", + "${SHADERS}/ABuffer/abufferResolveFragment.glsl"); + if (!_resolveShader) + return false; + _resolveShader->setProgramObjectCallback(shaderCallback); +} + +} // openspace diff --git a/src/abuffer/abuffersinglelinked.cpp b/src/abuffer/abuffersinglelinked.cpp index 89750f6ced..834f54ad07 100644 --- a/src/abuffer/abuffersinglelinked.cpp +++ b/src/abuffer/abuffersinglelinked.cpp @@ -56,11 +56,11 @@ ABufferSingleLinked::ABufferSingleLinked() {} ABufferSingleLinked::~ABufferSingleLinked() { - glDeleteTextures(1,&_anchorPointerTexture); - glDeleteTextures(1,&_fragmentTexture); - glDeleteBuffers(1,&_anchorPointerTextureInitializer); - glDeleteBuffers(1,&_atomicCounterBuffer); - glDeleteBuffers(1,&_anchorPointerTextureInitializer); + glDeleteTextures(1, &_anchorPointerTexture); + glDeleteTextures(1, &_fragmentTexture); + glDeleteBuffers(1, &_anchorPointerTextureInitializer); + glDeleteBuffers(1, &_atomicCounterBuffer); + glDeleteBuffers(1, &_anchorPointerTextureInitializer); } bool ABufferSingleLinked::initialize() { @@ -123,7 +123,6 @@ void ABufferSingleLinked::clear() { } void ABufferSingleLinked::preRender() { - // Bind head-pointer image for read-write glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, _atomicCounterBuffer); glBindImageTexture(0, _anchorPointerTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); @@ -135,7 +134,6 @@ void ABufferSingleLinked::postRender() { } std::vector ABufferSingleLinked::pixelData() { - unsigned int* anchorTexture = new unsigned int[_totalPixels]; unsigned int* fragmentBuffer = new unsigned int[_totalPixels*MAX_LAYERS*4]; @@ -201,4 +199,4 @@ std::vector ABufferSingleLinked::pixelData() return d; } -} // openspace \ No newline at end of file +} // openspace diff --git a/src/engine/configurationmanager.cpp b/src/engine/configurationmanager.cpp index 65f9f26f81..e8a423df53 100644 --- a/src/engine/configurationmanager.cpp +++ b/src/engine/configurationmanager.cpp @@ -57,7 +57,9 @@ const std::string ConfigurationManager::KeySpiceLeapsecondKernel = "SpiceKernel. const std::string ConfigurationManager::KeyLogLevel = "Logging.LogLevel"; const std::string ConfigurationManager::KeyLogImmediateFlush = "Logging.ImmediateFlush"; const std::string ConfigurationManager::KeyLogs = "Logging.Logs"; +const std::string ConfigurationManager::KeyCapabilitiesVerbosity = "Logging.CapabilitiesVerbosity"; const std::string ConfigurationManager::KeyDisableMasterRendering = "DisableRenderingOnMaster"; +const std::string ConfigurationManager::KeyDownloadRequestURL = "DownloadRequestURL"; bool ConfigurationManager::loadFromFile(const std::string& filename) { using ghoul::filesystem::FileSystem; diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp new file mode 100644 index 0000000000..715279cd5c --- /dev/null +++ b/src/engine/downloadmanager.cpp @@ -0,0 +1,278 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2015 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef OPENSPACE_CURL_ENABLED +#include +#endif + +#ifdef WIN32 +#include +#endif + +namespace { + const std::string _loggerCat = "DownloadManager"; + + const std::string RequestIdentifier = "identifier"; + const std::string RequestFileVersion = "file_version"; + const std::string RequestApplicationVersion = "application_version"; + + struct ProgressInformation { + openspace::DownloadManager::FileFuture* future; + std::chrono::system_clock::time_point startTime; + const openspace::DownloadManager::DownloadProgressCallback* callback; + }; + + size_t writeData(void* ptr, size_t size, size_t nmemb, FILE* stream) { + size_t written; + written = fwrite(ptr, size, nmemb, stream); + return written; + } + + + int xferinfo(void* p, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) + { + if (dltotal == 0) + return 0; + + ghoul_assert(p, "Passed progress information is nullptr"); + ProgressInformation* i = static_cast(p); + ghoul_assert(i, "Passed pointer is not a ProgressInformation"); + ghoul_assert(i->future, "FileFuture is not initialized"); + ghoul_assert(i->callback, "Callback pointer is nullptr"); + + if (i->future->abortDownload) { + i->future->isAborted = true; + return 1; + } + + i->future->currentSize = dlnow; + i->future->totalSize = dltotal; + i->future->progress = static_cast(dlnow) / static_cast(dltotal); + + auto now = std::chrono::system_clock::now(); + + // Compute time spent transferring. + auto transferTime = now - i->startTime; + // Compute estimated transfer time. + auto estimatedTime = transferTime / i->future->progress; + // Compute estimated time remaining. + auto timeRemaining = estimatedTime - transferTime; + + float s = std::chrono::duration_cast(timeRemaining).count(); + + i->future->secondsRemaining = s; + + if (*(i->callback)) { + // The callback function is a pointer to an std::function; that is the reason + // for the excessive referencing + (*(i->callback))(*(i->future)); + } + + return 0; + } +} + +namespace openspace { + +DownloadManager::FileFuture::FileFuture(std::string file) + : currentSize(-1) + , totalSize(-1) + , progress(0.f) + , secondsRemaining(-1.f) + , isFinished(false) + , isAborted(false) + , filePath(std::move(file)) + , errorMessage("") + , abortDownload(false) +{} + +DownloadManager::DownloadManager(std::string requestURL, int applicationVersion) + : _requestURL(std::move(requestURL)) + , _applicationVersion(std::move(applicationVersion)) +{ + curl_global_init(CURL_GLOBAL_ALL); + // TODO: Check if URL is accessible ---abock + // TODO: Allow for multiple requestURLs +} + +DownloadManager::FileFuture* DownloadManager::downloadFile( + const std::string& url, + const ghoul::filesystem::File& file, + bool overrideFile, + DownloadFinishedCallback finishedCallback, + DownloadProgressCallback progressCallback) +{ + if (!overrideFile && FileSys.fileExists(file)) + return nullptr; + + FileFuture* future = new FileFuture( + file.filename() + ); + FILE* fp = fopen(file.path().c_str(), "wb"); + + LDEBUG("Starting download for file: '" << url << + "' into file '" << file.path() << "'"); + std::thread t = std::thread([url, finishedCallback, progressCallback, future, fp]() { + CURL* curl = curl_easy_init(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); + + ProgressInformation p = { + future, + std::chrono::system_clock::now(), + &progressCallback + }; + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + fclose(fp); + + if (res == CURLE_OK) + future->isFinished = true; + else + future->errorMessage = curl_easy_strerror(res); + + if (finishedCallback) + finishedCallback(*future); + } + }); + +#ifdef WIN32 + std::thread::native_handle_type h = t.native_handle(); + SetPriorityClass(h, IDLE_PRIORITY_CLASS); + SetThreadPriority(h, THREAD_PRIORITY_LOWEST); +#else + // TODO: Implement thread priority ---abock +#endif + + t.detach(); + + return future; +} + +std::vector DownloadManager::downloadRequestFiles( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles, + DownloadFinishedCallback finishedCallback, + DownloadProgressCallback progressCallback) +{ + std::vector futures; + bool s = FileSys.createDirectory(destination, true); + // TODO: Check s ---abock + // TODO: Escaping is necessary ---abock + const std::string fullRequest =_requestURL + "?" + + RequestIdentifier + "=" + identifier + "&" + + RequestFileVersion + "=" + std::to_string(version) + "&" + + RequestApplicationVersion + "=" + std::to_string(_applicationVersion); + LDEBUG("Request: " << fullRequest); + + std::string requestFile = absPath("${TEMPORARY}/" + identifier); + LDEBUG("Request File: " << requestFile); + + bool isFinished = false; + auto callback = [&futures, destination, &progressCallback, &isFinished, requestFile, overrideFiles](const FileFuture& f) { + LDEBUG("Finished: " << requestFile); + std::ifstream temporary(requestFile); + std::string line; + int nFiles = 0; + int nFinished = 0; + while (std::getline(temporary, line)) { + ++nFiles; + std::string file = ghoul::filesystem::File(line).filename(); + + LDEBUG("\tLine: " << line << " ; Dest: " << destination.path() + "/" + file); + + FileFuture* future = DlManager.downloadFile( + line, + destination.path() + "/" + file, + overrideFiles + ); + if (future) + futures.push_back(future); + } + isFinished = true; + }; + + FileFuture* f = downloadFile( + fullRequest, + requestFile, + true, + callback + ); + + while (!isFinished) {} + + return futures; +} + +void DownloadManager::downloadRequestFilesAsync( + const std::string& identifier, + const ghoul::filesystem::Directory& destination, + int version, + bool overrideFiles, + AsyncDownloadFinishedCallback callback) +{ + std::thread t = std::thread([this, identifier, destination, version, overrideFiles, callback](){ + std::vector f = downloadRequestFiles( + identifier, + destination, + version, + overrideFiles + ); + + callback(f); + }); + +#ifdef WIN32 + std::thread::native_handle_type h = t.native_handle(); + SetPriorityClass(h, IDLE_PRIORITY_CLASS); + SetThreadPriority(h, THREAD_PRIORITY_LOWEST); +#else + // TODO: Implement thread priority ---abock +#endif + + t.detach(); +} + +} // namespace openspace diff --git a/src/engine/moduleengine.cpp b/src/engine/moduleengine.cpp index 86f5163810..d6888df8f9 100644 --- a/src/engine/moduleengine.cpp +++ b/src/engine/moduleengine.cpp @@ -36,11 +36,39 @@ namespace { namespace openspace { -bool ModuleEngine::initialize() { - LDEBUG("Initializing modules"); +bool ModuleEngine::create() { + LDEBUG("Creating modules"); registerModules(AllModules); + for (OpenSpaceModule* m : _modules) { + bool success = m->create(); + if (!success) { + LERROR("Could not initialize module '" << m->name() << "'"); + return false; + } + } + LDEBUG("Finished creating modules"); + return true; +} + +bool ModuleEngine::destroy() { + LDEBUG("Destroying modules"); + for (OpenSpaceModule* m : _modules) { + bool success = m->destroy(); + if (!success) { + LERROR("Could not deinitialize module '" << m->name() << "'"); + return false; + } + delete m; + } + LDEBUG("Finished destroying modules"); + return true; +} + +bool ModuleEngine::initialize() { + LDEBUG("Initializing modules"); + for (OpenSpaceModule* m : _modules) { bool success = m->initialize(); if (!success) { @@ -54,13 +82,13 @@ bool ModuleEngine::initialize() { bool ModuleEngine::deinitialize() { LDEBUG("Deinitializing modules"); + for (OpenSpaceModule* m : _modules) { bool success = m->deinitialize(); if (!success) { LERROR("Could not deinitialize module '" << m->name() << "'"); return false; } - delete m; } LDEBUG("Finished Deinitializing modules"); return true; @@ -78,5 +106,4 @@ const std::vector ModuleEngine::modules() const { return _modules; } - } // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 8261b3c3f5..3d919d213c 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,6 @@ #endif #endif - using namespace openspace::scripting; using namespace ghoul::filesystem; using namespace ghoul::logging; @@ -87,6 +87,7 @@ namespace { const std::string _sgctConfigArgumentCommand = "-config"; const int CacheVersion = 1; + const int DownloadVersion = 1; struct { std::string configurationName; @@ -229,7 +230,7 @@ bool OpenSpaceEngine::create( } // Register modules - _engine->_moduleEngine->initialize(); + _engine->_moduleEngine->create(); // Create the cachemanager FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion); @@ -263,6 +264,7 @@ bool OpenSpaceEngine::create( void OpenSpaceEngine::destroy() { _engine->_moduleEngine->deinitialize(); + _engine->_moduleEngine->destroy(); _engine->_console->deinitialize(); _engine->_scriptEngine->deinitialize(); @@ -287,10 +289,29 @@ bool OpenSpaceEngine::initialize() { SysCap.addComponent(new ghoul::systemcapabilities::GeneralCapabilitiesComponent); SysCap.addComponent(new ghoul::systemcapabilities::OpenGLCapabilitiesComponent); SysCap.detectCapabilities(); - SysCap.logCapabilities(); + + using Verbosity = ghoul::systemcapabilities::SystemCapabilitiesComponent::Verbosity; + Verbosity verbosity = Verbosity::Default; + if (configurationManager()->hasKeyAndValue(ConfigurationManager::KeyCapabilitiesVerbosity)) { + std::map verbosityMap = { + { "Minimal", Verbosity::Minimal }, + { "Default", Verbosity::Default }, + { "Full", Verbosity::Full } + }; + + std::string v = configurationManager()->value(ConfigurationManager::KeyCapabilitiesVerbosity); + if (verbosityMap.find(v) != verbosityMap.end()) + verbosity = verbosityMap[v]; + } + SysCap.logCapabilities(verbosity); + std::string requestURL = ""; + bool success = configurationManager()->getValue(ConfigurationManager::KeyDownloadRequestURL, requestURL); + if (success) + DownloadManager::initialize(requestURL, DownloadVersion); + // Load SPICE time kernel - bool success = loadSpiceKernels(); + success = loadSpiceKernels(); if (!success) return false; @@ -331,11 +352,6 @@ bool OpenSpaceEngine::initialize() { // initialize the RenderEngine _renderEngine->initialize(); - // if (_configurationManager->hasKeyAndValue(KeyRenderingMethod)) - // _renderEngine->initialize(_configurationManager->value(KeyRenderingMethod)); - // else - // _renderEngine->initialize(DefaultRenderingMethod); - sceneGraph->initialize(); std::string sceneDescriptionPath; @@ -345,8 +361,6 @@ bool OpenSpaceEngine::initialize() { sceneGraph->scheduleLoadSceneFile(sceneDescriptionPath); _interactionHandler->setKeyboardController(new interaction::KeyboardControllerFixed); - //_interactionHandler.setKeyboardController(new interaction::KeyboardControllerLua); - //_interactionHandler.setMouseController(new interaction::TrackballMouseController); _interactionHandler->setMouseController(new interaction::OrbitalMouseController); // Run start up scripts @@ -358,6 +372,8 @@ bool OpenSpaceEngine::initialize() { LINFO("Initializing GUI"); _gui->initialize(); + // Initialize modules + _moduleEngine->initialize(); LINFO("Finished initializing"); return true; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 0a761ee744..92ea150b82 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -81,13 +81,6 @@ namespace { const std::string KeyRenderingMethod = "RenderingMethod"; const std::string DefaultRenderingMethod = "ABufferSingleLinked"; - - const std::map RenderingMethods = { - { "ABufferFrameBuffer", ABUFFER_FRAMEBUFFER}, - { "ABufferSingleLinked", ABUFFER_SINGLE_LINKED }, - { "ABufferFixed", ABUFFER_FIXED }, - { "ABufferDynamic", ABUFFER_DYNAMIC } - }; } namespace openspace { @@ -99,7 +92,7 @@ RenderEngine::RenderEngine() : _mainCamera(nullptr) , _sceneGraph(nullptr) , _abuffer(nullptr) - , _abufferImplementation(-1) + , _abufferImplementation(ABufferImplementation::Invalid) , _log(nullptr) , _showInfo(true) , _showScreenLog(true) @@ -153,33 +146,29 @@ bool RenderEngine::initialize() { } } - auto it = RenderingMethods.find(renderingMethod); - if (it == RenderingMethods.end()) { + _abufferImplementation = aBufferFromString(renderingMethod); + switch (_abufferImplementation) { + case ABufferImplementation::FrameBuffer: + LINFO("Creating ABufferFramebuffer implementation"); + _abuffer = new ABufferFramebuffer; + break; + case ABufferImplementation::SingleLinked: + LINFO("Creating ABufferSingleLinked implementation"); + _abuffer = new ABufferSingleLinked(); + break; + case ABufferImplementation::Fixed: + LINFO("Creating ABufferFixed implementation"); + _abuffer = new ABufferFixed(); + break; + case ABufferImplementation::Dynamic: + LINFO("Creating ABufferDynamic implementation"); + _abuffer = new ABufferDynamic(); + break; + case ABufferImplementation::Invalid: LFATAL("Rendering method '" << renderingMethod << "' not among the available " << "rendering methods"); return false; } - else { - _abufferImplementation = it->second; - switch (_abufferImplementation) { - case ABUFFER_FRAMEBUFFER: - LINFO("Creating ABufferFramebuffer implementation"); - _abuffer = new ABufferFramebuffer; - break; - case ABUFFER_SINGLE_LINKED: - LINFO("Creating ABufferSingleLinked implementation"); - _abuffer = new ABufferSingleLinked(); - break; - case ABUFFER_FIXED: - LINFO("Creating ABufferFixed implementation"); - _abuffer = new ABufferFixed(); - break; - case ABUFFER_DYNAMIC: - LINFO("Creating ABufferDynamic implementation"); - _abuffer = new ABufferDynamic(); - break; - } - } generateGlslConfig(); @@ -363,18 +352,20 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi _abuffer->clear(); // SGCT resets certain settings -#ifndef __APPLE__ - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_BLEND); -#else - glEnable(GL_DEPTH_TEST); -// glDisable(GL_CULL_FACE); - glEnable(GL_CULL_FACE); -// glDisable(GL_BLEND); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -#endif + + if (_abufferImplementation == ABufferImplementation::FrameBuffer) { + glEnable(GL_DEPTH_TEST); + // glDisable(GL_CULL_FACE); + glEnable(GL_CULL_FACE); + // glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else { + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + } // setup the camera for the current frame _mainCamera->setViewMatrix( @@ -398,7 +389,7 @@ void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &vi glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _abuffer->resolve(); + _abuffer->resolve(_globalBlackOutFactor); glDisable(GL_BLEND); } else { @@ -692,10 +683,14 @@ Camera* RenderEngine::camera() const { return _mainCamera; } -ABuffer* RenderEngine::abuffer() const { +ABuffer* RenderEngine::aBuffer() const { return _abuffer; } +RenderEngine::ABufferImplementation RenderEngine::aBufferImplementation() const { + return _abufferImplementation; +} + float RenderEngine::globalBlackOutFactor() { return _globalBlackOutFactor; } @@ -729,7 +724,7 @@ void RenderEngine::generateGlslConfig() { << "#define ABUFFER_SINGLE_LINKED " << ABUFFER_SINGLE_LINKED << "\n" << "#define ABUFFER_FIXED " << ABUFFER_FIXED << "\n" << "#define ABUFFER_DYNAMIC " << ABUFFER_DYNAMIC << "\n" - << "#define ABUFFER_IMPLEMENTATION " << _abufferImplementation << "\n"; + << "#define ABUFFER_IMPLEMENTATION " << int(_abufferImplementation) << "\n"; // System specific #ifdef WIN32 os << "#define WIN32\n"; @@ -1282,4 +1277,18 @@ void RenderEngine::setDisableRenderingOnMaster(bool enabled) { _disableMasterRendering = enabled; } +RenderEngine::ABufferImplementation RenderEngine::aBufferFromString(const std::string& impl) { + const std::map RenderingMethods = { + { "ABufferFrameBuffer", ABufferImplementation::FrameBuffer }, + { "ABufferSingleLinked", ABufferImplementation::SingleLinked }, + { "ABufferFixed", ABufferImplementation::Fixed }, + { "ABufferDynamic", ABufferImplementation::Dynamic } + }; + + if (RenderingMethods.find(impl) != RenderingMethods.end()) + return RenderingMethods.at(impl); + else + return ABufferImplementation::Invalid; +} + }// namespace openspace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 8e6b0c58bc..27db64793a 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -149,9 +149,7 @@ void Scene::update(const UpdateData& data) { _sceneGraphToLoad = ""; if (!success) return; -#ifndef __APPLE__ - OsEng.renderEngine()->abuffer()->invalidateABuffer(); -#endif + OsEng.renderEngine()->aBuffer()->invalidateABuffer(); } for (SceneGraphNode* node : _graph.nodes()) node->update(data); diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp index a26ec95e0c..33a9bf607d 100644 --- a/src/scene/scenegraph.cpp +++ b/src/scene/scenegraph.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -335,31 +336,31 @@ bool SceneGraph::sortTopologically() { } } - -#ifdef __APPLE__ - auto it = std::find_if( - _topologicalSortedNodes.begin(), - _topologicalSortedNodes.end(), - [](SceneGraphNode* node) { - return node->name() == "Stars"; - } - ); - SceneGraphNode* n = *it; - _topologicalSortedNodes.erase(it); - _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n); - - it = std::find_if( - _topologicalSortedNodes.begin(), - _topologicalSortedNodes.end(), - [](SceneGraphNode* node) { - return node->name() == "MilkyWay"; - } - ); - n = *it; - _topologicalSortedNodes.erase(it); - _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n); -#endif - + + RenderEngine::ABufferImplementation i = OsEng.renderEngine()->aBufferImplementation(); + if (i == RenderEngine::ABufferImplementation::FrameBuffer) { + auto it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "Stars"; + } + ); + SceneGraphNode* n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n); + + it = std::find_if( + _topologicalSortedNodes.begin(), + _topologicalSortedNodes.end(), + [](SceneGraphNode* node) { + return node->name() == "MilkyWay"; + } + ); + n = *it; + _topologicalSortedNodes.erase(it); + _topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n); + } return true; diff --git a/src/util/openspacemodule.cpp b/src/util/openspacemodule.cpp index dc944f3fe3..35a3dee439 100644 --- a/src/util/openspacemodule.cpp +++ b/src/util/openspacemodule.cpp @@ -37,8 +37,13 @@ namespace { //ghoul::filesystem::FileSystem::TokenClosingBraces namespace openspace { -bool OpenSpaceModule::initialize() { - ghoul_assert(!(name().empty()), "Module name must be set before initialize call"); +OpenSpaceModule::OpenSpaceModule(std::string name) + : _name(std::move(name)) +{ + ghoul_assert(!_name.empty(), "Empty module name is not allowed"); +} + +bool OpenSpaceModule::create() { std::string moduleNameUpper = name(); std::transform(moduleNameUpper.begin(), moduleNameUpper.end(), moduleNameUpper.begin(), toupper); std::string moduleToken = @@ -53,7 +58,7 @@ bool OpenSpaceModule::initialize() { return true; } -bool OpenSpaceModule::deinitialize() { +bool OpenSpaceModule::destroy() { return true; } @@ -61,10 +66,6 @@ std::string OpenSpaceModule::name() const { return _name; } -void OpenSpaceModule::setName(std::string name) { - _name = std::move(name); -} - std::string OpenSpaceModule::modulePath() const { std::string moduleName = name(); std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), tolower); @@ -79,5 +80,13 @@ std::string OpenSpaceModule::modulePath() const { return ""; } +bool OpenSpaceModule::initialize() { + return true; +} + +bool OpenSpaceModule::deinitialize() { + return true; +} + } // namespace openspace diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 896b249610..fea22903c9 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -42,33 +42,24 @@ namespace openspace { SpiceManager* SpiceManager::_manager = nullptr; -void SpiceManager::initialize() { - assert(_manager == nullptr); - _manager = new SpiceManager; - _manager->_lastAssignedKernel = 0; - - // Set the SPICE library to not exit the program if an error occurs - erract_c("SET", 0, const_cast("REPORT")); - // But we do not want SPICE to print the errors, we will fetch them ourselves - errprt_c("SET", 0, const_cast("NONE")); +SpiceManager::SpiceManager() + : _lastAssignedKernel(0) +{ + // Set the SPICE library to not exit the program if an error occurs + erract_c("SET", 0, const_cast("REPORT")); + // But we do not want SPICE to print the errors, we will fetch them ourselves + errprt_c("SET", 0, const_cast("NONE")); } -void SpiceManager::deinitialize() { - for (const KernelInformation& i : _manager->_loadedKernels) - unload_c(i.path.c_str()); +SpiceManager::~SpiceManager() { + for (const KernelInformation& i : _manager->_loadedKernels) + unload_c(i.path.c_str()); - delete _manager; - _manager = nullptr; - - // Set values back to default - erract_c("SET", 0, const_cast("DEFAULT")); - errprt_c("SET", 0, const_cast("DEFAULT")); + // Set values back to default + erract_c("SET", 0, const_cast("DEFAULT")); + errprt_c("SET", 0, const_cast("DEFAULT")); } -SpiceManager& SpiceManager::ref() { - assert(_manager != nullptr); - return *_manager; -} SpiceManager::KernelIdentifier SpiceManager::loadKernel(const std::string& filePath) { if (filePath.empty()) { @@ -1068,4 +1059,4 @@ bool SpiceManager::getPlanetEllipsoid(std::string planetName, float &a, float &b return !hasError; } -} \ No newline at end of file +} diff --git a/support/cmake/handle_external_library.cmake b/support/cmake/handle_external_library.cmake index bd75cce586..27cd7af07f 100644 --- a/support/cmake/handle_external_library.cmake +++ b/support/cmake/handle_external_library.cmake @@ -31,7 +31,7 @@ function (include_external_library target_name library_name path) add_subdirectory(${path}) get_property(INCLUDE_DIR TARGET ${target_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) target_link_libraries(${target_name} ${library_name}) - target_include_directories(${target_name} PUBLIC ${INCLUDE_DIR}) + target_include_directories(${target_name} PUBLIC SYSTEM ${INCLUDE_DIR}) set_property(TARGET ${library_name} PROPERTY FOLDER "External") if (OPENSPACE_DISABLE_EXTERNAL_WARNINGS) if (MSVC) @@ -41,5 +41,4 @@ function (include_external_library target_name library_name path) endif () endif () endif () - endfunction () \ No newline at end of file diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index bf3a1d45c0..1f90d5e24f 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -162,6 +162,22 @@ function (add_external_dependencies) find_package(Spice REQUIRED) target_include_directories(libOpenSpace SYSTEM PUBLIC ${SPICE_INCLUDE_DIRS}) target_link_libraries(libOpenSpace ${SPICE_LIBRARIES}) + + # Curl + if (WIN32) + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl") + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl" PARENT_SCOPE) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_ROOT_DIR}/include) + target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") + else () + find_package(CURL) + if (CURL_FOUND) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_INCLUDE_DIRS}) + target_link_libraries(libOpenSpace ${CURL_LIBRARIES}) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED") + endif () + endif() endfunction () @@ -175,6 +191,7 @@ function (handle_applications) set(DEFAULT_APPLICATIONS "OpenSpace" + "Launcher" ) mark_as_advanced(DEFAULT_APPLICATIONS) @@ -213,6 +230,17 @@ function (handle_applications) target_link_libraries(${APPLICATION_NAME} Ghoul) target_link_libraries(${APPLICATION_NAME} libOpenSpace) + + if (MSVC) + set_target_properties(${APPLICATION_NAME} PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + endif () + + + if (WIN32) + copy_files(${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll") + endif () endif () list(APPEND applications ${APPLICATION_NAME}) @@ -257,14 +285,6 @@ endfunction () -function(handle_option_gui) - if (OPENSPACE_BUILD_GUI_APPLICATIONS) - add_subdirectory(gui) - endif () -endfunction () - - - function (handle_option_tests) if (OPENSPACE_HAVE_TESTS) if (NOT TARGET gtest) @@ -421,6 +441,9 @@ endfunction () function (copy_dynamic_libraries) if (WIN32) + + copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") + # Copy DLLs needed by Ghoul into the executable directory ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul)