From 5a1669cf03224d350f11ecb75113153ee527a417 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Mon, 29 Nov 2010 00:53:58 +0000 Subject: [PATCH] revert --- libtorrent_utp/AUTHORS | 26 - libtorrent_utp/CMakeLists.txt | 302 - libtorrent_utp/COPYING | 28 - libtorrent_utp/ChangeLog | 772 -- libtorrent_utp/Jamfile | 515 - libtorrent_utp/LICENSE | 103 - libtorrent_utp/Makefile.am | 99 - libtorrent_utp/NEWS | 3 - libtorrent_utp/README | 25 - libtorrent_utp/autotool.sh | 176 - libtorrent_utp/bindings/Makefile.am | 4 - libtorrent_utp/bindings/README.txt | 3 - libtorrent_utp/bindings/c/Jamfile | 38 - libtorrent_utp/bindings/c/library.cpp | 606 -- libtorrent_utp/bindings/c/libtorrent.h | 298 - libtorrent_utp/bindings/c/simple_client.c | 123 - libtorrent_utp/bindings/python/Jamfile | 66 - libtorrent_utp/bindings/python/Makefile.am | 45 - libtorrent_utp/bindings/python/client.py | 364 - libtorrent_utp/bindings/python/setup.py.in | 86 - .../bindings/python/simple_client.py | 31 - libtorrent_utp/bindings/python/src/alert.cpp | 400 - .../bindings/python/src/big_number.cpp | 21 - .../bindings/python/src/converters.cpp | 51 - .../bindings/python/src/create_torrent.cpp | 128 - .../bindings/python/src/datetime.cpp | 81 - libtorrent_utp/bindings/python/src/entry.cpp | 140 - .../bindings/python/src/extensions.cpp | 172 - .../bindings/python/src/fingerprint.cpp | 27 - libtorrent_utp/bindings/python/src/gil.hpp | 144 - .../bindings/python/src/ip_filter.cpp | 32 - .../bindings/python/src/magnet_uri.cpp | 59 - libtorrent_utp/bindings/python/src/module.cpp | 55 - .../bindings/python/src/optional.hpp | 31 - .../bindings/python/src/peer_info.cpp | 131 - .../bindings/python/src/peer_plugin.cpp | 359 - .../bindings/python/src/session.cpp | 407 - .../bindings/python/src/session_settings.cpp | 203 - .../bindings/python/src/torrent.cpp | 15 - .../bindings/python/src/torrent_handle.cpp | 381 - .../bindings/python/src/torrent_info.cpp | 221 - .../bindings/python/src/torrent_status.cpp | 112 - .../bindings/python/src/utility.cpp | 37 - .../bindings/python/src/version.cpp | 16 - libtorrent_utp/build_dist.sh | 21 - libtorrent_utp/configure.ac | 759 -- libtorrent_utp/dht_flood.py | 68 - libtorrent_utp/docs/Linkage.png | Bin 37932 -> 0 bytes libtorrent_utp/docs/arctic_thumb.png | Bin 5201 -> 0 bytes libtorrent_utp/docs/bitbuddy_thumb.jpg | Bin 2766 -> 0 bytes libtorrent_utp/docs/bitfox.png | Bin 4667 -> 0 bytes libtorrent_utp/docs/bitrocket_thumb.png | Bin 9566 -> 0 bytes libtorrent_utp/docs/bitscast_thumb.png | Bin 23988 -> 0 bytes libtorrent_utp/docs/bitslug_thumb.png | Bin 26204 -> 0 bytes libtorrent_utp/docs/btg_thumb.jpg | Bin 42653 -> 0 bytes libtorrent_utp/docs/bubba.png | Bin 29631 -> 0 bytes libtorrent_utp/docs/building.html | 746 -- libtorrent_utp/docs/building.rst | 645 -- libtorrent_utp/docs/client_test.html | 86 - libtorrent_utp/docs/client_test.png | Bin 110655 -> 0 bytes libtorrent_utp/docs/client_test.rst | 50 - libtorrent_utp/docs/contributing.html | 103 - libtorrent_utp/docs/contributing.rst | 45 - libtorrent_utp/docs/cwnd.png | Bin 18998 -> 0 bytes libtorrent_utp/docs/cwnd_thumb.png | Bin 30207 -> 0 bytes libtorrent_utp/docs/delays.png | Bin 10875 -> 0 bytes libtorrent_utp/docs/delays_thumb.png | Bin 10938 -> 0 bytes libtorrent_utp/docs/deluge.png | Bin 36818 -> 0 bytes libtorrent_utp/docs/dht_extensions.html | 111 - libtorrent_utp/docs/dht_extensions.rst | 55 - libtorrent_utp/docs/disk_access.png | Bin 3032 -> 0 bytes libtorrent_utp/docs/disk_access_elevator.png | Bin 5768 -> 0 bytes .../docs/disk_access_no_elevator.png | Bin 5309 -> 0 bytes libtorrent_utp/docs/disk_buffer.png | Bin 5170 -> 0 bytes .../docs/disk_buffer_before_optimization.png | Bin 4846 -> 0 bytes libtorrent_utp/docs/disk_buffer_sample.png | Bin 5340 -> 0 bytes libtorrent_utp/docs/disk_io.png | Bin 4393 -> 0 bytes libtorrent_utp/docs/electric_sheep_thumb.jpg | Bin 6882 -> 0 bytes libtorrent_utp/docs/examples.html | 313 - libtorrent_utp/docs/examples.rst | 260 - libtorrent_utp/docs/extension_protocol.html | 301 - libtorrent_utp/docs/extension_protocol.rst | 202 - libtorrent_utp/docs/fatrat.png | Bin 45067 -> 0 bytes libtorrent_utp/docs/fdm.png | Bin 52045 -> 0 bytes libtorrent_utp/docs/features.html | 401 - libtorrent_utp/docs/features.rst | 405 - libtorrent_utp/docs/firetorrent.png | Bin 26385 -> 0 bytes libtorrent_utp/docs/flush.jpg | Bin 9581 -> 0 bytes libtorrent_utp/docs/folx.png | Bin 14818 -> 0 bytes libtorrent_utp/docs/halite_thumb.png | Bin 20926 -> 0 bytes libtorrent_utp/docs/im_thumb.jpg | Bin 12093 -> 0 bytes libtorrent_utp/docs/index.html | 138 - libtorrent_utp/docs/index.rst | 169 - libtorrent_utp/docs/leechcraft.png | Bin 28376 -> 0 bytes libtorrent_utp/docs/libtorrent_plugins.html | 273 - libtorrent_utp/docs/libtorrent_plugins.rst | 240 - libtorrent_utp/docs/libtorrent_screen.png | Bin 53067 -> 0 bytes libtorrent_utp/docs/limewire.png | Bin 20970 -> 0 bytes libtorrent_utp/docs/lince.png | Bin 15486 -> 0 bytes libtorrent_utp/docs/make_thumb.sh | 2 - libtorrent_utp/docs/make_torrent.html | 504 - libtorrent_utp/docs/make_torrent.rst | 498 - libtorrent_utp/docs/makefile | 44 - libtorrent_utp/docs/manual.html | 7862 -------------- libtorrent_utp/docs/manual.rst | 7898 -------------- libtorrent_utp/docs/merkle_tree.graffle | 1718 --- libtorrent_utp/docs/merkle_tree.png | Bin 18792 -> 0 bytes libtorrent_utp/docs/miro.jpg | Bin 8299 -> 0 bytes libtorrent_utp/docs/moopolice_thumb.gif | Bin 8443 -> 0 bytes libtorrent_utp/docs/our_delay_base.png | Bin 34999 -> 0 bytes libtorrent_utp/docs/our_delay_base_thumb.png | Bin 55460 -> 0 bytes libtorrent_utp/docs/projects.html | 257 - libtorrent_utp/docs/projects.rst | 463 - libtorrent_utp/docs/pump.png | Bin 68026 -> 0 bytes libtorrent_utp/docs/python_binding.html | 171 - libtorrent_utp/docs/python_binding.rst | 129 - libtorrent_utp/docs/qbittorrent_thumb.jpg | Bin 5009 -> 0 bytes libtorrent_utp/docs/read_disk_buffers.dot | 20 - libtorrent_utp/docs/read_disk_buffers.graffle | 2834 ----- libtorrent_utp/docs/read_disk_buffers.png | Bin 50638 -> 0 bytes libtorrent_utp/docs/running_tests.html | 114 - libtorrent_utp/docs/running_tests.rst | 75 - libtorrent_utp/docs/session_stats_peers.png | Bin 3902 -> 0 bytes libtorrent_utp/docs/storage.graffle | 1726 --- libtorrent_utp/docs/storage.png | Bin 30224 -> 0 bytes libtorrent_utp/docs/style.css | 297 - libtorrent_utp/docs/stylesheet | 287 - libtorrent_utp/docs/t2e.jpg | Bin 2368 -> 0 bytes libtorrent_utp/docs/template.txt | 39 - libtorrent_utp/docs/tonidoplug.png | Bin 18260 -> 0 bytes libtorrent_utp/docs/tuning.html | 560 - libtorrent_utp/docs/tuning.rst | 524 - libtorrent_utp/docs/tvblob.jpg | Bin 8686 -> 0 bytes libtorrent_utp/docs/tvitty.jpg | Bin 10746 -> 0 bytes libtorrent_utp/docs/udp_tracker_protocol.html | 529 - libtorrent_utp/docs/udp_tracker_protocol.rst | 289 - libtorrent_utp/docs/unicode_support.png | Bin 49602 -> 0 bytes libtorrent_utp/docs/utp.html | 342 - libtorrent_utp/docs/utp.rst | 347 - libtorrent_utp/docs/write_disk_buffers.dot | 20 - .../docs/write_disk_buffers.graffle | 2745 ----- libtorrent_utp/docs/write_disk_buffers.png | Bin 50619 -> 0 bytes libtorrent_utp/docs/ziptorrent_thumb.gif | Bin 6827 -> 0 bytes libtorrent_utp/docs/zyxel.png | Bin 14612 -> 0 bytes libtorrent_utp/examples/Jamfile | 28 - libtorrent_utp/examples/Makefile.am | 37 - libtorrent_utp/examples/client_test.cpp | 1839 ---- libtorrent_utp/examples/connection_tester.cpp | 261 - libtorrent_utp/examples/dump_torrent.cpp | 152 - libtorrent_utp/examples/enum_if.cpp | 102 - .../examples/fragmentation_test.cpp | 142 - libtorrent_utp/examples/make_torrent.cpp | 217 - libtorrent_utp/examples/simple_client.cpp | 72 - libtorrent_utp/examples/upnp_test.cpp | 93 - libtorrent_utp/examples/utp_test.cpp | 48 - .../include/libtorrent/ConvertUTF.h | 164 - libtorrent_utp/include/libtorrent/GeoIP.h | 180 - libtorrent_utp/include/libtorrent/Makefile.am | 135 - .../include/libtorrent/add_torrent_params.hpp | 92 - libtorrent_utp/include/libtorrent/address.hpp | 77 - libtorrent_utp/include/libtorrent/alert.hpp | 234 - .../include/libtorrent/alert_types.hpp | 1190 --- libtorrent_utp/include/libtorrent/alloca.hpp | 52 - .../include/libtorrent/allocator.hpp | 56 - libtorrent_utp/include/libtorrent/assert.hpp | 75 - .../include/libtorrent/aux_/session_impl.hpp | 924 -- .../include/libtorrent/bandwidth_limit.hpp | 88 - .../include/libtorrent/bandwidth_manager.hpp | 112 - .../libtorrent/bandwidth_queue_entry.hpp | 71 - .../include/libtorrent/bandwidth_socket.hpp | 51 - libtorrent_utp/include/libtorrent/bencode.hpp | 417 - .../include/libtorrent/bitfield.hpp | 269 - .../include/libtorrent/broadcast_socket.hpp | 131 - .../include/libtorrent/bt_peer_connection.hpp | 466 - libtorrent_utp/include/libtorrent/buffer.hpp | 208 - .../include/libtorrent/build_config.hpp | 129 - .../include/libtorrent/chained_buffer.hpp | 200 - libtorrent_utp/include/libtorrent/config.hpp | 356 - .../include/libtorrent/connection_queue.hpp | 120 - .../include/libtorrent/copy_ptr.hpp | 69 - .../include/libtorrent/create_torrent.hpp | 413 - .../include/libtorrent/deadline_timer.hpp | 103 - libtorrent_utp/include/libtorrent/debug.hpp | 173 - .../include/libtorrent/disk_buffer_holder.hpp | 74 - .../include/libtorrent/disk_io_thread.hpp | 473 - libtorrent_utp/include/libtorrent/entry.hpp | 228 - .../include/libtorrent/enum_net.hpp | 83 - libtorrent_utp/include/libtorrent/error.hpp | 62 - .../include/libtorrent/error_code.hpp | 358 - .../include/libtorrent/escape_string.hpp | 103 - .../include/libtorrent/extensions.hpp | 196 - .../include/libtorrent/extensions/logger.hpp | 60 - .../libtorrent/extensions/lt_trackers.hpp | 55 - .../extensions/metadata_transfer.hpp | 55 - .../libtorrent/extensions/smart_ban.hpp | 55 - .../libtorrent/extensions/ut_metadata.hpp | 55 - .../include/libtorrent/extensions/ut_pex.hpp | 54 - libtorrent_utp/include/libtorrent/file.hpp | 287 - .../include/libtorrent/file_pool.hpp | 94 - .../include/libtorrent/file_storage.hpp | 253 - .../include/libtorrent/fingerprint.hpp | 97 - libtorrent_utp/include/libtorrent/gzip.hpp | 52 - libtorrent_utp/include/libtorrent/hasher.hpp | 156 - .../include/libtorrent/http_connection.hpp | 235 - .../include/libtorrent/http_parser.hpp | 144 - .../libtorrent/http_seed_connection.hpp | 140 - .../include/libtorrent/http_stream.hpp | 123 - .../libtorrent/http_tracker_connection.hpp | 117 - .../include/libtorrent/i2p_stream.hpp | 223 - .../include/libtorrent/identify_client.hpp | 58 - .../libtorrent/instantiate_connection.hpp | 51 - .../include/libtorrent/intrusive_ptr_base.hpp | 85 - .../include/libtorrent/invariant_check.hpp | 78 - libtorrent_utp/include/libtorrent/io.hpp | 155 - .../include/libtorrent/io_service.hpp | 77 - .../include/libtorrent/io_service_fwd.hpp | 77 - .../include/libtorrent/ip_filter.hpp | 314 - .../libtorrent/kademlia/dht_tracker.hpp | 173 - .../include/libtorrent/kademlia/find_data.hpp | 109 - .../include/libtorrent/kademlia/logging.hpp | 153 - .../include/libtorrent/kademlia/msg.hpp | 114 - .../include/libtorrent/kademlia/node.hpp | 309 - .../libtorrent/kademlia/node_entry.hpp | 99 - .../include/libtorrent/kademlia/node_id.hpp | 63 - .../include/libtorrent/kademlia/observer.hpp | 182 - .../include/libtorrent/kademlia/refresh.hpp | 65 - .../libtorrent/kademlia/routing_table.hpp | 198 - .../libtorrent/kademlia/rpc_manager.hpp | 125 - .../kademlia/traversal_algorithm.hpp | 136 - .../include/libtorrent/lazy_entry.hpp | 286 - libtorrent_utp/include/libtorrent/lsd.hpp | 99 - .../include/libtorrent/magnet_uri.hpp | 69 - libtorrent_utp/include/libtorrent/max.hpp | 111 - libtorrent_utp/include/libtorrent/natpmp.hpp | 173 - .../include/libtorrent/packet_buffer.hpp | 108 - .../include/libtorrent/parse_url.hpp | 60 - libtorrent_utp/include/libtorrent/pch.hpp | 118 - .../include/libtorrent/pe_crypto.hpp | 167 - libtorrent_utp/include/libtorrent/peer.hpp | 63 - .../include/libtorrent/peer_connection.hpp | 1210 --- libtorrent_utp/include/libtorrent/peer_id.hpp | 217 - .../include/libtorrent/peer_info.hpp | 242 - .../include/libtorrent/peer_request.hpp | 49 - .../libtorrent/piece_block_progress.hpp | 57 - .../include/libtorrent/piece_picker.hpp | 584 - libtorrent_utp/include/libtorrent/policy.hpp | 572 - .../include/libtorrent/proxy_base.hpp | 235 - libtorrent_utp/include/libtorrent/ptime.hpp | 136 - libtorrent_utp/include/libtorrent/puff.hpp | 33 - libtorrent_utp/include/libtorrent/session.hpp | 445 - .../include/libtorrent/session_settings.hpp | 1072 -- .../include/libtorrent/session_status.hpp | 128 - .../include/libtorrent/settings.hpp | 62 - .../include/libtorrent/size_type.hpp | 53 - .../include/libtorrent/sliding_average.hpp | 77 - libtorrent_utp/include/libtorrent/socket.hpp | 186 - .../include/libtorrent/socket_io.hpp | 143 - .../include/libtorrent/socket_type.hpp | 288 - .../include/libtorrent/socket_type_fwd.hpp | 42 - .../include/libtorrent/socks5_stream.hpp | 179 - .../include/libtorrent/ssl_stream.hpp | 253 - libtorrent_utp/include/libtorrent/stat.hpp | 356 - libtorrent_utp/include/libtorrent/storage.hpp | 459 - .../include/libtorrent/storage_defs.hpp | 67 - libtorrent_utp/include/libtorrent/thread.hpp | 75 - libtorrent_utp/include/libtorrent/time.hpp | 136 - .../include/libtorrent/timestamp_history.hpp | 80 - libtorrent_utp/include/libtorrent/tommath.h | 584 - .../include/libtorrent/tommath_class.h | 1000 -- .../include/libtorrent/tommath_superclass.h | 84 - libtorrent_utp/include/libtorrent/torrent.hpp | 1250 --- .../include/libtorrent/torrent_handle.hpp | 684 -- .../include/libtorrent/torrent_info.hpp | 487 - .../include/libtorrent/tracker_manager.hpp | 281 - .../include/libtorrent/udp_socket.hpp | 229 - .../libtorrent/udp_tracker_connection.hpp | 143 - .../include/libtorrent/union_endpoint.hpp | 94 - libtorrent_utp/include/libtorrent/upnp.hpp | 322 - libtorrent_utp/include/libtorrent/utf8.hpp | 111 - .../include/libtorrent/utp_socket_manager.hpp | 116 - .../include/libtorrent/utp_stream.hpp | 386 - libtorrent_utp/include/libtorrent/version.hpp | 47 - .../libtorrent/web_connection_base.hpp | 171 - .../libtorrent/web_peer_connection.hpp | 152 - .../include/libtorrent/xml_parse.hpp | 212 - .../libtorrent-rasterbar-cmake.pc.in | 6 - libtorrent_utp/libtorrent-rasterbar.pc.in | 16 - libtorrent_utp/list_files.py | 22 - libtorrent_utp/m4/ax_boost_base.m4 | 224 - libtorrent_utp/m4/ax_boost_python.m4 | 103 - libtorrent_utp/m4/ax_boost_system.m4 | 114 - libtorrent_utp/m4/ax_check_geoip.m4 | 95 - libtorrent_utp/m4/ax_check_openssl.m4 | 124 - libtorrent_utp/m4/ax_pthread.m4 | 383 - libtorrent_utp/m4/ax_python_devel.m4 | 323 - libtorrent_utp/m4/pkgconfig.m4 | 155 - libtorrent_utp/parse_bandwidth_log.py | 24 - libtorrent_utp/parse_buffer_log.py | 90 - libtorrent_utp/parse_dht_log.py | 78 - libtorrent_utp/parse_dht_rtt.py | 51 - libtorrent_utp/parse_dht_stats.py | 53 - libtorrent_utp/parse_disk_access.py | 102 - libtorrent_utp/parse_disk_buffer_log.py | 91 - libtorrent_utp/parse_disk_log.py | 121 - libtorrent_utp/parse_memory_log.py | 126 - libtorrent_utp/parse_sample.py | 101 - libtorrent_utp/parse_session_stats.py | 47 - libtorrent_utp/project-root.jam | 0 libtorrent_utp/set_version.py | 46 - libtorrent_utp/src/ConvertUTF.cpp | 539 - libtorrent_utp/src/GeoIP.c | 1077 -- libtorrent_utp/src/Makefile.am | 107 - libtorrent_utp/src/alert.cpp | 495 - libtorrent_utp/src/allocator.cpp | 152 - libtorrent_utp/src/assert.cpp | 165 - libtorrent_utp/src/bandwidth_limit.cpp | 92 - libtorrent_utp/src/bandwidth_manager.cpp | 223 - libtorrent_utp/src/bandwidth_queue_entry.cpp | 73 - libtorrent_utp/src/broadcast_socket.cpp | 377 - libtorrent_utp/src/bt_peer_connection.cpp | 3431 ------ libtorrent_utp/src/connection_queue.cpp | 331 - libtorrent_utp/src/create_torrent.cpp | 491 - libtorrent_utp/src/disk_buffer_holder.cpp | 85 - libtorrent_utp/src/disk_io_thread.cpp | 2421 ----- libtorrent_utp/src/entry.cpp | 560 - libtorrent_utp/src/enum_net.cpp | 953 -- libtorrent_utp/src/error_code.cpp | 264 - libtorrent_utp/src/escape_string.cpp | 685 -- libtorrent_utp/src/file.cpp | 1688 --- libtorrent_utp/src/file_pool.cpp | 179 - libtorrent_utp/src/file_storage.cpp | 509 - libtorrent_utp/src/gzip.cpp | 174 - libtorrent_utp/src/http_connection.cpp | 820 -- libtorrent_utp/src/http_parser.cpp | 370 - libtorrent_utp/src/http_seed_connection.cpp | 459 - libtorrent_utp/src/http_stream.cpp | 177 - .../src/http_tracker_connection.cpp | 523 - libtorrent_utp/src/i2p_stream.cpp | 431 - libtorrent_utp/src/identify_client.cpp | 417 - libtorrent_utp/src/instantiate_connection.cpp | 124 - libtorrent_utp/src/ip_filter.cpp | 100 - libtorrent_utp/src/kademlia/dht_tracker.cpp | 665 -- libtorrent_utp/src/kademlia/find_data.cpp | 254 - libtorrent_utp/src/kademlia/node.cpp | 939 -- libtorrent_utp/src/kademlia/node_id.cpp | 113 - libtorrent_utp/src/kademlia/refresh.cpp | 83 - libtorrent_utp/src/kademlia/routing_table.cpp | 641 -- libtorrent_utp/src/kademlia/rpc_manager.cpp | 478 - .../src/kademlia/traversal_algorithm.cpp | 304 - libtorrent_utp/src/lazy_bdecode.cpp | 548 - libtorrent_utp/src/logger.cpp | 240 - libtorrent_utp/src/lsd.cpp | 232 - libtorrent_utp/src/lt_trackers.cpp | 351 - libtorrent_utp/src/magnet_uri.cpp | 210 - libtorrent_utp/src/metadata_transfer.cpp | 591 - libtorrent_utp/src/mpi.c | 9514 ----------------- libtorrent_utp/src/natpmp.cpp | 643 -- libtorrent_utp/src/packet_buffer.cpp | 189 - libtorrent_utp/src/parse_url.cpp | 131 - libtorrent_utp/src/pe_crypto.cpp | 371 - libtorrent_utp/src/peer_connection.cpp | 5603 ---------- libtorrent_utp/src/piece_picker.cpp | 2397 ----- libtorrent_utp/src/policy.cpp | 1624 --- libtorrent_utp/src/puff.cpp | 842 -- libtorrent_utp/src/session.cpp | 945 -- libtorrent_utp/src/session_impl.cpp | 4465 -------- libtorrent_utp/src/settings.cpp | 124 - libtorrent_utp/src/sha1.cpp | 321 - libtorrent_utp/src/smart_ban.cpp | 316 - libtorrent_utp/src/socket_io.cpp | 80 - libtorrent_utp/src/socket_type.cpp | 183 - libtorrent_utp/src/socks5_stream.cpp | 517 - libtorrent_utp/src/stat.cpp | 51 - libtorrent_utp/src/storage.cpp | 3072 ------ libtorrent_utp/src/thread.cpp | 109 - libtorrent_utp/src/time.cpp | 223 - libtorrent_utp/src/timestamp_history.cpp | 105 - libtorrent_utp/src/torrent.cpp | 7056 ------------ libtorrent_utp/src/torrent_handle.cpp | 864 -- libtorrent_utp/src/torrent_info.cpp | 1231 --- libtorrent_utp/src/tracker_manager.cpp | 349 - libtorrent_utp/src/udp_socket.cpp | 1025 -- libtorrent_utp/src/udp_tracker_connection.cpp | 670 -- libtorrent_utp/src/upnp.cpp | 1357 --- libtorrent_utp/src/ut_metadata.cpp | 447 - libtorrent_utp/src/ut_pex.cpp | 571 - libtorrent_utp/src/utp_socket_manager.cpp | 324 - libtorrent_utp/src/utp_stream.cpp | 2900 ----- libtorrent_utp/src/web_connection_base.cpp | 202 - libtorrent_utp/src/web_peer_connection.cpp | 800 -- libtorrent_utp/test/Jamfile | 49 - libtorrent_utp/test/Makefile.am | 71 - libtorrent_utp/test/main.cpp | 62 - libtorrent_utp/test/setup_transfer.cpp | 961 -- libtorrent_utp/test/setup_transfer.hpp | 74 - libtorrent_utp/test/test.hpp | 116 - libtorrent_utp/test/test_auto_unchoke.cpp | 121 - .../test/test_bandwidth_limiter.cpp | 486 - .../test/test_bdecode_performance.cpp | 28 - libtorrent_utp/test/test_bencoding.cpp | 214 - libtorrent_utp/test/test_buffer.cpp | 323 - libtorrent_utp/test/test_dht.cpp | 228 - libtorrent_utp/test/test_fast_extension.cpp | 294 - libtorrent_utp/test/test_hasher.cpp | 80 - libtorrent_utp/test/test_http_connection.cpp | 221 - libtorrent_utp/test/test_ip_filter.cpp | 257 - libtorrent_utp/test/test_lsd.cpp | 107 - .../test/test_metadata_extension.cpp | 135 - libtorrent_utp/test/test_natpmp.cpp | 67 - libtorrent_utp/test/test_pe_crypto.cpp | 208 - libtorrent_utp/test/test_pex.cpp | 159 - libtorrent_utp/test/test_piece_picker.cpp | 1087 -- libtorrent_utp/test/test_primitives.cpp | 1407 --- libtorrent_utp/test/test_storage.cpp | 1058 -- libtorrent_utp/test/test_swarm.cpp | 228 - libtorrent_utp/test/test_threads.cpp | 73 - libtorrent_utp/test/test_torrent.cpp | 181 - .../test/test_trackers_extension.cpp | 78 - libtorrent_utp/test/test_transfer.cpp | 575 - libtorrent_utp/test/test_upnp.cpp | 267 - libtorrent_utp/test/test_utp.cpp | 151 - libtorrent_utp/test/test_web_seed.cpp | 298 - 422 files changed, 159584 deletions(-) delete mode 100644 libtorrent_utp/AUTHORS delete mode 100644 libtorrent_utp/CMakeLists.txt delete mode 100644 libtorrent_utp/COPYING delete mode 100644 libtorrent_utp/ChangeLog delete mode 100755 libtorrent_utp/Jamfile delete mode 100644 libtorrent_utp/LICENSE delete mode 100644 libtorrent_utp/Makefile.am delete mode 100644 libtorrent_utp/NEWS delete mode 100644 libtorrent_utp/README delete mode 100755 libtorrent_utp/autotool.sh delete mode 100644 libtorrent_utp/bindings/Makefile.am delete mode 100644 libtorrent_utp/bindings/README.txt delete mode 100755 libtorrent_utp/bindings/c/Jamfile delete mode 100644 libtorrent_utp/bindings/c/library.cpp delete mode 100644 libtorrent_utp/bindings/c/libtorrent.h delete mode 100644 libtorrent_utp/bindings/c/simple_client.c delete mode 100755 libtorrent_utp/bindings/python/Jamfile delete mode 100644 libtorrent_utp/bindings/python/Makefile.am delete mode 100755 libtorrent_utp/bindings/python/client.py delete mode 100644 libtorrent_utp/bindings/python/setup.py.in delete mode 100755 libtorrent_utp/bindings/python/simple_client.py delete mode 100644 libtorrent_utp/bindings/python/src/alert.cpp delete mode 100644 libtorrent_utp/bindings/python/src/big_number.cpp delete mode 100644 libtorrent_utp/bindings/python/src/converters.cpp delete mode 100644 libtorrent_utp/bindings/python/src/create_torrent.cpp delete mode 100644 libtorrent_utp/bindings/python/src/datetime.cpp delete mode 100644 libtorrent_utp/bindings/python/src/entry.cpp delete mode 100644 libtorrent_utp/bindings/python/src/extensions.cpp delete mode 100644 libtorrent_utp/bindings/python/src/fingerprint.cpp delete mode 100644 libtorrent_utp/bindings/python/src/gil.hpp delete mode 100644 libtorrent_utp/bindings/python/src/ip_filter.cpp delete mode 100644 libtorrent_utp/bindings/python/src/magnet_uri.cpp delete mode 100644 libtorrent_utp/bindings/python/src/module.cpp delete mode 100644 libtorrent_utp/bindings/python/src/optional.hpp delete mode 100644 libtorrent_utp/bindings/python/src/peer_info.cpp delete mode 100644 libtorrent_utp/bindings/python/src/peer_plugin.cpp delete mode 100644 libtorrent_utp/bindings/python/src/session.cpp delete mode 100644 libtorrent_utp/bindings/python/src/session_settings.cpp delete mode 100644 libtorrent_utp/bindings/python/src/torrent.cpp delete mode 100644 libtorrent_utp/bindings/python/src/torrent_handle.cpp delete mode 100644 libtorrent_utp/bindings/python/src/torrent_info.cpp delete mode 100644 libtorrent_utp/bindings/python/src/torrent_status.cpp delete mode 100644 libtorrent_utp/bindings/python/src/utility.cpp delete mode 100644 libtorrent_utp/bindings/python/src/version.cpp delete mode 100755 libtorrent_utp/build_dist.sh delete mode 100644 libtorrent_utp/configure.ac delete mode 100755 libtorrent_utp/dht_flood.py delete mode 100644 libtorrent_utp/docs/Linkage.png delete mode 100644 libtorrent_utp/docs/arctic_thumb.png delete mode 100644 libtorrent_utp/docs/bitbuddy_thumb.jpg delete mode 100644 libtorrent_utp/docs/bitfox.png delete mode 100644 libtorrent_utp/docs/bitrocket_thumb.png delete mode 100644 libtorrent_utp/docs/bitscast_thumb.png delete mode 100644 libtorrent_utp/docs/bitslug_thumb.png delete mode 100644 libtorrent_utp/docs/btg_thumb.jpg delete mode 100644 libtorrent_utp/docs/bubba.png delete mode 100644 libtorrent_utp/docs/building.html delete mode 100644 libtorrent_utp/docs/building.rst delete mode 100644 libtorrent_utp/docs/client_test.html delete mode 100644 libtorrent_utp/docs/client_test.png delete mode 100644 libtorrent_utp/docs/client_test.rst delete mode 100644 libtorrent_utp/docs/contributing.html delete mode 100644 libtorrent_utp/docs/contributing.rst delete mode 100644 libtorrent_utp/docs/cwnd.png delete mode 100644 libtorrent_utp/docs/cwnd_thumb.png delete mode 100644 libtorrent_utp/docs/delays.png delete mode 100644 libtorrent_utp/docs/delays_thumb.png delete mode 100644 libtorrent_utp/docs/deluge.png delete mode 100644 libtorrent_utp/docs/dht_extensions.html delete mode 100644 libtorrent_utp/docs/dht_extensions.rst delete mode 100644 libtorrent_utp/docs/disk_access.png delete mode 100644 libtorrent_utp/docs/disk_access_elevator.png delete mode 100644 libtorrent_utp/docs/disk_access_no_elevator.png delete mode 100644 libtorrent_utp/docs/disk_buffer.png delete mode 100644 libtorrent_utp/docs/disk_buffer_before_optimization.png delete mode 100644 libtorrent_utp/docs/disk_buffer_sample.png delete mode 100644 libtorrent_utp/docs/disk_io.png delete mode 100644 libtorrent_utp/docs/electric_sheep_thumb.jpg delete mode 100644 libtorrent_utp/docs/examples.html delete mode 100644 libtorrent_utp/docs/examples.rst delete mode 100644 libtorrent_utp/docs/extension_protocol.html delete mode 100644 libtorrent_utp/docs/extension_protocol.rst delete mode 100644 libtorrent_utp/docs/fatrat.png delete mode 100644 libtorrent_utp/docs/fdm.png delete mode 100644 libtorrent_utp/docs/features.html delete mode 100644 libtorrent_utp/docs/features.rst delete mode 100644 libtorrent_utp/docs/firetorrent.png delete mode 100644 libtorrent_utp/docs/flush.jpg delete mode 100644 libtorrent_utp/docs/folx.png delete mode 100644 libtorrent_utp/docs/halite_thumb.png delete mode 100644 libtorrent_utp/docs/im_thumb.jpg delete mode 100644 libtorrent_utp/docs/index.html delete mode 100644 libtorrent_utp/docs/index.rst delete mode 100644 libtorrent_utp/docs/leechcraft.png delete mode 100644 libtorrent_utp/docs/libtorrent_plugins.html delete mode 100644 libtorrent_utp/docs/libtorrent_plugins.rst delete mode 100644 libtorrent_utp/docs/libtorrent_screen.png delete mode 100644 libtorrent_utp/docs/limewire.png delete mode 100644 libtorrent_utp/docs/lince.png delete mode 100755 libtorrent_utp/docs/make_thumb.sh delete mode 100644 libtorrent_utp/docs/make_torrent.html delete mode 100644 libtorrent_utp/docs/make_torrent.rst delete mode 100644 libtorrent_utp/docs/makefile delete mode 100644 libtorrent_utp/docs/manual.html delete mode 100644 libtorrent_utp/docs/manual.rst delete mode 100644 libtorrent_utp/docs/merkle_tree.graffle delete mode 100644 libtorrent_utp/docs/merkle_tree.png delete mode 100644 libtorrent_utp/docs/miro.jpg delete mode 100644 libtorrent_utp/docs/moopolice_thumb.gif delete mode 100644 libtorrent_utp/docs/our_delay_base.png delete mode 100644 libtorrent_utp/docs/our_delay_base_thumb.png delete mode 100644 libtorrent_utp/docs/projects.html delete mode 100644 libtorrent_utp/docs/projects.rst delete mode 100644 libtorrent_utp/docs/pump.png delete mode 100644 libtorrent_utp/docs/python_binding.html delete mode 100644 libtorrent_utp/docs/python_binding.rst delete mode 100644 libtorrent_utp/docs/qbittorrent_thumb.jpg delete mode 100644 libtorrent_utp/docs/read_disk_buffers.dot delete mode 100644 libtorrent_utp/docs/read_disk_buffers.graffle delete mode 100644 libtorrent_utp/docs/read_disk_buffers.png delete mode 100644 libtorrent_utp/docs/running_tests.html delete mode 100644 libtorrent_utp/docs/running_tests.rst delete mode 100644 libtorrent_utp/docs/session_stats_peers.png delete mode 100644 libtorrent_utp/docs/storage.graffle delete mode 100644 libtorrent_utp/docs/storage.png delete mode 100644 libtorrent_utp/docs/style.css delete mode 100644 libtorrent_utp/docs/stylesheet delete mode 100644 libtorrent_utp/docs/t2e.jpg delete mode 100644 libtorrent_utp/docs/template.txt delete mode 100644 libtorrent_utp/docs/tonidoplug.png delete mode 100644 libtorrent_utp/docs/tuning.html delete mode 100644 libtorrent_utp/docs/tuning.rst delete mode 100644 libtorrent_utp/docs/tvblob.jpg delete mode 100644 libtorrent_utp/docs/tvitty.jpg delete mode 100644 libtorrent_utp/docs/udp_tracker_protocol.html delete mode 100644 libtorrent_utp/docs/udp_tracker_protocol.rst delete mode 100644 libtorrent_utp/docs/unicode_support.png delete mode 100644 libtorrent_utp/docs/utp.html delete mode 100644 libtorrent_utp/docs/utp.rst delete mode 100644 libtorrent_utp/docs/write_disk_buffers.dot delete mode 100644 libtorrent_utp/docs/write_disk_buffers.graffle delete mode 100644 libtorrent_utp/docs/write_disk_buffers.png delete mode 100644 libtorrent_utp/docs/ziptorrent_thumb.gif delete mode 100644 libtorrent_utp/docs/zyxel.png delete mode 100644 libtorrent_utp/examples/Jamfile delete mode 100644 libtorrent_utp/examples/Makefile.am delete mode 100644 libtorrent_utp/examples/client_test.cpp delete mode 100644 libtorrent_utp/examples/connection_tester.cpp delete mode 100644 libtorrent_utp/examples/dump_torrent.cpp delete mode 100644 libtorrent_utp/examples/enum_if.cpp delete mode 100644 libtorrent_utp/examples/fragmentation_test.cpp delete mode 100644 libtorrent_utp/examples/make_torrent.cpp delete mode 100644 libtorrent_utp/examples/simple_client.cpp delete mode 100644 libtorrent_utp/examples/upnp_test.cpp delete mode 100644 libtorrent_utp/examples/utp_test.cpp delete mode 100644 libtorrent_utp/include/libtorrent/ConvertUTF.h delete mode 100644 libtorrent_utp/include/libtorrent/GeoIP.h delete mode 100644 libtorrent_utp/include/libtorrent/Makefile.am delete mode 100644 libtorrent_utp/include/libtorrent/add_torrent_params.hpp delete mode 100644 libtorrent_utp/include/libtorrent/address.hpp delete mode 100644 libtorrent_utp/include/libtorrent/alert.hpp delete mode 100644 libtorrent_utp/include/libtorrent/alert_types.hpp delete mode 100644 libtorrent_utp/include/libtorrent/alloca.hpp delete mode 100644 libtorrent_utp/include/libtorrent/allocator.hpp delete mode 100644 libtorrent_utp/include/libtorrent/assert.hpp delete mode 100644 libtorrent_utp/include/libtorrent/aux_/session_impl.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bandwidth_limit.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bandwidth_manager.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bandwidth_queue_entry.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bandwidth_socket.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bencode.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bitfield.hpp delete mode 100644 libtorrent_utp/include/libtorrent/broadcast_socket.hpp delete mode 100644 libtorrent_utp/include/libtorrent/bt_peer_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/buffer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/build_config.hpp delete mode 100644 libtorrent_utp/include/libtorrent/chained_buffer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/config.hpp delete mode 100644 libtorrent_utp/include/libtorrent/connection_queue.hpp delete mode 100644 libtorrent_utp/include/libtorrent/copy_ptr.hpp delete mode 100644 libtorrent_utp/include/libtorrent/create_torrent.hpp delete mode 100644 libtorrent_utp/include/libtorrent/deadline_timer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/debug.hpp delete mode 100644 libtorrent_utp/include/libtorrent/disk_buffer_holder.hpp delete mode 100644 libtorrent_utp/include/libtorrent/disk_io_thread.hpp delete mode 100644 libtorrent_utp/include/libtorrent/entry.hpp delete mode 100644 libtorrent_utp/include/libtorrent/enum_net.hpp delete mode 100644 libtorrent_utp/include/libtorrent/error.hpp delete mode 100644 libtorrent_utp/include/libtorrent/error_code.hpp delete mode 100644 libtorrent_utp/include/libtorrent/escape_string.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/logger.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/lt_trackers.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/metadata_transfer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/smart_ban.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/ut_metadata.hpp delete mode 100644 libtorrent_utp/include/libtorrent/extensions/ut_pex.hpp delete mode 100644 libtorrent_utp/include/libtorrent/file.hpp delete mode 100644 libtorrent_utp/include/libtorrent/file_pool.hpp delete mode 100644 libtorrent_utp/include/libtorrent/file_storage.hpp delete mode 100644 libtorrent_utp/include/libtorrent/fingerprint.hpp delete mode 100644 libtorrent_utp/include/libtorrent/gzip.hpp delete mode 100644 libtorrent_utp/include/libtorrent/hasher.hpp delete mode 100644 libtorrent_utp/include/libtorrent/http_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/http_parser.hpp delete mode 100644 libtorrent_utp/include/libtorrent/http_seed_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/http_stream.hpp delete mode 100644 libtorrent_utp/include/libtorrent/http_tracker_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/i2p_stream.hpp delete mode 100644 libtorrent_utp/include/libtorrent/identify_client.hpp delete mode 100644 libtorrent_utp/include/libtorrent/instantiate_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/intrusive_ptr_base.hpp delete mode 100644 libtorrent_utp/include/libtorrent/invariant_check.hpp delete mode 100644 libtorrent_utp/include/libtorrent/io.hpp delete mode 100644 libtorrent_utp/include/libtorrent/io_service.hpp delete mode 100644 libtorrent_utp/include/libtorrent/io_service_fwd.hpp delete mode 100644 libtorrent_utp/include/libtorrent/ip_filter.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/dht_tracker.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/find_data.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/logging.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/msg.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/node.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/node_entry.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/node_id.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/observer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/refresh.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/routing_table.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/rpc_manager.hpp delete mode 100644 libtorrent_utp/include/libtorrent/kademlia/traversal_algorithm.hpp delete mode 100644 libtorrent_utp/include/libtorrent/lazy_entry.hpp delete mode 100644 libtorrent_utp/include/libtorrent/lsd.hpp delete mode 100644 libtorrent_utp/include/libtorrent/magnet_uri.hpp delete mode 100644 libtorrent_utp/include/libtorrent/max.hpp delete mode 100644 libtorrent_utp/include/libtorrent/natpmp.hpp delete mode 100644 libtorrent_utp/include/libtorrent/packet_buffer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/parse_url.hpp delete mode 100644 libtorrent_utp/include/libtorrent/pch.hpp delete mode 100644 libtorrent_utp/include/libtorrent/pe_crypto.hpp delete mode 100644 libtorrent_utp/include/libtorrent/peer.hpp delete mode 100644 libtorrent_utp/include/libtorrent/peer_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/peer_id.hpp delete mode 100644 libtorrent_utp/include/libtorrent/peer_info.hpp delete mode 100644 libtorrent_utp/include/libtorrent/peer_request.hpp delete mode 100644 libtorrent_utp/include/libtorrent/piece_block_progress.hpp delete mode 100644 libtorrent_utp/include/libtorrent/piece_picker.hpp delete mode 100644 libtorrent_utp/include/libtorrent/policy.hpp delete mode 100644 libtorrent_utp/include/libtorrent/proxy_base.hpp delete mode 100644 libtorrent_utp/include/libtorrent/ptime.hpp delete mode 100644 libtorrent_utp/include/libtorrent/puff.hpp delete mode 100644 libtorrent_utp/include/libtorrent/session.hpp delete mode 100644 libtorrent_utp/include/libtorrent/session_settings.hpp delete mode 100644 libtorrent_utp/include/libtorrent/session_status.hpp delete mode 100644 libtorrent_utp/include/libtorrent/settings.hpp delete mode 100644 libtorrent_utp/include/libtorrent/size_type.hpp delete mode 100644 libtorrent_utp/include/libtorrent/sliding_average.hpp delete mode 100644 libtorrent_utp/include/libtorrent/socket.hpp delete mode 100644 libtorrent_utp/include/libtorrent/socket_io.hpp delete mode 100644 libtorrent_utp/include/libtorrent/socket_type.hpp delete mode 100644 libtorrent_utp/include/libtorrent/socket_type_fwd.hpp delete mode 100644 libtorrent_utp/include/libtorrent/socks5_stream.hpp delete mode 100644 libtorrent_utp/include/libtorrent/ssl_stream.hpp delete mode 100644 libtorrent_utp/include/libtorrent/stat.hpp delete mode 100644 libtorrent_utp/include/libtorrent/storage.hpp delete mode 100644 libtorrent_utp/include/libtorrent/storage_defs.hpp delete mode 100644 libtorrent_utp/include/libtorrent/thread.hpp delete mode 100644 libtorrent_utp/include/libtorrent/time.hpp delete mode 100644 libtorrent_utp/include/libtorrent/timestamp_history.hpp delete mode 100644 libtorrent_utp/include/libtorrent/tommath.h delete mode 100644 libtorrent_utp/include/libtorrent/tommath_class.h delete mode 100644 libtorrent_utp/include/libtorrent/tommath_superclass.h delete mode 100644 libtorrent_utp/include/libtorrent/torrent.hpp delete mode 100644 libtorrent_utp/include/libtorrent/torrent_handle.hpp delete mode 100644 libtorrent_utp/include/libtorrent/torrent_info.hpp delete mode 100644 libtorrent_utp/include/libtorrent/tracker_manager.hpp delete mode 100644 libtorrent_utp/include/libtorrent/udp_socket.hpp delete mode 100644 libtorrent_utp/include/libtorrent/udp_tracker_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/union_endpoint.hpp delete mode 100644 libtorrent_utp/include/libtorrent/upnp.hpp delete mode 100644 libtorrent_utp/include/libtorrent/utf8.hpp delete mode 100644 libtorrent_utp/include/libtorrent/utp_socket_manager.hpp delete mode 100644 libtorrent_utp/include/libtorrent/utp_stream.hpp delete mode 100644 libtorrent_utp/include/libtorrent/version.hpp delete mode 100644 libtorrent_utp/include/libtorrent/web_connection_base.hpp delete mode 100644 libtorrent_utp/include/libtorrent/web_peer_connection.hpp delete mode 100644 libtorrent_utp/include/libtorrent/xml_parse.hpp delete mode 100644 libtorrent_utp/libtorrent-rasterbar-cmake.pc.in delete mode 100644 libtorrent_utp/libtorrent-rasterbar.pc.in delete mode 100755 libtorrent_utp/list_files.py delete mode 100644 libtorrent_utp/m4/ax_boost_base.m4 delete mode 100644 libtorrent_utp/m4/ax_boost_python.m4 delete mode 100644 libtorrent_utp/m4/ax_boost_system.m4 delete mode 100644 libtorrent_utp/m4/ax_check_geoip.m4 delete mode 100644 libtorrent_utp/m4/ax_check_openssl.m4 delete mode 100644 libtorrent_utp/m4/ax_pthread.m4 delete mode 100644 libtorrent_utp/m4/ax_python_devel.m4 delete mode 100644 libtorrent_utp/m4/pkgconfig.m4 delete mode 100755 libtorrent_utp/parse_bandwidth_log.py delete mode 100755 libtorrent_utp/parse_buffer_log.py delete mode 100755 libtorrent_utp/parse_dht_log.py delete mode 100755 libtorrent_utp/parse_dht_rtt.py delete mode 100644 libtorrent_utp/parse_dht_stats.py delete mode 100755 libtorrent_utp/parse_disk_access.py delete mode 100755 libtorrent_utp/parse_disk_buffer_log.py delete mode 100755 libtorrent_utp/parse_disk_log.py delete mode 100755 libtorrent_utp/parse_memory_log.py delete mode 100644 libtorrent_utp/parse_sample.py delete mode 100755 libtorrent_utp/parse_session_stats.py delete mode 100755 libtorrent_utp/project-root.jam delete mode 100644 libtorrent_utp/set_version.py delete mode 100644 libtorrent_utp/src/ConvertUTF.cpp delete mode 100644 libtorrent_utp/src/GeoIP.c delete mode 100644 libtorrent_utp/src/Makefile.am delete mode 100644 libtorrent_utp/src/alert.cpp delete mode 100644 libtorrent_utp/src/allocator.cpp delete mode 100644 libtorrent_utp/src/assert.cpp delete mode 100644 libtorrent_utp/src/bandwidth_limit.cpp delete mode 100644 libtorrent_utp/src/bandwidth_manager.cpp delete mode 100644 libtorrent_utp/src/bandwidth_queue_entry.cpp delete mode 100644 libtorrent_utp/src/broadcast_socket.cpp delete mode 100644 libtorrent_utp/src/bt_peer_connection.cpp delete mode 100644 libtorrent_utp/src/connection_queue.cpp delete mode 100644 libtorrent_utp/src/create_torrent.cpp delete mode 100644 libtorrent_utp/src/disk_buffer_holder.cpp delete mode 100644 libtorrent_utp/src/disk_io_thread.cpp delete mode 100644 libtorrent_utp/src/entry.cpp delete mode 100644 libtorrent_utp/src/enum_net.cpp delete mode 100644 libtorrent_utp/src/error_code.cpp delete mode 100644 libtorrent_utp/src/escape_string.cpp delete mode 100644 libtorrent_utp/src/file.cpp delete mode 100644 libtorrent_utp/src/file_pool.cpp delete mode 100644 libtorrent_utp/src/file_storage.cpp delete mode 100644 libtorrent_utp/src/gzip.cpp delete mode 100644 libtorrent_utp/src/http_connection.cpp delete mode 100644 libtorrent_utp/src/http_parser.cpp delete mode 100644 libtorrent_utp/src/http_seed_connection.cpp delete mode 100644 libtorrent_utp/src/http_stream.cpp delete mode 100644 libtorrent_utp/src/http_tracker_connection.cpp delete mode 100644 libtorrent_utp/src/i2p_stream.cpp delete mode 100644 libtorrent_utp/src/identify_client.cpp delete mode 100644 libtorrent_utp/src/instantiate_connection.cpp delete mode 100644 libtorrent_utp/src/ip_filter.cpp delete mode 100644 libtorrent_utp/src/kademlia/dht_tracker.cpp delete mode 100644 libtorrent_utp/src/kademlia/find_data.cpp delete mode 100644 libtorrent_utp/src/kademlia/node.cpp delete mode 100644 libtorrent_utp/src/kademlia/node_id.cpp delete mode 100644 libtorrent_utp/src/kademlia/refresh.cpp delete mode 100644 libtorrent_utp/src/kademlia/routing_table.cpp delete mode 100644 libtorrent_utp/src/kademlia/rpc_manager.cpp delete mode 100644 libtorrent_utp/src/kademlia/traversal_algorithm.cpp delete mode 100644 libtorrent_utp/src/lazy_bdecode.cpp delete mode 100644 libtorrent_utp/src/logger.cpp delete mode 100644 libtorrent_utp/src/lsd.cpp delete mode 100644 libtorrent_utp/src/lt_trackers.cpp delete mode 100644 libtorrent_utp/src/magnet_uri.cpp delete mode 100644 libtorrent_utp/src/metadata_transfer.cpp delete mode 100644 libtorrent_utp/src/mpi.c delete mode 100644 libtorrent_utp/src/natpmp.cpp delete mode 100644 libtorrent_utp/src/packet_buffer.cpp delete mode 100644 libtorrent_utp/src/parse_url.cpp delete mode 100644 libtorrent_utp/src/pe_crypto.cpp delete mode 100644 libtorrent_utp/src/peer_connection.cpp delete mode 100644 libtorrent_utp/src/piece_picker.cpp delete mode 100644 libtorrent_utp/src/policy.cpp delete mode 100644 libtorrent_utp/src/puff.cpp delete mode 100644 libtorrent_utp/src/session.cpp delete mode 100644 libtorrent_utp/src/session_impl.cpp delete mode 100644 libtorrent_utp/src/settings.cpp delete mode 100644 libtorrent_utp/src/sha1.cpp delete mode 100644 libtorrent_utp/src/smart_ban.cpp delete mode 100644 libtorrent_utp/src/socket_io.cpp delete mode 100644 libtorrent_utp/src/socket_type.cpp delete mode 100644 libtorrent_utp/src/socks5_stream.cpp delete mode 100644 libtorrent_utp/src/stat.cpp delete mode 100644 libtorrent_utp/src/storage.cpp delete mode 100644 libtorrent_utp/src/thread.cpp delete mode 100644 libtorrent_utp/src/time.cpp delete mode 100644 libtorrent_utp/src/timestamp_history.cpp delete mode 100644 libtorrent_utp/src/torrent.cpp delete mode 100644 libtorrent_utp/src/torrent_handle.cpp delete mode 100644 libtorrent_utp/src/torrent_info.cpp delete mode 100644 libtorrent_utp/src/tracker_manager.cpp delete mode 100644 libtorrent_utp/src/udp_socket.cpp delete mode 100644 libtorrent_utp/src/udp_tracker_connection.cpp delete mode 100644 libtorrent_utp/src/upnp.cpp delete mode 100644 libtorrent_utp/src/ut_metadata.cpp delete mode 100644 libtorrent_utp/src/ut_pex.cpp delete mode 100644 libtorrent_utp/src/utp_socket_manager.cpp delete mode 100644 libtorrent_utp/src/utp_stream.cpp delete mode 100644 libtorrent_utp/src/web_connection_base.cpp delete mode 100644 libtorrent_utp/src/web_peer_connection.cpp delete mode 100644 libtorrent_utp/test/Jamfile delete mode 100644 libtorrent_utp/test/Makefile.am delete mode 100644 libtorrent_utp/test/main.cpp delete mode 100644 libtorrent_utp/test/setup_transfer.cpp delete mode 100644 libtorrent_utp/test/setup_transfer.hpp delete mode 100644 libtorrent_utp/test/test.hpp delete mode 100644 libtorrent_utp/test/test_auto_unchoke.cpp delete mode 100644 libtorrent_utp/test/test_bandwidth_limiter.cpp delete mode 100644 libtorrent_utp/test/test_bdecode_performance.cpp delete mode 100644 libtorrent_utp/test/test_bencoding.cpp delete mode 100644 libtorrent_utp/test/test_buffer.cpp delete mode 100644 libtorrent_utp/test/test_dht.cpp delete mode 100644 libtorrent_utp/test/test_fast_extension.cpp delete mode 100644 libtorrent_utp/test/test_hasher.cpp delete mode 100644 libtorrent_utp/test/test_http_connection.cpp delete mode 100644 libtorrent_utp/test/test_ip_filter.cpp delete mode 100644 libtorrent_utp/test/test_lsd.cpp delete mode 100644 libtorrent_utp/test/test_metadata_extension.cpp delete mode 100644 libtorrent_utp/test/test_natpmp.cpp delete mode 100644 libtorrent_utp/test/test_pe_crypto.cpp delete mode 100644 libtorrent_utp/test/test_pex.cpp delete mode 100644 libtorrent_utp/test/test_piece_picker.cpp delete mode 100644 libtorrent_utp/test/test_primitives.cpp delete mode 100644 libtorrent_utp/test/test_storage.cpp delete mode 100644 libtorrent_utp/test/test_swarm.cpp delete mode 100644 libtorrent_utp/test/test_threads.cpp delete mode 100644 libtorrent_utp/test/test_torrent.cpp delete mode 100644 libtorrent_utp/test/test_trackers_extension.cpp delete mode 100644 libtorrent_utp/test/test_transfer.cpp delete mode 100644 libtorrent_utp/test/test_upnp.cpp delete mode 100644 libtorrent_utp/test/test_utp.cpp delete mode 100644 libtorrent_utp/test/test_web_seed.cpp diff --git a/libtorrent_utp/AUTHORS b/libtorrent_utp/AUTHORS deleted file mode 100644 index 5a3d84e12..000000000 --- a/libtorrent_utp/AUTHORS +++ /dev/null @@ -1,26 +0,0 @@ - -Written by Arvid Norberg. Copyright (c) 2003-2007 - -Lots of testing, suggestions and contributions by: -Massaroddel -Tianhao Qiu. - -Contributions by: -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/libtorrent_utp/CMakeLists.txt b/libtorrent_utp/CMakeLists.txt deleted file mode 100644 index 605f63e18..000000000 --- a/libtorrent_utp/CMakeLists.txt +++ /dev/null @@ -1,302 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -project(libtorrent) - -set(sources - web_connection_base - alert - allocator - assert - bandwidth_limit - bandwidth_manager - bandwidth_queue_entry - connection_queue - create_torrent - disk_buffer_holder - entry - error_code - file_storage - lazy_bdecode - escape_string - file - gzip - http_connection - http_stream - http_parser - i2p_stream - identify_client - ip_filter - peer_connection - bt_peer_connection - web_peer_connection - http_seed_connection - instantiate_connection - natpmp - packet_buffer - piece_picker - policy - puff - session - session_impl - settings - socket_io - socket_type - socks5_stream - stat - storage - thread - time - torrent - torrent_handle - torrent_info - tracker_manager - http_tracker_connection - udp_tracker_connection - udp_socket - upnp - logger - file_pool - lsd - disk_io_thread - enum_net - broadcast_socket - magnet_uri - parse_url - ConvertUTF - -# -- 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 -) - -set(includes include) - -option(shared "build libtorrent as a shared library" ON) -option(tcmalloc "link against google performance tools tcmalloc" OFF) -option(pool-allocators "Uses a pool allocator for disk and piece buffers" ON) -option(encryption "link against openssl and enable encryption" ON) -option(geoip "link against LGPL GeoIP code from Maxmind, to enable geoip database support" OFF) -option(dht "enable support for Mainline DHT" ON) -option(resolve-countries "enable support for resolving countries from peer IPs" ON) -option(unicode "enable unicode support" ON) -option(deprecated-functions "enable deprecated functions for backwards compatibility" ON) -option(exceptions "build with exception support" ON) -option(logging "build with logging" OFF) -option(verbose-logging "build with verbose logging" OFF) -option(build_tests "build tests" OFF) -option(build_examples "build examples" OFF) - -set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) - -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release FORCE) -endif (NOT CMAKE_BUILD_TYPE) - -# 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") - -set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") - -if (encryption) - list(APPEND sources pe_crypto) -endif (encryption) - -if (logging) - add_definitions(-DTORRENT_LOGGING) -endif (logging) -if (verbose-logging) - add_definitions(-DTORRENT_VERBOSE_LOGGING) -endif (verbose-logging) - -foreach(s ${sources}) - list(APPEND sources2 src/${s}) -endforeach(s) - -if (dht) - foreach(s ${kademlia_sources}) - list(APPEND sources2 src/kademlia/${s}) - endforeach(s) -else (dht) - add_definitions(-DTORRENT_DISABLE_DHT) -endif (dht) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") - -if (shared) - add_library(torrent-rasterbar SHARED ${sources2}) -else (shared) - add_library(torrent-rasterbar STATIC ${sources2}) -endif (shared) - -FIND_PACKAGE( Boost 1.34 COMPONENTS filesystem) -if (NOT Boost_VERSION LESS 103500) - FIND_PACKAGE( Boost 1.35 COMPONENTS filesystem system) -endif (NOT Boost_VERSION LESS 103500) -include_directories(${Boost_INCLUDE_DIR}) -target_link_libraries(torrent-rasterbar ${Boost_LIBRARIES}) - -# this works around a bug in asio in boost-1.39 -add_definitions(-DBOOST_ASIO_HASH_MAP_BUCKETS=1021) - -if (WIN32) - target_link_libraries(torrent-rasterbar wsock32 ws2_32) -endif (WIN32) - -if (encryption) - add_definitions(-DTORRENT_USE_OPENSSL) - if (WIN32) - target_link_libraries(torrent-rasterbar ssleay32 libeay32 advapi32 user32 shell32 gdi32) - else (WIN32) - target_link_libraries(torrent-rasterbar crypto ssl) - endif (WIN32) -else (encryption) - add_definitions(-DTORRENT_DISABLE_ENCRYPTION) - list(APPEND sources sha1) -endif (encryption) - -if (NOT pool-allocators) - add_definitions(-DTORRENT_DISABLE_POOL_ALLOCATOR) -endif (NOT pool-allocators) - -if (NOT geoip) - add_definitions(-DTORRENT_DISABLE_GEO_IP) -endif (NOT geoip) - -if (NOT resolve-countries) - add_definitions(-DTORRENT_DISABLE_RESOLVE_COUNTRIES) -endif (NOT resolve-countries) - -if (unicode) - add_definitions(-DUNICODE -D_UNICODE) -endif (unicode) - -if (NOT deprecated-functions) - add_definitions(-DTORRENT_NO_DEPRECATE) -endif (NOT deprecated-functions) - -if (exceptions) - add_definitions(-fexceptions) -else (exceptions) - add_definitions(-fno-exceptions) -endif (exceptions) - -if (MSVC) -# disable bogus deprecation warnings on msvc8 - add_definitions(-D_SCL_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) -# these compiler settings just makes the compiler standard conforming - add_definitions(/Zc:wchar_t /Zc:forScope) - -# msvc,release:/OPT:ICF=5 -# msvc,release:/OPT:REF -endif(MSVC) - -add_definitions(-D_FILE_OFFSET_BITS=64) -add_definitions(-DBOOST_DISABLE_EXCEPTION) -add_definitions(-DBOOST_ASIO_ENABLE_CANCELIO) - -if (tcmalloc) - target_link_libraries(torrent-rasterbar tcmalloc) -endif (tcmalloc) - -target_link_libraries(torrent-rasterbar z) -include_directories(${includes}) - -set_target_properties(torrent-rasterbar PROPERTIES - SOVERSION 1 - VERSION 1) - -set (VERSION "0.16.0") - -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 (s) - -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 (IS64BITS AND RESPECTLIB64) - set (LIBDIR "lib") -endif (IS64BITS AND RESPECTLIB64) - -install(TARGETS torrent-rasterbar DESTINATION ${LIBDIR} CONFIGURATIONS release) -install(DIRECTORY include/libtorrent - DESTINATION include - PATTERN ".svn" EXCLUDE) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libtorrent-rasterbar.pc DESTINATION ${LIBDIR}/pkgconfig) - -# === build examples === -if(build_examples) - set(examples client_test dump_torrent simple_client enum_if make_torrent) - - foreach(s ${examples}) - add_executable(${s} examples/${s}.cpp) - target_link_libraries(${s} torrent-rasterbar) - endforeach(s) - - FIND_PACKAGE( Boost 1.34 COMPONENTS program_options regex) - target_link_libraries(client_test ${Boost_LIBRARIES}) - include_directories(${Boost_INCLUDE_DIR}) -endif(build_examples) -# === build tests === -if(build_tests) - set(tests - test_auto_unchoke - test_http_connection - test_buffer - test_storage - test_torrent - test_dht - test_transfer - test_piece_picker - test_fast_extension - test_pe_crypto - test_bencoding - test_bdecode_performance - test_primitives - test_ip_filter - test_hasher - test_metadata_extension - test_swarm - test_lsd - test_pex - test_web_seed - test_bandwidth_limiter - ) - - add_library(test_common STATIC test/main.cpp test/setup_transfer.cpp) - enable_testing() - - foreach(s ${tests}) - add_executable(${s} test/${s}.cpp) - target_link_libraries(${s} torrent-rasterbar test_common) - add_test(${s} ${s}) - endforeach(s) - - add_executable(test_upnp test/test_upnp.cpp) - target_link_libraries(test_upnp torrent-rasterbar) - - add_executable(test_natpmp test/test_natpmp.cpp) - target_link_libraries(test_natpmp torrent-rasterbar) -endif(build_tests) diff --git a/libtorrent_utp/COPYING b/libtorrent_utp/COPYING deleted file mode 100644 index f38f5cb2d..000000000 --- a/libtorrent_utp/COPYING +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2003 - 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 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/libtorrent_utp/ChangeLog b/libtorrent_utp/ChangeLog deleted file mode 100644 index d29dc7d16..000000000 --- a/libtorrent_utp/ChangeLog +++ /dev/null @@ -1,772 +0,0 @@ - * 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 - - * 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/libtorrent_utp/Jamfile b/libtorrent_utp/Jamfile deleted file mode 100755 index 78f792cd2..000000000 --- a/libtorrent_utp/Jamfile +++ /dev/null @@ -1,515 +0,0 @@ -# This Jamfile requires boost-build v2 to build. -# The version shipped with boost 1.34.0 - -import modules ; -import os ; -import errors ; -import feature : feature ; -import package ; -import virtual-target ; - -BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; -CXXFLAGS = [ modules.peek : CXXFLAGS ] ; -LDFLAGS = [ modules.peek : LDFLAGS ] ; - -ECHO "BOOST_ROOT =" $(BOOST_ROOT) ; -ECHO "OS =" [ os.name ] ; - -if $(BOOST_ROOT) -{ - use-project /boost : $(BOOST_ROOT) ; -} - -VERSION = 0.16.0 ; - -# rule for linking the correct libraries depending -# on features and target-os -rule linking ( properties * ) -{ - local result ; - - # openssl libraries, if enabled - if openssl in $(properties) - { - # exclude gcc from a regular windows build to make mingw - # link against the regular unix library name - - if windows in $(properties) - { - result += gdi32 ; - } - - if windows in $(properties) && ! gcc in $(properties) - { - result += ssleay32 - libeay32 - advapi32 - user32 - shell32 - ; - } - else - { - result += crypto ssl ; - } - } - - # gcrypt libraries, if enabled - if gcrypt in $(properties) - { - # on mac os x, adding the /opt/local/include path - # would include openssl headers incompatible with - # the system library. Only add this include path - # if we're not using openssl (which we're most - # likely not if we're using libgcrypt) - result += gcrypt /opt/local/include ; - } - - if tommath in $(properties) - { - result += src/mpi.c ; - } - - if shared in $(properties) - { - result += GeoIP ; - } - - # socket functions on windows require winsock libraries - if windows in $(properties) - || cygwin in $(properties) - { - result += ws2_32 - wsock32 - iphlpapi - WIN32_LEAN_AND_MEAN - _WIN32_WINNT=0x0600 - __USE_W32_SOCKETS - WIN32 - _WIN32 - ; - } - - if beos in $(properties) - { - result += netkit ; - } - - if solaris in $(properties) - { - result += libsocket libnsl ; - } - - if on in $(properties) - && ( gcc in $(properties) - || darwin in $(properties) ) - { - result += -fprofile-arcs -ftest-coverage - -lgcov NDEBUG ; - } - - # clock_gettime on linux requires librt - if yes in $(properties) - { - result += librt ; - } - - if yes in $(properties) - { - result += tcmalloc ; - } - - if system in $(properties) - { - result += boost_system ; - } - - if gcc in $(properties) - && linux in $(properties) - && debug in $(properties) - { - # for backtraces in assertion failures - # which only works on ELF targets with gcc - result += -export-dynamic -rdynamic ; - } - - if source in $(properties) - { - if static in $(properties) - { - if gcc in $(properties) && shared in $(properties) - { - result += on ; - } - - result += /boost/system//boost_system/static ; - } - else - { - result += /boost/system//boost_system/shared ; - } - result += $(BOOST_ROOT) BOOST_ALL_NO_LIB ; - } - - if system in $(properties) - { - # on mac the boost headers are installed in - # a directory that isn't automatically accessable - result += /opt/local/include/boost-1_35 - /opt/local/include - ; - } - - return $(result) ; -} - -# rule for adding the right source files -# depending on target-os and features -rule building ( properties * ) -{ - local result ; - - if ( linux in $(properties) - || darwin in $(properties) ) - && ( gcc in $(properties) - || darwin in $(properties) ) - { - result += src/assert.cpp ; - } - - if static in $(properties) - { - result += src/GeoIP.c ; - } - - if off in $(properties) - || tommath in $(properties) - { - result += src/sha1.cpp ; - } - - if ! ( off in $(properties) ) - { - result += src/pe_crypto.cpp ; - - if linux in $(properties) - && openssl in $(properties) - { - # linker library on linux, required when using openssl - result += /usr/lib/libdl.so ; - } - - } - - return $(result) ; -} - -rule tag ( name : type ? : property-set ) -{ - name = [ virtual-target.add-prefix-and-suffix $(name) : $(type) : $(property-set) ] ; - - if $(type) = SHARED_LIB && - ( ! ( [ $(property-set).get ] in windows cygwin ) ) - { - name = $(name).$(VERSION) ; - } - - return $(name) ; -} - -feature tcmalloc : no yes : composite propagated link-incompatible ; - -feature timer : auto boost absolute performance clock system_time - : composite propagated link-incompatible ; -feature.compose boost : TORRENT_USE_BOOST_DATE_TIME=1 ; -feature.compose absolute : TORRENT_USE_ABSOLUTE_TIME=1 ; -feature.compose performance : TORRENT_USE_PERFORMANCE_TIMER=1 ; -feature.compose clock : TORRENT_USE_CLOCK_GETTIME=1 ; -feature.compose system_time : TORRENT_USE_SYSTEM_TIME=1 ; - -feature ipv6 : on off : composite propagated link-incompatible ; -feature.compose off : TORRENT_USE_IPV6=0 ; - -feature need-librt : no yes : composite propagated link-incompatible ; - -feature fiemap : off on : composite propagated ; -feature.compose on : HAVE_LINUX_FIEMAP_H ; - -feature full-stats : on off : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_FULL_STATS ; - -feature asserts : on off production : composite propagated ; -feature.compose production : TORRENT_PRODUCTION_ASSERTS=1 ; -feature.compose off : TORRENT_NO_ASSERTS=1 ; - -feature asio-debugging : off on : composite propagated link-incompatible ; -feature.compose on : TORRENT_ASIO_DEBUGGING ; - -feature pool-allocators : on off : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_POOL_ALLOCATOR ; - -feature piece-allocator : valloc memalign posix_memalign : composite propagated ; -feature.compose memalign : TORRENT_USE_MEMALIGN=1 ; -feature.compose posix_memalign : TORRENT_USE_POSIX_MEMALIGN=1 ; - -feature geoip : off static shared : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_GEO_IP ; - -feature bandwidth-limit-logging : off on : composite propagated link-incompatible ; -feature.compose on : TORRENT_VERBOSE_BANDWIDTH_LIMIT ; - -feature invariant-checks : on off full : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_INVARIANT_CHECKS ; -feature.compose full : TORRENT_EXPENSIVE_INVARIANT_CHECKS ; - -feature disk-stats : off on : composite propagated link-incompatible ; -feature.compose on : TORRENT_DISK_STATS ; - -feature simulate-slow-read : off on : composite propagated ; -feature.compose on : TORRENT_SIMULATE_SLOW_READ ; - -feature logging : none default errors verbose : composite propagated link-incompatible ; -feature.compose default : TORRENT_LOGGING ; -feature.compose errors : TORRENT_ERROR_LOGGING ; -feature.compose verbose : TORRENT_VERBOSE_LOGGING ; - -feature dht-support : on off logging : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_DHT ; -feature.compose logging : TORRENT_DHT_VERBOSE_LOGGING ; - -feature encryption : tommath off openssl gcrypt : composite propagated link-incompatible ; -feature.compose openssl : TORRENT_USE_OPENSSL ; -feature.compose gcrypt : TORRENT_USE_GCRYPT ; -feature.compose tommath : TORRENT_USE_TOMMATH ; -feature.compose off : TORRENT_DISABLE_ENCRYPTION ; - -feature resolve-countries : on off : composite propagated link-incompatible ; -feature.compose off : TORRENT_DISABLE_RESOLVE_COUNTRIES ; - -feature character-set : unicode ansi : composite propagated link-incompatible ; -feature.compose unicode : _UNICODE UNICODE ; - -feature deprecated-functions : on off : composite propagated link-incompatible ; -feature.compose off : TORRENT_NO_DEPRECATE ; - -feature statistics : off on : composite propagated link-incompatible ; -feature.compose on : TORRENT_STATS ; - -feature upnp-logging : off on : composite propagated link-incompatible ; -feature.compose on : TORRENT_UPNP_LOGGING ; - -feature boost : system source : link-incompatible propagated ; -feature boost-link : static shared : composite ; - -feature debug-iterators : off on : composite propagated link-incompatible ; -feature.compose on : _SCL_SECURE=1 _GLIBCXX_DEBUG ; - -feature test-coverage : off on : composite propagated ; - -feature fpic : off on : composite propagated link-incompatible ; -feature.compose on : -fPIC ; -feature.compose off : darwin:-mdynamic-no-pic ; - -feature visibility : default hidden : composite propagated link-incompatible ; -feature.compose hidden : -fvisibility=hidden ; - -# required for openssl on windows -lib ssleay32 : : ssleay32 ; -lib libeay32 : : libeay32 ; -lib advapi32 : : Advapi32 ; -lib user32 : : User32 ; -lib shell32 : : shell32 ; -lib gdi32 : : gdi32 ; - -# required for networking on beos -lib netkit : : net /boot/system/lib shared ; - -local boost-library-search-path = - /opt/local/lib - /usr/lib - /usr/local/lib - /sw/lib - ; - -lib boost_system : : darwin boost_system-mt $(boost-library-search-path) ; - -lib boost_system : : boost_system ; - -# openssl on linux/bsd/macos etc. -lib gcrypt : : gcrypt shared /opt/local/lib ; -lib crypto : : crypto /lib ; -lib ssl : : ssl shared crypto ; - -# time functions used on linux require librt -lib librt : : rt shared ; - -lib libsocket : : libnsl socket shared /usr/sfw/lib shared ; -lib libnsl : : nsl shared /usr/sfw/lib shared ; - -lib tcmalloc : : tcmalloc shared ; - -# GeoIP shared library -lib GeoIP : : GeoIP shared ; - -# socket libraries on windows -lib wsock32 : : wsock32 shared ; -lib ws2_32 : : ws2_32 shared ; -lib iphlpapi : : iphlpapi shared ; - -SOURCES = - alert - allocator - assert - bandwidth_limit - bandwidth_manager - bandwidth_queue_entry - connection_queue - create_torrent - disk_buffer_holder - entry - error_code - file_storage - lazy_bdecode - escape_string - file - gzip - http_connection - http_stream - http_parser - identify_client - ip_filter - peer_connection - bt_peer_connection - web_connection_base - web_peer_connection - http_seed_connection - i2p_stream - instantiate_connection - natpmp - packet_buffer - piece_picker - policy - puff - session - session_impl - settings - socket_io - socket_type - socks5_stream - stat - storage - torrent - torrent_handle - torrent_info - time - tracker_manager - http_tracker_connection - udp_tracker_connection - sha1 - timestamp_history - udp_socket - upnp - utp_socket_manager - utp_stream - logger - file_pool - lsd - disk_io_thread - enum_net - broadcast_socket - magnet_uri - parse_url - ConvertUTF - thread - -# -- extensions -- - metadata_transfer - ut_pex - ut_metadata - lt_trackers - smart_ban - ; - -KADEMLIA_SOURCES = - dht_tracker - node - refresh - rpc_manager - find_data - node_id - routing_table - traversal_algorithm - ; - -local usage-requirements = - ./include - ./include/libtorrent - /usr/sfw/include - release:NDEBUG - debug:TORRENT_DEBUG - _FILE_OFFSET_BITS=64 - BOOST_EXCEPTION_DISABLE -# enable cancel support in asio - BOOST_ASIO_ENABLE_CANCELIO - @linking -# these compiler settings just makes the compiler standard conforming - msvc:/Zc:wchar_t - msvc:/Zc:forScope -# disable bogus deprecation warnings on msvc8 - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_DEPRECATE -# msvc optimizations - msvc,release:/OPT:ICF=5 - msvc,release:/OPT:REF -# disable warning C4503: decorated name length exceeded, name was truncated - msvc:/wd4503 -# disable warning C4275: non-dll interface class 'x' used as base for dll-interface struct 'y' - msvc:/wd4275 -# disable warning C4251: 'x' needs to have dll-interface to be used by clients of class 'y' - msvc:/wd4251 -# disable some warnings for gcc - gcc:-fno-strict-aliasing - gcc:-Wno-missing-braces - system:$(CXXFLAGS) - system:$(LDFLAGS) -# this works around a bug in asio in boost-1.39 - BOOST_ASIO_HASH_MAP_BUCKETS=1021 - @tag - ; - -project torrent ; - -lib torrent - - : # sources - src/$(SOURCES).cpp - - : # requirements - BOOST_THREAD_USE_LIB - multi - shared:TORRENT_BUILDING_SHARED - on:src/kademlia/$(KADEMLIA_SOURCES).cpp - logging:src/kademlia/$(KADEMLIA_SOURCES).cpp - @building - system:$(CXXFLAGS) - $(usage-requirements) - - : # default build - static - multi - - : # usage requirements - $(usage-requirements) - ; - -headers = [ path.glob-tree include/libtorrent : *.hpp ] ; - -package.install install - : libtorrent - libtorrent - on - : - : torrent - : $(headers) - ; - diff --git a/libtorrent_utp/LICENSE b/libtorrent_utp/LICENSE deleted file mode 100644 index d52545910..000000000 --- a/libtorrent_utp/LICENSE +++ /dev/null @@ -1,103 +0,0 @@ -Copyright (c) 2003-2010, 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/libtorrent_utp/Makefile.am b/libtorrent_utp/Makefile.am deleted file mode 100644 index 430c69edb..000000000 --- a/libtorrent_utp/Makefile.am +++ /dev/null @@ -1,99 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 - -#DISTCHECK_CONFIGURE_FLAGS = --enable-tests - -SUBDIRS = include/libtorrent src examples test bindings - -DOCS_IMAGES = \ - docs/arctic_thumb.png \ - docs/bitbuddy_thumb.jpg \ - docs/bitfox.png \ - docs/bitrocket_thumb.png \ - docs/bitscast_thumb.png \ - docs/bitslug_thumb.png \ - docs/btg_thumb.jpg \ - docs/bubba.png \ - docs/client_test.png \ - docs/deluge.png \ - docs/disk_access.png \ - docs/disk_buffer_before_optimization.png \ - docs/disk_buffer.png \ - docs/disk_buffer_sample.png \ - docs/disk_io.png \ - docs/electric_sheep_thumb.jpg \ - docs/fatrat.png \ - docs/fdm.png \ - docs/firetorrent.png \ - docs/flush.jpg \ - docs/halite_thumb.png \ - docs/im_thumb.jpg \ - docs/leechcraft.png \ - docs/libtorrent_screen.png \ - docs/limewire.png \ - docs/lince.png \ - docs/Linkage.png \ - docs/merkle_tree.graffle \ - docs/merkle_tree.png \ - docs/miro.jpg \ - docs/moopolice_thumb.gif \ - docs/pump.png \ - docs/qbittorrent_thumb.jpg \ - docs/read_disk_buffers.dot \ - docs/read_disk_buffers.graffle \ - docs/read_disk_buffers.png \ - docs/session_stats_peers.png \ - docs/storage.graffle \ - docs/storage.png \ - docs/style.css \ - docs/tvblob.jpg \ - docs/tvitty.jpg \ - docs/unicode_support.png \ - docs/write_disk_buffers.dot \ - docs/write_disk_buffers.graffle \ - docs/write_disk_buffers.png \ - docs/ziptorrent_thumb.gif - -DOCS_PAGES = \ - docs/building.html \ - docs/building.rst \ - docs/client_test.html \ - docs/client_test.rst \ - docs/dht_extensions.html \ - docs/dht_extensions.rst \ - docs/examples.html \ - docs/examples.rst \ - docs/extension_protocol.html \ - docs/extension_protocol.rst \ - docs/features.html \ - docs/features.rst \ - docs/index.html \ - docs/index.rst \ - docs/libtorrent_plugins.html \ - docs/libtorrent_plugins.rst \ - docs/make_torrent.html \ - docs/make_torrent.rst \ - docs/manual.html \ - docs/manual.rst \ - docs/projects.html \ - docs/projects.rst \ - docs/python_binding.html \ - docs/python_binding.rst \ - docs/running_tests.html \ - docs/running_tests.rst \ - docs/tuning.html \ - docs/tuning.rst \ - docs/udp_tracker_protocol.html \ - docs/udp_tracker_protocol.rst - -EXTRA_DIST = \ - Jamfile \ - project-root.jam \ - CMakeLists.txt \ - LICENSE \ - libtorrent-rasterbar.pc \ - libtorrent-rasterbar-cmake.pc \ - $(DOCS_PAGES) \ - $(DOCS_IMAGES) - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libtorrent-rasterbar.pc diff --git a/libtorrent_utp/NEWS b/libtorrent_utp/NEWS deleted file mode 100644 index cd9658d6b..000000000 --- a/libtorrent_utp/NEWS +++ /dev/null @@ -1,3 +0,0 @@ - -initial release of libtorrent 0.9 - diff --git a/libtorrent_utp/README b/libtorrent_utp/README deleted file mode 100644 index 0b19bf61f..000000000 --- a/libtorrent_utp/README +++ /dev/null @@ -1,25 +0,0 @@ -libtorrent is a C++ library that aims to be a good alternative to all the -other bittorrent implementations around. It is a -library and not a full featured client, although it comes with a working -example client. - -The main goals of libtorrent are: - - * to be cpu efficient - * to be memory efficient - * to be very easy to use - -See docs/manual.html for more detailed build and usage instructions. - -To build with autotools, run: - - ./configure - -Followed by - - make - -When libtorrent is built, finish off by running the tests: - - make check - diff --git a/libtorrent_utp/autotool.sh b/libtorrent_utp/autotool.sh deleted file mode 100755 index 0301f1397..000000000 --- a/libtorrent_utp/autotool.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/bin/sh - -# Using this script should be identical to the resulto of "autoreconf -fi". -# Some code taken from the gnome macros/autogen.sh scripts. - -# $Id$ - - -############################################################################### -# utility functions -############################################################################### - -# Not all echo versions allow -n, so we check what is possible. This test is -# based on the one in autoconf. -ECHO_C= -ECHO_N= -case `echo -n x` in --n*) - case `echo 'x\c'` in - *c*) ;; - *) ECHO_C='\c';; - esac;; -*) - ECHO_N='-n';; -esac - -# some terminal codes ... -boldface="`tput bold 2>/dev/null`" -normal="`tput sgr0 2>/dev/null`" - -printbold() { - echo $ECHO_N "$boldface" $ECHO_C - echo "$@" - echo $ECHO_N "$normal" $ECHO_C -} - -printerr() { - echo "$@" >&2 -} - -# Usage: -# compare_versions MIN_VERSION ACTUAL_VERSION -# returns true if ACTUAL_VERSION >= MIN_VERSION -compare_versions() { - ch_min_version=$1 - ch_actual_version=$2 - ch_status=0 - IFS="${IFS= }"; ch_save_IFS="$IFS"; IFS="." - set $ch_actual_version - for ch_min in $ch_min_version; do - ch_cur=`echo $1 | sed 's/[^0-9].*$//'`; shift # remove letter suffixes - if [ -z "$ch_min" ]; then break; fi - if [ -z "$ch_cur" ]; then ch_status=1; break; fi - if [ $ch_cur -gt $ch_min ]; then break; fi - if [ $ch_cur -lt $ch_min ]; then ch_status=1; break; fi - done - IFS="$ch_save_IFS" - return $ch_status -} - -# Usage: -# version_check PACKAGE VARIABLE CHECKPROGS MIN_VERSION -# checks to see if the package is available -version_check() { - vc_package=$1 - vc_variable=$2 - vc_checkprogs=$3 - vc_min_version=$4 - vc_status=1 - - vc_checkprog=`eval echo "\\$$vc_variable"` - if [ -n "$vc_checkprog" ]; then - printbold "Using $vc_checkprog for $vc_package" - return 0 - fi - - vc_comparator=">=" - - printbold "Checking for $vc_package $vc_comparator $vc_min_version..." - - for vc_checkprog in $vc_checkprogs; do - echo $ECHO_N " testing $vc_checkprog... " $ECHO_C - if $vc_checkprog --version < /dev/null > /dev/null 2>&1; then - vc_actual_version=`$vc_checkprog --version | head -n 1 | \ - sed 's/^.*[ ]\([0-9.]*[a-z]*\).*$/\1/'` - if compare_versions $vc_min_version $vc_actual_version; then - echo "found $vc_actual_version" - # set variables - eval "$vc_variable=$vc_checkprog; \ - ${vc_variable}_VERSION=$vc_actual_version" - vc_status=0 - break - else - echo "too old (found version $vc_actual_version)" - fi - else - echo "not found." - fi - done - - if [ "$vc_status" != 0 ]; then - printerr "***Error***: $vc_package $vc_comparator $vc_min_version not found." - fi - - return $vc_status -} - -############################################################################### -# main section -############################################################################### - -configure_ac="configure.ac" - -(test -f $configure_ac && test -f src/torrent.cpp) || { - printerr "***Error***: Run this script from the top-level source directory." - exit 1 -} - -echo -printbold "Bootstrapping autotools for libtorrent-rasterbar" -echo - -REQUIRED_AUTOCONF_VERSION=`cat $configure_ac | grep '^AC_PREREQ' | -sed -n -e 's/AC_PREREQ(\([^()]*\))/\1/p' | sed -e 's/^\[\(.*\)\]$/\1/' | sed -e 1q` - -REQUIRED_AUTOMAKE_VERSION=`cat configure.ac | grep '^AM_INIT_AUTOMAKE' | -sed -n -e 's/AM_INIT_AUTOMAKE(\([^()]*\))/\1/p' | sed -e 's/^\[\(.*\)\]$/\1/' | sed -e 's/\(.*\) .*/\1/' | sed -e 1q` - -REQUIRED_LIBTOOL_VERSION=`cat $configure_ac | grep '^LT_PREREQ' | -sed -n -e 's/LT_PREREQ(\([^()]*\))/\1/p' | sed -e 's/^\[\(.*\)\]$/\1/' | sed -e 1q` - -printbold "Checking autotools requirements:" -echo - -version_check autoconf AUTOCONF 'autoconf autoconf2.59 autoconf-2.53 autoconf2.50' $REQUIRED_AUTOCONF_VERSION || exit 1 -AUTOHEADER=`echo $AUTOCONF | sed s/autoconf/autoheader/` - -version_check automake AUTOMAKE "automake automake-1.11 automake-1.10" $REQUIRED_AUTOMAKE_VERSION || exit 1 -ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/` - -version_check libtool LIBTOOLIZE "libtoolize glibtoolize" $REQUIRED_LIBTOOL_VERSION || exit 1 - -echo -printbold "Processing $configure_ac" -echo - -if grep "^A[CM]_PROG_LIBTOOL" $configure_ac >/dev/null || - grep "^LT_INIT" $configure_ac >/dev/null; then - printbold "Running $LIBTOOLIZE..." - $LIBTOOLIZE --force --copy || exit 1 -fi - -m4dir=`cat $configure_ac | grep '^AC_CONFIG_MACRO_DIR' | -sed -n -e 's/AC_CONFIG_MACRO_DIR(\([^()]*\))/\1/p' | sed -e 's/^\[\(.*\)\]$/\1/' | sed -e 1q` -if [ -n "$m4dir" ]; then - m4dir="-I $m4dir" -fi -printbold "Running $ACLOCAL..." -$ACLOCAL $m4dir || exit 1 - -printbold "Running $AUTOCONF..." -$AUTOCONF || exit 1 -if grep "^A[CM]_CONFIG_HEADER" $configure_ac >/dev/null; then - printbold "Running $AUTOHEADER..." - $AUTOHEADER || exit 1 - # this prevents automake from thinking config.h.in is out of - # date, since autoheader doesn't touch the file if it doesn't - # change. - test -f config.h.in && touch config.h.in -fi - -printbold "Running $AUTOMAKE..." -$AUTOMAKE --gnu --add-missing --force --copy || exit 1 - -echo -printbold "Bootstrap complete, now run \`configure'." diff --git a/libtorrent_utp/bindings/Makefile.am b/libtorrent_utp/bindings/Makefile.am deleted file mode 100644 index 929b41f11..000000000 --- a/libtorrent_utp/bindings/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ - -SUBDIRS = python - -EXTRA_DIST = README.txt diff --git a/libtorrent_utp/bindings/README.txt b/libtorrent_utp/bindings/README.txt deleted file mode 100644 index 977ca8c1c..000000000 --- a/libtorrent_utp/bindings/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -Documentation covering building and using the python binding for libtorrent -is located in the main doc directory. See docs/python_binding.html - diff --git a/libtorrent_utp/bindings/c/Jamfile b/libtorrent_utp/bindings/c/Jamfile deleted file mode 100755 index 1933a901d..000000000 --- a/libtorrent_utp/bindings/c/Jamfile +++ /dev/null @@ -1,38 +0,0 @@ -use-project /torrent : ../.. ; - -rule libtorrent_linking ( properties * ) -{ - local result ; - - if gcc in $(properties) && shared in $(properties) - { - result += on ; - } - -# if gcc in $(properties) || darwin in $(properties) -# { -# result += hidden ; -# } - - return $(result) ; -} - -lib torrentc - - : # sources - library.cpp - - : # requirements - @libtorrent_linking - /torrent//torrent/static - . - - : # default build - static - - : # usage-requirements - . -; - -exe simple_client : simple_client.c torrentc ; - diff --git a/libtorrent_utp/bindings/c/library.cpp b/libtorrent_utp/bindings/c/library.cpp deleted file mode 100644 index b40131537..000000000 --- a/libtorrent_utp/bindings/c/library.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/* - -Copyright (c) 2009, 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/session.hpp" -#include "libtorrent/magnet_uri.hpp" -#include "libtorrent/torrent_handle.hpp" -#include - -#include - -namespace -{ - std::vector handles; - - int find_handle(libtorrent::torrent_handle h) - { - std::vector::const_iterator i - = std::find(handles.begin(), handles.end(), h); - if (i == handles.end()) return -1; - return i - handles.begin(); - } - - libtorrent::torrent_handle get_handle(int i) - { - if (i < 0 || i >= int(handles.size())) return libtorrent::torrent_handle(); - return handles[i]; - } - - int add_handle(libtorrent::torrent_handle const& h) - { - std::vector::iterator i = std::find_if(handles.begin() - , handles.end(), !boost::bind(&libtorrent::torrent_handle::is_valid, _1)); - if (i != handles.end()) - { - *i = h; - return i - handles.begin(); - } - - handles.push_back(h); - return handles.size() - 1; - } - - int set_int_value(void* dst, int* size, int val) - { - if (*size < sizeof(int)) return -2; - *((int*)dst) = val; - *size = sizeof(int); - return 0; - } - - void copy_proxy_setting(libtorrent::proxy_settings* s, proxy_setting const* ps) - { - s->hostname.assign(ps->hostname); - s->port = ps->port; - s->username.assign(ps->username); - s->password.assign(ps->password); - s->type = (libtorrent::proxy_settings::proxy_type)ps->type; - } -} - -extern "C" -{ - -TORRENT_EXPORT void* session_create(int tag, ...) -{ - using namespace libtorrent; - - va_list lp; - va_start(lp, tag); - - fingerprint fing("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0); - std::pair listen_range(-1, -1); - char const* listen_interface = "0.0.0.0"; - int flags = session::start_default_features | session::add_default_plugins; - int alert_mask = alert::error_notification; - - while (tag != TAG_END) - { - switch (tag) - { - case SES_FINGERPRINT: - { - char const* f = va_arg(lp, char const*); - fing.name[0] = f[0]; - fing.name[1] = f[1]; - break; - } - case SES_LISTENPORT: - listen_range.first = va_arg(lp, int); - break; - case SES_LISTENPORT_END: - listen_range.second = va_arg(lp, int); - break; - case SES_VERSION_MAJOR: - fing.major_version = va_arg(lp, int); - break; - case SES_VERSION_MINOR: - fing.minor_version = va_arg(lp, int); - break; - case SES_VERSION_TINY: - fing.revision_version = va_arg(lp, int); - break; - case SES_VERSION_TAG: - fing.tag_version = va_arg(lp, int); - break; - case SES_FLAGS: - flags = va_arg(lp, int); - break; - case SES_ALERT_MASK: - alert_mask = va_arg(lp, int); - break; - case SES_LISTEN_INTERFACE: - listen_interface = va_arg(lp, char const*); - break; - default: - // skip unknown tags - va_arg(lp, void*); - break; - } - - tag = va_arg(lp, int); - } - - if (listen_range.first != -1 && (listen_range.second == -1 - || listen_range.second < listen_range.first)) - listen_range.second = listen_range.first; - - return new (std::nothrow) session(fing, listen_range, listen_interface, flags, alert_mask); -} - -TORRENT_EXPORT void session_close(void* ses) -{ - delete (libtorrent::session*)ses; -} - -TORRENT_EXPORT int session_add_torrent(void* ses, int tag, ...) -{ - using namespace libtorrent; - - va_list lp; - va_start(lp, tag); - session* s = (session*)ses; - add_torrent_params params; - - char const* torrent_data = 0; - int torrent_size = 0; - - char const* resume_data = 0; - int resume_size = 0; - - char const* magnet_url = 0; - - error_code ec; - - while (tag != TAG_END) - { - switch (tag) - { - case TOR_FILENAME: - params.ti = new (std::nothrow) torrent_info(va_arg(lp, char const*), ec); - break; - case TOR_TORRENT: - torrent_data = va_arg(lp, char const*); - break; - case TOR_TORRENT_SIZE: - torrent_size = va_arg(lp, int); - break; - case TOR_INFOHASH: - params.ti = new (std::nothrow) torrent_info(sha1_hash(va_arg(lp, char const*))); - break; - case TOR_INFOHASH_HEX: - { - sha1_hash ih; - from_hex(va_arg(lp, char const*), 40, (char*)&ih[0]); - params.ti = new (std::nothrow) torrent_info(ih); - break; - } - case TOR_MAGNETLINK: - magnet_url = va_arg(lp, char const*); - break; - case TOR_TRACKER_URL: - params.tracker_url = va_arg(lp, char const*); - break; - case TOR_RESUME_DATA: - resume_data = va_arg(lp, char const*); - break; - case TOR_RESUME_DATA_SIZE: - resume_size = va_arg(lp, int); - break; - case TOR_SAVE_PATH: - params.save_path = va_arg(lp, char const*); - break; - case TOR_NAME: - params.name = va_arg(lp, char const*); - break; - case TOR_PAUSED: - params.paused = va_arg(lp, int) != 0; - break; - case TOR_AUTO_MANAGED: - params.auto_managed = va_arg(lp, int) != 0; - break; - case TOR_DUPLICATE_IS_ERROR: - params.duplicate_is_error = va_arg(lp, int) != 0; - break; - case TOR_USER_DATA: - params.userdata = va_arg(lp, void*); - break; - case TOR_SEED_MODE: - params.seed_mode = va_arg(lp, int) != 0; - break; - case TOR_OVERRIDE_RESUME_DATA: - params.override_resume_data = va_arg(lp, int) != 0; - break; - case TOR_STORAGE_MODE: - params.storage_mode = (libtorrent::storage_mode_t)va_arg(lp, int); - break; - default: - // ignore unknown tags - va_arg(lp, void*); - break; - } - - tag = va_arg(lp, int); - } - - if (!params.ti && torrent_data && torrent_size) - params.ti = new (std::nothrow) torrent_info(torrent_data, torrent_size); - - std::vector rd; - if (resume_data && resume_size) - { - rd.assign(resume_data, resume_data + resume_size); - params.resume_data = &rd; - } - torrent_handle h; - if (!params.ti && magnet_url) - { - h = add_magnet_uri(*s, magnet_url, params, ec); - } - else - { - h = s->add_torrent(params, ec); - } - - if (!h.is_valid()) - { - return -1; - } - - int i = find_handle(h); - if (i == -1) i = add_handle(h); - - return i; -} - -TORRENT_EXPORT void session_remove_torrent(void* ses, int tor, int flags) -{ - using namespace libtorrent; - torrent_handle h = get_handle(tor); - if (!h.is_valid()) return; - - session* s = (session*)ses; - s->remove_torrent(h, flags); -} - -TORRENT_EXPORT int session_pop_alert(void* ses, char* dest, int len, int* category) -{ - using namespace libtorrent; - - session* s = (session*)ses; - - std::auto_ptr a = s->pop_alert(); - if (!a.get()) return -1; - - if (category) *category = a->category(); - strncpy(dest, a->message().c_str(), len - 1); - dest[len - 1] = 0; - - return 0; // for now -} - -TORRENT_EXPORT int session_set_settings(void* ses, int tag, ...) -{ - using namespace libtorrent; - - session* s = (session*)ses; - - va_list lp; - va_start(lp, tag); - - while (tag != TAG_END) - { - switch (tag) - { - case SET_UPLOAD_RATE_LIMIT: - s->set_upload_rate_limit(va_arg(lp, int)); - break; - case SET_DOWNLOAD_RATE_LIMIT: - s->set_download_rate_limit(va_arg(lp, int)); - break; - case SET_LOCAL_UPLOAD_RATE_LIMIT: - s->set_local_upload_rate_limit(va_arg(lp, int)); - break; - case SET_LOCAL_DOWNLOAD_RATE_LIMIT: - s->set_local_download_rate_limit(va_arg(lp, int)); - break; - case SET_MAX_UPLOAD_SLOTS: - s->set_max_uploads(va_arg(lp, int)); - break; - case SET_MAX_CONNECTIONS: - s->set_max_connections(va_arg(lp, int)); - break; - case SET_HALF_OPEN_LIMIT: - s->set_max_half_open_connections(va_arg(lp, int)); - break; - case SET_PEER_PROXY: - { - libtorrent::proxy_settings ps; - copy_proxy_setting(&ps, va_arg(lp, struct proxy_setting const*)); - s->set_peer_proxy(ps); - } - case SET_WEB_SEED_PROXY: - { - libtorrent::proxy_settings ps; - copy_proxy_setting(&ps, va_arg(lp, struct proxy_setting const*)); - s->set_web_seed_proxy(ps); - } - case SET_TRACKER_PROXY: - { - libtorrent::proxy_settings ps; - copy_proxy_setting(&ps, va_arg(lp, struct proxy_setting const*)); - s->set_tracker_proxy(ps); - } - case SET_ALERT_MASK: - { - s->set_alert_mask(va_arg(lp, int)); - } -#ifndef TORRENT_DISABLE_DHT - case SET_DHT_PROXY: - { - libtorrent::proxy_settings ps; - copy_proxy_setting(&ps, va_arg(lp, struct proxy_setting const*)); - s->set_dht_proxy(ps); - } -#endif - case SET_PROXY: - { - libtorrent::proxy_settings ps; - copy_proxy_setting(&ps, va_arg(lp, struct proxy_setting const*)); - s->set_peer_proxy(ps); - s->set_web_seed_proxy(ps); - s->set_tracker_proxy(ps); -#ifndef TORRENT_DISABLE_DHT - s->set_dht_proxy(ps); -#endif - } - default: - // ignore unknown tags - va_arg(lp, void*); - break; - } - - tag = va_arg(lp, int); - } - return 0; -} - -TORRENT_EXPORT int session_get_setting(void* ses, int tag, void* value, int* value_size) -{ - using namespace libtorrent; - session* s = (session*)ses; - - switch (tag) - { - case SET_UPLOAD_RATE_LIMIT: - return set_int_value(value, value_size, s->upload_rate_limit()); - case SET_DOWNLOAD_RATE_LIMIT: - return set_int_value(value, value_size, s->download_rate_limit()); - case SET_LOCAL_UPLOAD_RATE_LIMIT: - return set_int_value(value, value_size, s->local_upload_rate_limit()); - case SET_LOCAL_DOWNLOAD_RATE_LIMIT: - return set_int_value(value, value_size, s->local_download_rate_limit()); - case SET_MAX_UPLOAD_SLOTS: - return set_int_value(value, value_size, s->max_uploads()); - case SET_MAX_CONNECTIONS: - return set_int_value(value, value_size, s->max_connections()); - case SET_HALF_OPEN_LIMIT: - return set_int_value(value, value_size, s->max_half_open_connections()); - default: - return -2; - } -} - -TORRENT_EXPORT int session_get_status(void* sesptr, struct session_status* s, int struct_size) -{ - libtorrent::session* ses = (libtorrent::session*)sesptr; - - libtorrent::session_status ss = ses->status(); - if (struct_size != sizeof(session_status)) return -1; - - s->has_incoming_connections = ss.has_incoming_connections; - - s->upload_rate = ss.upload_rate; - s->download_rate = ss.download_rate; - s->total_download = ss.total_download; - s->total_upload = ss.total_upload; - - s->payload_upload_rate = ss.payload_upload_rate; - s->payload_download_rate = ss.payload_download_rate; - s->total_payload_download = ss.total_payload_download; - s->total_payload_upload = ss.total_payload_upload; - - s->ip_overhead_upload_rate = ss.ip_overhead_upload_rate; - s->ip_overhead_download_rate = ss.ip_overhead_download_rate; - s->total_ip_overhead_download = ss.total_ip_overhead_download; - s->total_ip_overhead_upload = ss.total_ip_overhead_upload; - - s->dht_upload_rate = ss.dht_upload_rate; - s->dht_download_rate = ss.dht_download_rate; - s->total_dht_download = ss.total_dht_download; - s->total_dht_upload = ss.total_dht_upload; - - s->tracker_upload_rate = ss.tracker_upload_rate; - s->tracker_download_rate = ss.tracker_download_rate; - s->total_tracker_download = ss.total_tracker_download; - s->total_tracker_upload = ss.total_tracker_upload; - - s->total_redundant_bytes = ss.total_redundant_bytes; - s->total_failed_bytes = ss.total_failed_bytes; - - s->num_peers = ss.num_peers; - s->num_unchoked = ss.num_unchoked; - s->allowed_upload_slots = ss.allowed_upload_slots; - - s->up_bandwidth_queue = ss.up_bandwidth_queue; - s->down_bandwidth_queue = ss.down_bandwidth_queue; - - s->up_bandwidth_bytes_queue = ss.up_bandwidth_bytes_queue; - s->down_bandwidth_bytes_queue = ss.down_bandwidth_bytes_queue; - - s->optimistic_unchoke_counter = ss.optimistic_unchoke_counter; - s->unchoke_counter = ss.unchoke_counter; - - s->dht_nodes = ss.dht_nodes; - s->dht_node_cache = ss.dht_node_cache; - s->dht_torrents = ss.dht_torrents; - s->dht_global_nodes = ss.dht_global_nodes; - return 0; -} - -TORRENT_EXPORT int torrent_get_status(int tor, torrent_status* s, int struct_size) -{ - libtorrent::torrent_handle h = get_handle(tor); - if (!h.is_valid()) return -1; - - libtorrent::torrent_status ts = h.status(); - - if (struct_size != sizeof(torrent_status)) return -1; - - s->state = (state_t)ts.state; - s->paused = ts.paused; - s->progress = ts.progress; - strncpy(s->error, ts.error.c_str(), 1025); - s->next_announce = ts.next_announce.total_seconds(); - s->announce_interval = ts.announce_interval.total_seconds(); - strncpy(s->current_tracker, ts.current_tracker.c_str(), 512); - s->total_download = ts.total_download = ts.total_download = ts.total_download; - s->total_upload = ts.total_upload = ts.total_upload = ts.total_upload; - s->total_payload_download = ts.total_payload_download; - s->total_payload_upload = ts.total_payload_upload; - s->total_failed_bytes = ts.total_failed_bytes; - s->total_redundant_bytes = ts.total_redundant_bytes; - s->download_rate = ts.download_rate; - s->upload_rate = ts.upload_rate; - s->download_payload_rate = ts.download_payload_rate; - s->upload_payload_rate = ts.upload_payload_rate; - s->num_seeds = ts.num_seeds; - s->num_peers = ts.num_peers; - s->num_complete = ts.num_complete; - s->num_incomplete = ts.num_incomplete; - s->list_seeds = ts.list_seeds; - s->list_peers = ts.list_peers; - s->connect_candidates = ts.connect_candidates; - s->num_pieces = ts.num_pieces; - s->total_done = ts.total_done; - s->total_wanted_done = ts.total_wanted_done; - s->total_wanted = ts.total_wanted; - s->distributed_copies = ts.distributed_copies; - s->block_size = ts.block_size; - s->num_uploads = ts.num_uploads; - s->num_connections = ts.num_connections; - s->uploads_limit = ts.uploads_limit; - s->connections_limit = ts.connections_limit; -// s->storage_mode = (storage_mode_t)ts.storage_mode; - s->up_bandwidth_queue = ts.up_bandwidth_queue; - s->down_bandwidth_queue = ts.down_bandwidth_queue; - s->all_time_upload = ts.all_time_upload; - s->all_time_download = ts.all_time_download; - s->active_time = ts.active_time; - s->seeding_time = ts.seeding_time; - s->seed_rank = ts.seed_rank; - s->last_scrape = ts.last_scrape; - s->has_incoming = ts.has_incoming; - s->sparse_regions = ts.sparse_regions; - s->seed_mode = ts.seed_mode; - return 0; -} - -TORRENT_EXPORT int torrent_set_settings(int tor, int tag, ...) -{ - using namespace libtorrent; - torrent_handle h = get_handle(tor); - if (!h.is_valid()) return -1; - - va_list lp; - va_start(lp, tag); - - while (tag != TAG_END) - { - switch (tag) - { - case SET_UPLOAD_RATE_LIMIT: - h.set_upload_limit(va_arg(lp, int)); - break; - case SET_DOWNLOAD_RATE_LIMIT: - h.set_download_limit(va_arg(lp, int)); - break; - case SET_MAX_UPLOAD_SLOTS: - h.set_max_uploads(va_arg(lp, int)); - break; - case SET_MAX_CONNECTIONS: - h.set_max_connections(va_arg(lp, int)); - break; - case SET_SEQUENTIAL_DOWNLOAD: - h.set_sequential_download(va_arg(lp, int) != 0); - break; - case SET_SUPER_SEEDING: - h.super_seeding(va_arg(lp, int) != 0); - break; - default: - // ignore unknown tags - va_arg(lp, void*); - break; - } - - tag = va_arg(lp, int); - } - return 0; -} - -TORRENT_EXPORT int torrent_get_setting(int tor, int tag, void* value, int* value_size) -{ - using namespace libtorrent; - torrent_handle h = get_handle(tor); - if (!h.is_valid()) return -1; - - switch (tag) - { - case SET_UPLOAD_RATE_LIMIT: - return set_int_value(value, value_size, h.upload_limit()); - case SET_DOWNLOAD_RATE_LIMIT: - return set_int_value(value, value_size, h.download_limit()); - case SET_MAX_UPLOAD_SLOTS: - return set_int_value(value, value_size, h.max_uploads()); - case SET_MAX_CONNECTIONS: - return set_int_value(value, value_size, h.max_connections()); - case SET_SEQUENTIAL_DOWNLOAD: - return set_int_value(value, value_size, h.is_sequential_download()); - case SET_SUPER_SEEDING: - return set_int_value(value, value_size, h.super_seeding()); - default: - return -2; - } -} - -} // extern "C" - diff --git a/libtorrent_utp/bindings/c/libtorrent.h b/libtorrent_utp/bindings/c/libtorrent.h deleted file mode 100644 index c80906d05..000000000 --- a/libtorrent_utp/bindings/c/libtorrent.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - -Copyright (c) 2009, 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 LIBTORRENT_H -#define LIBTORRENT_H - -enum tags -{ - TAG_END = 0, - - SES_FINGERPRINT, // char const*, 2 character string - SES_LISTENPORT, // int - SES_LISTENPORT_END, // int - SES_VERSION_MAJOR, // int - SES_VERSION_MINOR, // int - SES_VERSION_TINY, // int - SES_VERSION_TAG, // int - SES_FLAGS, // int - SES_ALERT_MASK, // int - SES_LISTEN_INTERFACE, // char const* - - // === add_torrent tags === - - // identifying the torrent to add - TOR_FILENAME = 0x100, // char const* - TOR_TORRENT, // char const*, specify size of buffer with TOR_TORRENT_SIZE - TOR_TORRENT_SIZE, // int - TOR_INFOHASH, // char const*, must point to a 20 byte array - TOR_INFOHASH_HEX, // char const*, must point to a 40 byte string - TOR_MAGNETLINK, // char const*, url - - TOR_TRACKER_URL, // char const* - TOR_RESUME_DATA, // char const* - TOR_RESUME_DATA_SIZE, // int - TOR_SAVE_PATH, // char const* - TOR_NAME, // char const* - TOR_PAUSED, // int - TOR_AUTO_MANAGED, // int - TOR_DUPLICATE_IS_ERROR, // int - TOR_USER_DATA, //void* - TOR_SEED_MODE, // int - TOR_OVERRIDE_RESUME_DATA, // int - TOR_STORAGE_MODE, // int - - SET_UPLOAD_RATE_LIMIT = 0x200, // int - SET_DOWNLOAD_RATE_LIMIT, // int - SET_LOCAL_UPLOAD_RATE_LIMIT, // int - SET_LOCAL_DOWNLOAD_RATE_LIMIT, // int - SET_MAX_UPLOAD_SLOTS, // int - SET_MAX_CONNECTIONS, // int - SET_SEQUENTIAL_DOWNLOAD, // int, torrent only - SET_SUPER_SEEDING, // int, torrent only - SET_HALF_OPEN_LIMIT, // int, session only - SET_PEER_PROXY, // proxy_setting const*, session_only - SET_WEB_SEED_PROXY, // proxy_setting const*, session_only - SET_TRACKER_PROXY, // proxy_setting const*, session_only - SET_DHT_PROXY, // proxy_setting const*, session_only - SET_PROXY, // proxy_setting const*, session_only - SET_ALERT_MASK, // int, session_only -}; - -struct proxy_setting -{ - char hostname[256]; - int port; - - char username[256]; - char password[256]; - - int type; -}; - -enum category_t -{ - cat_error = 0x1, - cat_peer = 0x2, - cat_port_mapping = 0x4, - cat_storage = 0x8, - cat_tracker = 0x10, - cat_debug = 0x20, - cat_status = 0x40, - cat_progress = 0x80, - cat_ip_block = 0x100, - cat_performance_warning = 0x200, - cat_dht = 0x400, - - cat_all_categories = 0xffffffff -}; - -enum proxy_type_t -{ - proxy_none, - proxy_socks4, - proxy_socks5, - proxy_socks5_pw, - proxy_http, - proxy_http_pw -}; - -enum storage_mode_t -{ - storage_mode_allocate = 0, - storage_mode_sparse, - storage_mode_compact -}; - -enum state_t -{ - queued_for_checking, - checking_files, - downloading_metadata, - downloading, - finished, - seeding, - allocating, - checking_resume_data -}; - -struct torrent_status -{ - enum state_t state; - int paused; - float progress; - char error[1024]; - int next_announce; - int announce_interval; - char current_tracker[512]; - long long total_download; - long long total_upload; - long long total_payload_download; - long long total_payload_upload; - long long total_failed_bytes; - long long total_redundant_bytes; - float download_rate; - float upload_rate; - float download_payload_rate; - float upload_payload_rate; - int num_seeds; - int num_peers; - int num_complete; - int num_incomplete; - int list_seeds; - int list_peers; - int connect_candidates; - - // what to do? -// bitfield pieces; - - int num_pieces; - long long total_done; - long long total_wanted_done; - long long total_wanted; - float distributed_copies; - int block_size; - int num_uploads; - int num_connections; - int uploads_limit; - int connections_limit; -// enum storage_mode_t storage_mode; - int up_bandwidth_queue; - int down_bandwidth_queue; - long long all_time_upload; - long long all_time_download; - int active_time; - int seeding_time; - int seed_rank; - int last_scrape; - int has_incoming; - int sparse_regions; - int seed_mode; -}; - -struct session_status -{ - int has_incoming_connections; - - float upload_rate; - float download_rate; - long long total_download; - long long total_upload; - - float payload_upload_rate; - float payload_download_rate; - long long total_payload_download; - long long total_payload_upload; - - float ip_overhead_upload_rate; - float ip_overhead_download_rate; - long long total_ip_overhead_download; - long long total_ip_overhead_upload; - - float dht_upload_rate; - float dht_download_rate; - long long total_dht_download; - long long total_dht_upload; - - float tracker_upload_rate; - float tracker_download_rate; - long long total_tracker_download; - long long total_tracker_upload; - - long long total_redundant_bytes; - long long total_failed_bytes; - - int num_peers; - int num_unchoked; - int allowed_upload_slots; - - int up_bandwidth_queue; - int down_bandwidth_queue; - - int up_bandwidth_bytes_queue; - int down_bandwidth_bytes_queue; - - int optimistic_unchoke_counter; - int unchoke_counter; - - int dht_nodes; - int dht_node_cache; - int dht_torrents; - long long dht_global_nodes; -// std::vector active_requests; -}; - -#ifdef __cplusplus -extern "C" -{ -#endif - -// the functions whose signature ends with: -// , int first_tag, ...); -// takes a tag list. The tag list is a series -// of tag-value pairs. The tags are constants -// identifying which property the value controls. -// The type of the value varies between tags. -// The enumeration above specifies which type -// it expects. All tag lists must always be -// terminated by TAG_END. - -// use SES_* tags in tag list -void* session_create(int first_tag, ...); -void session_close(void* ses); - -// use TOR_* tags in tag list -int session_add_torrent(void* ses, int first_tag, ...); -void session_remove_torrent(void* ses, int tor, int flags); - -// return < 0 if there are no alerts. Otherwise returns the -// type of alert that was returned -int session_pop_alert(void* ses, char* dest, int len, int* category); - -int session_get_status(void* ses, struct session_status* s, int struct_size); - -// use SET_* tags in tag list -int session_set_settings(void* ses, int first_tag, ...); -int session_get_setting(void* ses, int tag, void* value, int* value_size); - -int torrent_get_status(int tor, struct torrent_status* s, int struct_size); - -// use SET_* tags in tag list -int torrent_set_settings(int tor, int first_tag, ...); -int torrent_get_setting(int tor, int tag, void* value, int* value_size); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/libtorrent_utp/bindings/c/simple_client.c b/libtorrent_utp/bindings/c/simple_client.c deleted file mode 100644 index 3c1d7a2be..000000000 --- a/libtorrent_utp/bindings/c/simple_client.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - -Copyright (c) 2009, 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 - -int quit = 0; - -void stop(int signal) -{ - quit = 1; -} - -int main(int argc, char* argv[]) -{ - if (argc != 2) - { - fprintf(stderr, "usage: ./simple_client torrent-file\n"); - return 1; - } - - int ret = 0; - void* ses = session_create( - SES_LISTENPORT, 6881, - SES_LISTENPORT_END, 6889, - SES_ALERT_MASK, ~(cat_progress | cat_port_mapping | cat_debug | cat_performance_warning | cat_peer), - TAG_END); - - int t = session_add_torrent(ses, - TOR_FILENAME, argv[1], - TOR_SAVE_PATH, "./", - TAG_END); - - if (t < 0) - { - fprintf(stderr, "Failed to add torrent\n"); - ret = 1; - goto exit; - } - - struct torrent_status st; - - printf("press ctrl-C to stop\n"); - - signal(SIGINT, &stop); - signal(SIGABRT, &stop); - signal(SIGQUIT, &stop); - - while (quit == 0) - { - char const* message = ""; - - char const* state[] = {"queued", "checking", "downloading metadata" - , "downloading", "finished", "seeding", "allocating" - , "checking_resume_data"}; - - if (torrent_get_status(t, &st, sizeof(st)) < 0) break; - printf("\r%3.f%% %d kB (%5.f kB/s) up: %d kB (%5.f kB/s) peers: %d '%s' %s " - , (double)st.progress * 100. - , (int)(st.total_payload_download / 1000) - , (double)st.download_payload_rate / 1000. - , (int)(st.total_payload_upload / 1000) - , (double)st.upload_payload_rate / 1000. - , st.num_peers - , state[st.state] - , message); - - - char msg[400]; - while (session_pop_alert(ses, msg, sizeof(msg), 0) >= 0) - { - printf("%s\n", msg); - } - - if (strlen(st.error) > 0) - { - fprintf(stderr, "\nERROR: %s\n", st.error); - break; - } - - fflush(stdout); - usleep(1000000); - } - printf("\nclosing\n"); - -exit: - - session_close(ses); - return ret; -} - diff --git a/libtorrent_utp/bindings/python/Jamfile b/libtorrent_utp/bindings/python/Jamfile deleted file mode 100755 index 290524001..000000000 --- a/libtorrent_utp/bindings/python/Jamfile +++ /dev/null @@ -1,66 +0,0 @@ -import python ; - -use-project /torrent : ../.. ; - -lib boost_python : : darwin boost_python-mt $(boost-library-search-path) ; -lib boost_python : : boost_python ; - -rule libtorrent_linking ( properties * ) -{ - local result ; - - if gcc in $(properties) - { - result += on ; - } - -# if gcc in $(properties) || darwin in $(properties) -# { -# result += hidden ; -# } - - # when building peer_plugin.cpp on msvc-7.1 it fails - # running out of internal heap space. Don't add it - # to windows build, since it's not critical anyway - if msvc in $(properties) - { - result += TORRENT_NO_PYTHON_PLUGINS ; - } - else - { - result += src/peer_plugin.cpp ; - } - return $(result) ; -} - -python-extension libtorrent - : src/module.cpp - src/big_number.cpp - src/converters.cpp - src/create_torrent.cpp - src/fingerprint.cpp - src/utility.cpp - src/session.cpp - src/entry.cpp - src/torrent_info.cpp - src/torrent_handle.cpp - src/torrent_status.cpp - src/session_settings.cpp - src/version.cpp - src/alert.cpp - src/datetime.cpp - src/extensions.cpp - src/torrent.cpp - src/peer_info.cpp - src/ip_filter.cpp - src/magnet_uri.cpp - : src - /torrent//torrent/static - system:boost_python - source,static:/boost/python//boost_python/static - source,shared:/boost/python//boost_python/shared - @libtorrent_linking - ; - -install stage_module : libtorrent : . ; - diff --git a/libtorrent_utp/bindings/python/Makefile.am b/libtorrent_utp/bindings/python/Makefile.am deleted file mode 100644 index 4bc8b9c50..000000000 --- a/libtorrent_utp/bindings/python/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ - -EXTRA_DIST = \ - Jamfile \ - setup.py \ - client.py \ - simple_client.py \ - src/alert.cpp \ - src/big_number.cpp \ - src/converters.cpp \ - src/create_torrent.cpp \ - src/datetime.cpp \ - src/entry.cpp \ - src/extensions.cpp \ - src/fingerprint.cpp \ - src/gil.hpp \ - src/ip_filter.cpp \ - src/magnet_uri.cpp \ - src/module.cpp \ - src/optional.hpp \ - src/peer_info.cpp \ - src/peer_plugin.cpp \ - src/session.cpp \ - src/session_settings.cpp \ - src/torrent.cpp \ - src/torrent_handle.cpp \ - src/torrent_info.cpp \ - src/torrent_status.cpp \ - src/utility.cpp \ - src/version.cpp - -if ENABLE_PYTHON_BINDING - -all-local: - $(PYTHON) setup.py build - -install-exec-local: - $(PYTHON) setup.py install @PYTHON_INSTALL_PARAMS@ - -uninstall-local: - rm -rf $(DESTDIR)$(libdir)/python*/*-packages/*libtorrent* - -clean-local: - $(PYTHON) setup.py clean --all - -endif diff --git a/libtorrent_utp/bindings/python/client.py b/libtorrent_utp/bindings/python/client.py deleted file mode 100755 index c8898bedd..000000000 --- a/libtorrent_utp/bindings/python/client.py +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/python - -# Copyright Daniel Wallin 2006. 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) - -import sys -import libtorrent as lt -import time -import os.path -import sys - -class WindowsConsole: - def __init__(self): - self.console = Console.getconsole() - - def clear(self): - self.console.page() - - def write(self, str): - self.console.write(str) - - def sleep_and_input(self, seconds): - time.sleep(seconds) - if msvcrt.kbhit(): - return msvcrt.getch() - return None - -class UnixConsole: - def __init__(self): - self.fd = sys.stdin - self.old = termios.tcgetattr(self.fd.fileno()) - new = termios.tcgetattr(self.fd.fileno()) - new[3] = new[3] & ~termios.ICANON - new[6][termios.VTIME] = 0 - new[6][termios.VMIN] = 1 - termios.tcsetattr(self.fd.fileno(), termios.TCSADRAIN, new) - - sys.exitfunc = self._onexit - - def _onexit(self): - termios.tcsetattr(self.fd.fileno(), termios.TCSADRAIN, self.old) - - def clear(self): - sys.stdout.write('\033[2J\033[0;0H') - sys.stdout.flush() - - def write(self, str): - sys.stdout.write(str) - sys.stdout.flush() - - def sleep_and_input(self, seconds): - read,_,_ = select.select([self.fd.fileno()], [], [], seconds) - if len(read) > 0: - return self.fd.read(1) - return None - -if os.name == 'nt': - import Console - import msvcrt -else: - import termios - import select - -class PythonExtension(lt.torrent_plugin): - def __init__(self, alerts): - lt.torrent_plugin.__init__(self) - self.alerts = alerts - self.alerts.append('PythonExtension') - self.count = 0 - - def on_piece_pass(self, index): - self.alerts.append('got piece %d' % index) - - def on_piece_failed(self, index): - self.alerts.append('failed piece %d' % index) - - def tick(self): - self.count += 1 - if self.count >= 10: - self.count = 0 - self.alerts.append('PythonExtension tick') - -def write_line(console, line): - console.write(line) - -def add_suffix(val): - prefix = ['B', 'kB', 'MB', 'GB', 'TB'] - for i in range(len(prefix)): - if abs(val) < 1000: - if i == 0: - return '%5.3g%s' % (val, prefix[i]) - else: - return '%4.3g%s' % (val, prefix[i]) - val /= 1000 - - return '%6.3gPB' % val - -def progress_bar(progress, width): - assert(progress <= 1) - progress_chars = int(progress * width + 0.5) - return progress_chars * '#' + (width - progress_chars) * '-' - -def print_peer_info(console, peers): - - out = ' down (total ) up (total ) q r flags block progress client\n' - - for p in peers: - - out += '%s/s ' % add_suffix(p.down_speed) - out += '(%s) ' % add_suffix(p.total_download) - out += '%s/s ' % add_suffix(p.up_speed) - out += '(%s) ' % add_suffix(p.total_upload) - out += '%2d ' % p.download_queue_length - out += '%2d ' % p.upload_queue_length - - if p.flags & lt.peer_info.interesting: out += 'I' - else: out += '.' - if p.flags & lt.peer_info.choked: out += 'C' - else: out += '.' - if p.flags & lt.peer_info.remote_interested: out += 'i' - else: out += '.' - if p.flags & lt.peer_info.remote_choked: out += 'c' - else: out += '.' - if p.flags & lt.peer_info.supports_extensions: out += 'e' - else: out += '.' - if p.flags & lt.peer_info.local_connection: out += 'l' - else: out += 'r' - out += ' ' - - if p.downloading_piece_index >= 0: - assert(p.downloading_progress <= p.downloading_total) - out += progress_bar(float(p.downloading_progress) / p.downloading_total, 15) - else: - out += progress_bar(0, 15) - out += ' ' - - if p.flags & lt.peer_info.handshake: - id = 'waiting for handshake' - elif p.flags & lt.peer_info.connecting: - id = 'connecting to peer' - elif p.flags & lt.peer_info.queued: - id = 'queued' - else: - id = p.client - - out += '%s\n' % id[:10] - - write_line(console, out) - - -def print_download_queue(console, download_queue): - - out = "" - - for e in download_queue: - out += '%4d: [' % e['piece_index']; - for b in e['blocks']: - s = b['state'] - if s == 3: - out += '#' - elif s == 2: - out += '=' - elif s == 1: - out += '-' - else: - out += ' ' - out += ']\n' - - write_line(console, out) - -def main(): - from optparse import OptionParser - - parser = OptionParser() - - parser.add_option('-p', '--port', - type='int', help='set listening port') - - parser.add_option('-r', '--ratio', - type='float', help='set the preferred upload/download ratio. 0 means infinite. Values smaller than 1 are clamped to 1') - - parser.add_option('-d', '--max-download-rate', - type='float', help='the maximum download rate given in kB/s. 0 means infinite.') - - parser.add_option('-u', '--max-upload-rate', - type='float', help='the maximum upload rate given in kB/s. 0 means infinite.') - - parser.add_option('-s', '--save-path', - type='string', help='the path where the downloaded file/folder should be placed.') - - parser.add_option('-a', '--allocation-mode', - type='string', help='sets mode used for allocating the downloaded files on disk. Possible options are [full | compact]') - - parser.set_defaults( - port=6881 - , ratio=0 - , max_download_rate=0 - , max_upload_rate=0 - , save_path='./' - , allocation_mode='compact' - ) - - (options, args) = parser.parse_args() - - if options.port < 0 or options.port > 65525: - options.port = 6881 - - options.max_upload_rate *= 1000 - options.max_download_rate *= 1000 - - if options.max_upload_rate <= 0: - options.max_upload_rate = -1 - if options.max_download_rate <= 0: - options.max_download_rate = -1 - - compact_allocation = options.allocation_mode == 'compact' - - settings = lt.session_settings() - settings.user_agent = 'python_client/' + lt.version - - ses = lt.session() - ses.set_download_rate_limit(int(options.max_download_rate)) - ses.set_upload_rate_limit(int(options.max_upload_rate)) - ses.listen_on(options.port, options.port + 10) - ses.set_settings(settings) -# ses.set_severity_level(lt.alert.severity_levels.info) - ses.add_extension(lt.create_ut_pex_plugin) - ses.add_extension(lt.create_ut_metadata_plugin) - ses.add_extension(lt.create_metadata_plugin) - - handles = [] - alerts = [] - - # Extensions - # ses.add_extension(lambda x: PythonExtension(alerts)) - - for f in args: - e = lt.bdecode(open(f, 'rb').read()) - info = lt.torrent_info(e) - print 'Adding \'%s\'...' % info.name() - - atp = {} - try: - atp["resume_data"] = open(os.path.join(options.save_path, info.name() + '.fastresume'), 'rb').read() - except: - pass - - atp["ti"] = info - atp["save_path"] = options.save_path - atp["storage_mode"] = lt.storage_mode_t.storage_mode_sparse - atp["paused"] = False - atp["auto_managed"] = True - atp["duplicate_is_error"] = True - - h = ses.add_torrent(atp) - - handles.append(h) - - h.set_max_connections(60) - h.set_max_uploads(-1) - h.set_ratio(options.ratio) - - if os.name == 'nt': - console = WindowsConsole() - else: - console = UnixConsole() - - alive = True - while alive: - console.clear() - - out = '' - - for h in handles: - if h.has_metadata(): - name = h.get_torrent_info().name()[:40] - else: - name = '-' - out += 'name: %-40s\n' % name - - s = h.status() - - if s.state != lt.torrent_status.seeding: - state_str = ['queued', 'checking', 'downloading metadata', \ - 'downloading', 'finished', 'seeding', \ - 'allocating', 'checking fastresume'] - out += state_str[s.state] + ' ' - - out += '%5.4f%% ' % (s.progress*100) - out += progress_bar(s.progress, 49) - out += '\n' - - out += 'total downloaded: %d Bytes\n' % s.total_done - out += 'peers: %d seeds: %d distributed copies: %d\n' % \ - (s.num_peers, s.num_seeds, s.distributed_copies) - out += '\n' - - out += 'download: %s/s (%s) ' \ - % (add_suffix(s.download_rate), add_suffix(s.total_download)) - out += 'upload: %s/s (%s) ' \ - % (add_suffix(s.upload_rate), add_suffix(s.total_upload)) - out += 'ratio: %s\n' % '0' - - if s.state != lt.torrent_status.seeding: - out += 'info-hash: %s\n' % h.info_hash() - out += 'next announce: %s\n' % s.next_announce - out += 'tracker: %s\n' % s.current_tracker - - write_line(console, out) - - print_peer_info(console, h.get_peer_info()) - print_download_queue(console, h.get_download_queue()) - - if True and s.state != lt.torrent_status.seeding: - out = '\n' - fp = h.file_progress() - ti = h.get_torrent_info() - for f,p in zip(ti.files(), fp): - out += progress_bar(p / f.size, 20) - out += ' ' + f.path + '\n' - write_line(console, out) - - write_line(console, 76 * '-' + '\n') - write_line(console, '(q)uit), (p)ause), (u)npause), (r)eannounce\n') - write_line(console, 76 * '-' + '\n') - - while 1: - a = ses.pop_alert() - if not a: break - alerts.append(a) - - if len(alerts) > 8: - del alerts[:len(alerts) - 8] - - for a in alerts: - if type(a) == str: - write_line(console, a + '\n') - else: - write_line(console, a.message() + '\n') - - c = console.sleep_and_input(0.5) - - if not c: - continue - - if c == 'r': - for h in handles: h.force_reannounce() - elif c == 'q': - alive = False - elif c == 'p': - for h in handles: h.pause() - elif c == 'u': - for h in handles: h.resume() - - ses.pause() - for h in handles: - if not h.is_valid() or not h.has_metadata(): - continue - data = lt.bencode(h.write_resume_data()) - open(os.path.join(options.save_path, h.get_torrent_info().name() + '.fastresume'), 'wb').write(data) - -main() - diff --git a/libtorrent_utp/bindings/python/setup.py.in b/libtorrent_utp/bindings/python/setup.py.in deleted file mode 100644 index 620f2e866..000000000 --- a/libtorrent_utp/bindings/python/setup.py.in +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python - -from distutils import sysconfig -from distutils.core import setup, Extension -import os -import platform -import sys - -if '@BOOST_PYTHON_LIB@' == '': - print 'You need to pass --enable-python-binding to configure in order ', - print 'to properly use this setup. There is no boost.python library configured now' - sys.exit(1) - -def parse_cmd(cmdline, prefix, keep_prefix = False): - ret = [] - for token in cmdline.split(): - if token[:len(prefix)] == prefix: - if keep_prefix: - ret.append(token) - else: - ret.append(token[len(prefix):]) - return ret - -def arch(): - if platform.system() != 'Darwin': return [] - a = os.uname()[4] - if a == 'Power Macintosh': a = 'ppc' - return ['-arch', a] - -if platform.system() == 'Windows': -# on windows, build using bjam and build an installer - import shutil - # msvc 9.0 (2008) is the official windows compiler for python 2.6 - # http://docs.python.org/whatsnew/2.6.html#build-and-c-api-changes - if os.system('bjam boost=source link=static geoip=static boost-link=static release msvc-9.0 optimization=space') != 0: - print 'build failed' - sys.exit(1) - try: os.mkdir(r'build') - except: pass - try: os.mkdir(r'build\lib') - except: pass - try: os.mkdir(r'libtorrent') - except: pass - shutil.copyfile(r'bin\msvc-9.0\release\boost-source\geoip-static\link-static\optimization-space\threading-multi\libtorrent.pyd', r'.\build\lib\libtorrent.pyd') - setup( name='python-libtorrent', - version='@PACKAGE_VERSION@', - author = 'Arvid Norberg', - author_email='@PACKAGE_BUGREPORT@', - description = 'Python bindings for libtorrent-rasterbar', - long_description = 'Python bindings for libtorrent-rasterbar', - url = 'http://www.rasterbar.com/products/libtorrent/index.html', - platforms = 'Windows', - license = 'Boost Software License - Version 1.0 - August 17th, 2003', - packages = ['libtorrent'], - ) - sys.exit(0) - -config_vars = sysconfig.get_config_vars() -if "CFLAGS" in config_vars and "-Wstrict-prototypes" in config_vars["CFLAGS"]: - config_vars["CFLAGS"] = config_vars["CFLAGS"].replace("-Wstrict-prototypes", " ") -if "OPT" in config_vars and "-Wstrict-prototypes" in config_vars["OPT"]: - config_vars["OPT"] = config_vars["OPT"].replace("-Wstrict-prototypes", " ") - -source_list = os.listdir(os.path.join(os.path.dirname(__file__), "src")) -source_list = [os.path.join("src", s) for s in source_list if s.endswith(".cpp")] - -extra_cmd = '@COMPILETIME_OPTIONS@ @CPPFLAGS@ @LIBS@ @BOOST_SYSTEM_LIB@ @BOOST_PYTHON_LIB@ @PTHREAD_LIBS@ @OPENSSL_LIBS@ @OPENSSL_LDFLAGS@ @OPENSSL_INCLUDES@' - -setup( name='python-libtorrent', - version='@PACKAGE_VERSION@', - author = 'Arvid Norberg', - author_email='@PACKAGE_BUGREPORT@', - description = 'Python bindings for libtorrent-rasterbar', - long_description = 'Python bindings for libtorrent-rasterbar', - url = 'http://www.rasterbar.com/products/libtorrent/index.html', - platforms = 'any', - license = 'Boost Software License - Version 1.0 - August 17th, 2003', - ext_modules = [Extension('libtorrent', - sources = source_list, - language='c++', - include_dirs = ['@top_srcdir@/include'] + parse_cmd(extra_cmd, '-I'), - library_dirs = ['@top_builddir@/src/.libs'] + parse_cmd(extra_cmd, '-L'), - extra_link_args = '@LDFLAGS@'.split() + arch(), - extra_compile_args = parse_cmd(extra_cmd, '-D', True) + arch(), - libraries = ['torrent-rasterbar'] + parse_cmd(extra_cmd, '-l'))], -) diff --git a/libtorrent_utp/bindings/python/simple_client.py b/libtorrent_utp/bindings/python/simple_client.py deleted file mode 100755 index 57b768eb4..000000000 --- a/libtorrent_utp/bindings/python/simple_client.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/python -# Copyright Arvid Norberg 2008. 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) - - -import libtorrent as lt -import time -import sys - -ses = lt.session() -ses.listen_on(6881, 6891) - -info = lt.torrent_info(sys.argv[1]) -h = ses.add_torrent({'ti': info, 'save_path': './'}) -print 'starting', h.name() - -while (not h.is_seed()): - s = h.status() - - state_str = ['queued', 'checking', 'downloading metadata', \ - 'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume'] - print '\r%.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d) %s' % \ - (s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \ - s.num_peers, state_str[s.state]), - sys.stdout.flush() - - time.sleep(1) - -print h.name(), 'complete' - diff --git a/libtorrent_utp/bindings/python/src/alert.cpp b/libtorrent_utp/bindings/python/src/alert.cpp deleted file mode 100644 index 17c7a5d9f..000000000 --- a/libtorrent_utp/bindings/python/src/alert.cpp +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -std::string get_buffer(read_piece_alert const& rpa) -{ - return rpa.buffer ? std::string(rpa.buffer.get(), rpa.size) - : std::string(); -} - -void bind_alert() -{ - using boost::noncopyable; - - { - scope alert_scope = class_("alert", no_init) - .def("message", &alert::message) - .def("what", &alert::what) - .def("category", &alert::category) -#ifndef TORRENT_NO_DEPRECATE - .def("severity", &alert::severity) -#endif - .def("__str__", &alert::message) - ; - -#ifndef TORRENT_NO_DEPRECATE - enum_("severity_levels") - .value("debug", alert::debug) - .value("info", alert::info) - .value("warning", alert::warning) - .value("critical", alert::critical) - .value("fatal", alert::fatal) - .value("none", alert::none) - ; -#endif - - enum_("category_t") - .value("error_notification", alert::error_notification) - .value("peer_notification", alert::peer_notification) - .value("port_mapping_notification", alert::port_mapping_notification) - .value("storage_notification", alert::storage_notification) - .value("tracker_notification", alert::tracker_notification) - .value("debug_notification", alert::debug_notification) - .value("status_notification", alert::status_notification) - .value("progress_notification", alert::progress_notification) - .value("ip_block_notification", alert::ip_block_notification) - .value("performance_warning", alert::performance_warning) - .value("all_categories", alert::all_categories) - ; - - } - - class_, noncopyable>( - "torrent_alert", no_init) - .def_readonly("handle", &torrent_alert::handle) - ; - - class_, noncopyable>( - "tracker_alert", no_init) - .def_readonly("url", &tracker_alert::url) - ; - - class_, noncopyable>( - "read_piece_alert", 0, no_init) - .add_property("buffer", get_buffer) - .def_readonly("piece", &read_piece_alert::piece) - .def_readonly("size", &read_piece_alert::size) - ; - - class_, noncopyable>( - "peer_alert", no_init) - .def_readonly("ip", &peer_alert::ip) - .def_readonly("pid", &peer_alert::pid) - ; - class_, noncopyable>( - "tracker_error_alert", no_init) - .def_readonly("msg", &tracker_error_alert::msg) - .def_readonly("times_in_row", &tracker_error_alert::times_in_row) - .def_readonly("status_code", &tracker_error_alert::status_code) - ; - - class_, noncopyable>( - "tracker_warning_alert", no_init); - - class_, noncopyable>( - "tracker_reply_alert", no_init) - .def_readonly("num_peers", &tracker_reply_alert::num_peers) - ; - - class_, noncopyable>( - "tracker_announce_alert", no_init) - .def_readonly("event", &tracker_announce_alert::event) - ; - - class_, noncopyable>( - "hash_failed_alert", no_init) - .def_readonly("piece_index", &hash_failed_alert::piece_index) - ; - - class_, noncopyable>( - "peer_ban_alert", no_init); - - class_, noncopyable>( - "peer_error_alert", no_init); - - class_, noncopyable>( - "invalid_request_alert", no_init) - .def_readonly("request", &invalid_request_alert::request) - ; - - class_("peer_request") - .def_readonly("piece", &peer_request::piece) - .def_readonly("start", &peer_request::start) - .def_readonly("length", &peer_request::length) - .def(self == self) - ; - - class_, noncopyable>( - "torrent_finished_alert", no_init); - - class_, noncopyable>( - "piece_finished_alert", no_init) - .def_readonly("piece_index", &piece_finished_alert::piece_index) - ; - - class_, noncopyable>( - "block_finished_alert", no_init) - .def_readonly("block_index", &block_finished_alert::block_index) - .def_readonly("piece_index", &block_finished_alert::piece_index) - ; - - class_, noncopyable>( - "block_downloading_alert", no_init) - .def_readonly("peer_speedmsg", &block_downloading_alert::peer_speedmsg) - .def_readonly("block_index", &block_downloading_alert::block_index) - .def_readonly("piece_index", &block_downloading_alert::piece_index) - ; - - class_, noncopyable>( - "storage_moved_alert", no_init) - .def_readonly("path", &storage_moved_alert::path) - ; - - class_, noncopyable>( - "storage_moved_failed_alert", no_init) - .def_readonly("error", &storage_moved_failed_alert::error) - ; - - class_, noncopyable>( - "torrent_deleted_alert", no_init) - .def_readonly("info_hash", &torrent_deleted_alert::info_hash) - ; - - class_, noncopyable>( - "torrent_paused_alert", no_init); - - class_, noncopyable>( - "torrent_checked_alert", no_init); - - class_, noncopyable>( - "url_seed_alert", no_init) - .def_readonly("url", &url_seed_alert::url) - .def_readonly("msg", &url_seed_alert::msg) - ; - - class_, noncopyable>( - "file_error_alert", no_init) - .def_readonly("file", &file_error_alert::file) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("msg", &file_error_alert::msg) -#endif - ; - - class_, noncopyable>( - "metadata_failed_alert", no_init); - - class_, noncopyable>( - "metadata_received_alert", no_init); - - class_, noncopyable>( - "listen_failed_alert", no_init) - .def_readonly("endpoint", &listen_failed_alert::endpoint) - .def_readonly("error", &listen_failed_alert::error) - ; - - class_, noncopyable>( - "listen_succeeded_alert", no_init) - .def_readonly("endpoint", &listen_succeeded_alert::endpoint) - ; - - class_, noncopyable>( - "portmap_error_alert", no_init) - .def_readonly("mapping", &portmap_error_alert::mapping) - .def_readonly("map_type", &portmap_error_alert::map_type) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("type", &portmap_error_alert::map_type) - .def_readonly("msg", &portmap_error_alert::msg) -#endif - ; - - class_, noncopyable>( - "portmap_alert", no_init) - .def_readonly("mapping", &portmap_alert::mapping) - .def_readonly("external_port", &portmap_alert::external_port) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("type", &portmap_alert::map_type) -#endif - .def_readonly("map_type", &portmap_alert::map_type) - ; - - class_, noncopyable>( - "portmap_log_alert", no_init) - .def_readonly("map_type", &portmap_log_alert::map_type) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("type", &portmap_log_alert::map_type) - .def_readonly("msg", &portmap_log_alert::msg) -#endif - ; - - class_, noncopyable>( - "fastresume_rejected_alert", no_init) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("msg", &fastresume_rejected_alert::msg) -#endif - ; - - class_, noncopyable>( - "peer_blocked_alert", no_init) - .def_readonly("ip", &peer_blocked_alert::ip) - ; - - class_, noncopyable>( - "scrape_reply_alert", no_init) - .def_readonly("incomplete", &scrape_reply_alert::incomplete) - .def_readonly("complete", &scrape_reply_alert::complete) - ; - - class_, noncopyable>( - "scrape_failed_alert", no_init); - - class_, noncopyable>( - "udp_error_alert", no_init) - .def_readonly("endpoint", &udp_error_alert::endpoint) - .def_readonly("error", &udp_error_alert::error) - ; - - class_, noncopyable>( - "external_ip_alert", no_init) - .def_readonly("external_address", &external_ip_alert::external_address) - ; - - class_, noncopyable>( - "save_resume_data_alert", no_init) - .def_readonly("resume_data", &save_resume_data_alert::resume_data) - ; - - class_, noncopyable>( - "file_completed_alert", no_init) - .def_readonly("index", &file_completed_alert::index) - ; - - class_, noncopyable>( - "file_renamed_alert", no_init) - .def_readonly("index", &file_renamed_alert::index) - .def_readonly("name", &file_renamed_alert::name) - ; - - class_, noncopyable>( - "file_rename_failed_alert", no_init) - .def_readonly("index", &file_rename_failed_alert::index) - .def_readonly("error", &file_rename_failed_alert::error) - ; - - class_, noncopyable>( - "torrent_resumed_alert", no_init - ); - - class_, noncopyable>( - "state_changed_alert", no_init) - .def_readonly("state", &state_changed_alert::state) - .def_readonly("prev_state", &state_changed_alert::prev_state) - ; - - class_, noncopyable>( - "dht_reply_alert", no_init) - .def_readonly("num_peers", &dht_reply_alert::num_peers) - ; - - class_, noncopyable>( - "dht_announce_alert", no_init) - .def_readonly("ip", &dht_announce_alert::ip) - .def_readonly("port", &dht_announce_alert::port) - .def_readonly("info_hash", &dht_announce_alert::info_hash) - ; - - class_, noncopyable>( - "dht_get_peers_alert", no_init - ) - .def_readonly("info_hash", &dht_get_peers_alert::info_hash) - ; - - class_, noncopyable>( - "peer_unsnubbed_alert", no_init - ); - - class_, noncopyable>( - "peer_snubbed_alert", no_init - ); - - class_, noncopyable>( - "peer_connect_alert", no_init - ); - - class_, noncopyable>( - "peer_disconnected_alert", no_init) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("msg", &peer_disconnected_alert::msg) -#endif - ; - - class_, noncopyable>( - "request_dropped_alert", no_init) - .def_readonly("block_index", &request_dropped_alert::block_index) - .def_readonly("piece_index", &request_dropped_alert::piece_index) - ; - - class_, noncopyable>( - "block_timeout_alert", no_init) - .def_readonly("block_index", &block_timeout_alert::block_index) - .def_readonly("piece_index", &block_timeout_alert::piece_index) - ; - - class_, noncopyable>( - "unwanted_block_alert", no_init) - .def_readonly("block_index", &unwanted_block_alert::block_index) - .def_readonly("piece_index", &unwanted_block_alert::piece_index) - ; - - class_, noncopyable>( - "torrent_delete_failed_alert", no_init) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("msg", &torrent_delete_failed_alert::msg) -#endif - ; - - class_, noncopyable>( - "save_resume_data_failed_alert", no_init) -#ifndef TORRENT_NO_DEPRECATE - .def_readonly("msg", &save_resume_data_failed_alert::msg) -#endif - ; - - class_, noncopyable>( - "performance_alert", no_init) - .def_readonly("warning_code", &performance_alert::warning_code) - ; - enum_("performance_warning_t") - .value("outstanding_disk_buffer_limit_reached", performance_alert::outstanding_disk_buffer_limit_reached) - .value("outstanding_request_limit_reached", performance_alert::outstanding_request_limit_reached) - .value("upload_limit_too_low", performance_alert::upload_limit_too_low) - .value("download_limit_too_low", performance_alert::download_limit_too_low) - ; - - - class_, noncopyable>( - "stats_alert", no_init) - .def_readonly("transferred", &stats_alert::transferred) - .def_readonly("interval", &stats_alert::interval) - ; - - enum_("stats_channel") - .value("upload_payload", stats_alert::upload_payload) - .value("upload_protocol", stats_alert::upload_protocol) - .value("upload_ip_protocol", stats_alert::upload_ip_protocol) - .value("upload_dht_protocol", stats_alert::upload_dht_protocol) - .value("upload_tracker_protocol", stats_alert::upload_tracker_protocol) - .value("download_payload", stats_alert::download_payload) - .value("download_protocol", stats_alert::download_protocol) - .value("download_ip_protocol", stats_alert::download_ip_protocol) - .value("download_dht_protocol", stats_alert::download_dht_protocol) - .value("download_tracker_protocol", stats_alert::download_tracker_protocol) - ; - - class_, noncopyable>( - "anonymous_mode_alert", no_init) - .def_readonly("kind", &anonymous_mode_alert::kind) - .def_readonly("str", &anonymous_mode_alert::str) - ; - - enum_("kind") - .value("tracker_no_anonymous", anonymous_mode_alert::tracker_not_anonymous) - ; -} diff --git a/libtorrent_utp/bindings/python/src/big_number.cpp b/libtorrent_utp/bindings/python/src/big_number.cpp deleted file mode 100644 index d91f02af0..000000000 --- a/libtorrent_utp/bindings/python/src/big_number.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include - -void bind_big_number() -{ - using namespace boost::python; - using namespace libtorrent; - - class_("big_number") - .def(self == self) - .def(self != self) - .def(self < self) - .def(self_ns::str(self)) - .def(init()) - ; -} - diff --git a/libtorrent_utp/bindings/python/src/converters.cpp b/libtorrent_utp/bindings/python/src/converters.cpp deleted file mode 100644 index cae84be75..000000000 --- a/libtorrent_utp/bindings/python/src/converters.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Andrew Resch 2009. 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) - -#include - -using namespace boost::python; - -template -struct pair_to_tuple -{ - static PyObject* convert(const std::pair& p) - { - return incref(make_tuple(p.first, p.second).ptr()); - } -}; - -template -struct tuple_to_pair -{ - tuple_to_pair() - { - converter::registry::push_back( - &convertible, &construct, type_id >() - ); - } - - static void* convertible(PyObject* x) - { - return PyTuple_Check(x) ? x: 0; - } - - static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) - { - void* storage = ((converter::rvalue_from_python_storage< - std::pair >*)data)->storage.bytes; - - object o(borrowed(x)); - std::pair p; - p.first = extract(o[0]); - p.second = extract(o[1]); - new (storage) std::pair(p); - data->convertible = storage; - } -}; - -void bind_converters() -{ - to_python_converter, pair_to_tuple >(); - tuple_to_pair(); -} diff --git a/libtorrent_utp/bindings/python/src/create_torrent.cpp b/libtorrent_utp/bindings/python/src/create_torrent.cpp deleted file mode 100644 index 048b633c4..000000000 --- a/libtorrent_utp/bindings/python/src/create_torrent.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright Daniel Wallin & Arvid Norberg 2009. 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) - -#include -#include -#include -#include "libtorrent/intrusive_ptr_base.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - void set_hash(create_torrent& c, int p, char const* hash) - { - c.set_hash(p, sha1_hash(hash)); - } - - void call_python_object(boost::python::object const& obj, int i) - { - obj(i); - } - -#ifndef BOOST_NO_EXCEPTIONS - void set_piece_hashes_callback(create_torrent& c, std::string const& p - , boost::python::object cb) - { - set_piece_hashes(c, p, boost::bind(call_python_object, cb, _1)); - } -#else - void set_piece_hashes_callback(create_torrent& c, std::string const& p - , boost::python::object cb) - { - error_code ec; - set_piece_hashes(c, p, boost::bind(call_python_object, cb, _1), ec); - } - - void set_piece_hashes0(create_torrent& c, std::string const & s) - { - error_code ec; - set_piece_hashes(c, s, ec); - } -#endif - - void add_node(create_torrent& ct, std::string const& addr, int port) - { - ct.add_node(std::make_pair(addr, port)); - } - - void add_file(file_storage& ct, file_entry const& fe - , std::string const& hash, std::string const& linkpath) - { - ct.add_file(fe, hash.empty() ? 0 : hash.c_str() - , linkpath.empty() ? 0 : &linkpath); - } -} - -void bind_create_torrent() -{ - void (file_storage::*add_file0)(std::string const&, size_type, int, std::time_t, std::string const&) = &file_storage::add_file; -#if TORRENT_USE_WSTRING - void (file_storage::*add_file1)(std::wstring const&, size_type, int, std::time_t, std::string const&) = &file_storage::add_file; -#endif - - void (file_storage::*set_name0)(std::string const&) = &file_storage::set_name; -#if TORRENT_USE_WSTRING - void (file_storage::*set_name1)(std::wstring const&) = &file_storage::set_name; -#endif - -#ifndef BOOST_NO_EXCEPTIONS - void (*set_piece_hashes0)(create_torrent&, std::string const&) = &set_piece_hashes; -#endif - void (*add_files0)(file_storage&, std::string const&, boost::uint32_t) = add_files; - - class_("file_storage") - .def("is_valid", &file_storage::is_valid) - .def("add_file", add_file, (arg("entry"), arg("hash") = std::string(), arg("symlink") = std::string())) - .def("add_file", add_file0, (arg("path"), arg("size"), arg("flags") = 0, arg("mtime") = 0, arg("linkpath") = "")) -#if TORRENT_USE_WSTRING - .def("add_file", add_file1, (arg("path"), arg("size"), arg("flags") = 0, arg("mtime") = 0, arg("linkpath") = "")) -#endif - .def("num_files", &file_storage::num_files) - .def("at", &file_storage::at, return_internal_reference<>()) - .def("hash", &file_storage::hash) - .def("symlink", &file_storage::symlink, return_internal_reference<>()) - .def("file_index", &file_storage::file_index) - .def("file_base", &file_storage::file_base) - .def("set_file_base", &file_storage::set_file_base) - .def("file_path", &file_storage::file_path) - .def("total_size", &file_storage::total_size) - .def("set_num_pieces", &file_storage::set_num_pieces) - .def("num_pieces", &file_storage::num_pieces) - .def("set_piece_length", &file_storage::set_piece_length) - .def("piece_length", &file_storage::piece_length) - .def("piece_size", &file_storage::piece_size) - .def("set_name", set_name0) -#if TORRENT_USE_WSTRING - .def("set_name", set_name1) -#endif - .def("name", &file_storage::name, return_internal_reference<>()) - ; - - class_("create_torrent", no_init) - .def(init()) - .def(init()) - - .def("generate", &create_torrent::generate) - - .def("files", &create_torrent::files, return_internal_reference<>()) - .def("set_comment", &create_torrent::set_comment) - .def("set_creator", &create_torrent::set_creator) - .def("set_hash", &set_hash) - .def("add_url_seed", &create_torrent::add_url_seed) - .def("add_node", &add_node) - .def("add_tracker", &create_torrent::add_tracker) - .def("set_priv", &create_torrent::set_priv) - .def("num_pieces", &create_torrent::num_pieces) - .def("piece_length", &create_torrent::piece_length) - .def("piece_size", &create_torrent::piece_size) - .def("priv", &create_torrent::priv) - ; - - def("add_files", add_files0, (arg("fs"), arg("path"), arg("flags") = 0)); - def("set_piece_hashes", set_piece_hashes0); - def("set_piece_hashes", set_piece_hashes_callback); - -} diff --git a/libtorrent_utp/bindings/python/src/datetime.cpp b/libtorrent_utp/bindings/python/src/datetime.cpp deleted file mode 100644 index da1347671..000000000 --- a/libtorrent_utp/bindings/python/src/datetime.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include "optional.hpp" -#include - -using namespace boost::python; - -#if BOOST_VERSION < 103400 - -// From Boost 1.34 -object import(str name) -{ - // should be 'char const *' but older python versions don't use 'const' yet. - char *n = extract(name); - handle<> module(borrowed(PyImport_ImportModule(n))); - return object(module); -} - -#endif - -object datetime_timedelta; -object datetime_datetime; - -struct time_duration_to_python -{ - static PyObject* convert(boost::posix_time::time_duration const& d) - { - object result = datetime_timedelta( - 0 // days - , 0 // seconds - , d.total_microseconds() - ); - - return incref(result.ptr()); - } -}; - -struct ptime_to_python -{ - static PyObject* convert(boost::posix_time::ptime const& pt) - { - boost::gregorian::date date = pt.date(); - boost::posix_time::time_duration td = pt.time_of_day(); - - object result = datetime_datetime( - (int)date.year() - , (int)date.month() - , (int)date.day() - , td.hours() - , td.minutes() - , td.seconds() - ); - - return incref(result.ptr()); - } -}; - -void bind_datetime() -{ - object datetime = import("datetime").attr("__dict__"); - - datetime_timedelta = datetime["timedelta"]; - datetime_datetime = datetime["datetime"]; - - to_python_converter< - boost::posix_time::time_duration - , time_duration_to_python - >(); - - to_python_converter< - boost::posix_time::ptime - , ptime_to_python - >(); - - optional_to_python(); -} - diff --git a/libtorrent_utp/bindings/python/src/entry.cpp b/libtorrent_utp/bindings/python/src/entry.cpp deleted file mode 100644 index 0a11070b3..000000000 --- a/libtorrent_utp/bindings/python/src/entry.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -struct entry_to_python -{ - static object convert(entry::list_type const& l) - { - list result; - - for (entry::list_type::const_iterator i(l.begin()), e(l.end()); i != e; ++i) - { - result.append(*i); - } - - return result; - } - - static object convert(entry::dictionary_type const& d) - { - dict result; - - for (entry::dictionary_type::const_iterator i(d.begin()), e(d.end()); i != e; ++i) - result[i->first] = i->second; - - return result; - } - - static object convert0(entry const& e) - { - switch (e.type()) - { - case entry::int_t: - return object(e.integer()); - case entry::string_t: - return object(e.string()); - case entry::list_t: - return convert(e.list()); - case entry::dictionary_t: - return convert(e.dict()); - default: - return object(); - } - } - - static PyObject* convert(boost::shared_ptr const& e) - { - if (!e) - return incref(Py_None); - return convert(*e); - } - - static PyObject* convert(entry const& e) - { - return incref(convert0(e).ptr()); - } -}; - -struct entry_from_python -{ - entry_from_python() - { - converter::registry::push_back( - &convertible, &construct, type_id() - ); - } - - static void* convertible(PyObject* e) - { - return e; - } - - static entry construct0(object e) - { - if (extract(e).check()) - { - dict d = extract(e); - list items(d.items()); - std::size_t length = extract(items.attr("__len__")()); - entry result(entry::dictionary_t); - - for (std::size_t i = 0; i < length; ++i) - { - result.dict().insert( - std::make_pair( - extract(items[i][0])() - , construct0(items[i][1]) - ) - ); - } - - return result; - } - else if (extract(e).check()) - { - list l = extract(e); - - std::size_t length = extract(l.attr("__len__")()); - entry result(entry::list_t); - - for (std::size_t i = 0; i < length; ++i) - { - result.list().push_back(construct0(l[i])); - } - - return result; - } - else if (extract(e).check()) - { - return entry(extract(e)()); - } - else if (extract(e).check()) - { - return entry(extract(e)()); - } - - return entry(); - } - - static void construct(PyObject* e, converter::rvalue_from_python_stage1_data* data) - { - void* storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; - new (storage) entry(construct0(object(borrowed(e)))); - data->convertible = storage; - } -}; - -void bind_entry() -{ - to_python_converter, entry_to_python>(); - to_python_converter(); - entry_from_python(); -} - diff --git a/libtorrent_utp/bindings/python/src/extensions.cpp b/libtorrent_utp/bindings/python/src/extensions.cpp deleted file mode 100644 index 05c67f047..000000000 --- a/libtorrent_utp/bindings/python/src/extensions.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Daniel Wallin, Arvid Norberg 2007. 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) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gil.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - - struct torrent_plugin_wrap : torrent_plugin, wrapper - { - boost::shared_ptr new_connection(peer_connection* p) - { - lock_gil lock; - - if (override f = this->get_override("new_connection")) - return f(ptr(p)); - return torrent_plugin::new_connection(p); - } - - boost::shared_ptr default_new_connection(peer_connection* p) - { - return this->torrent_plugin::new_connection(p); - } - - void on_piece_pass(int index) - { - lock_gil lock; - - if (override f = this->get_override("on_piece_pass")) - f(index); - else - torrent_plugin::on_piece_pass(index); - } - - void default_on_piece_pass(int index) - { - this->torrent_plugin::on_piece_pass(index); - } - - void on_piece_failed(int index) - { - lock_gil lock; - - if (override f = this->get_override("on_piece_failed")) - f(index); - else - torrent_plugin::on_piece_failed(index); - } - - void default_on_piece_failed(int index) - { - return this->torrent_plugin::on_piece_failed(index); - } - - void tick() - { - lock_gil lock; - - if (override f = this->get_override("tick")) - f(); - else - torrent_plugin::tick(); - } - - void default_tick() - { - return this->torrent_plugin::tick(); - } - - bool on_pause() - { - lock_gil lock; - - if (override f = this->get_override("on_pause")) - return f(); - return torrent_plugin::on_pause(); - } - - bool default_on_pause() - { - return this->torrent_plugin::on_pause(); - } - - bool on_resume() - { - lock_gil lock; - - if (override f = this->get_override("on_resume")) - return f(); - return torrent_plugin::on_resume(); - } - - bool default_on_resume() - { - return this->torrent_plugin::on_resume(); - } - }; - -} // namespace unnamed - - -boost::shared_ptr create_metadata_plugin_wrapper(torrent* t) { - return create_metadata_plugin(t, NULL); -} - -boost::shared_ptr create_ut_metadata_plugin_wrapper(torrent *t) { - return create_ut_metadata_plugin(t, NULL); -} - -boost::shared_ptr create_ut_pex_plugin_wrapper(torrent* t) { - return create_ut_pex_plugin(t, NULL); -} - -boost::shared_ptr create_smart_ban_plugin_wrapper(torrent* t) { - return create_smart_ban_plugin(t, NULL); -} - -void bind_extensions() -{ - class_< - torrent_plugin_wrap, boost::shared_ptr, boost::noncopyable - >("torrent_plugin") - .def( - "new_connection" - , &torrent_plugin::new_connection, &torrent_plugin_wrap::default_new_connection - ) - .def( - "on_piece_pass" - , &torrent_plugin::on_piece_pass, &torrent_plugin_wrap::default_on_piece_pass - ) - .def( - "on_piece_failed" - , &torrent_plugin::on_piece_failed, &torrent_plugin_wrap::default_on_piece_failed - ) - .def( - "tick" - , &torrent_plugin::tick, &torrent_plugin_wrap::default_tick - ) - .def( - "on_pause" - , &torrent_plugin::on_pause, &torrent_plugin_wrap::default_on_pause - ) - .def( - "on_resume" - , &torrent_plugin::on_resume, &torrent_plugin_wrap::default_on_resume - ); - - // TODO move to it's own file - class_("peer_connection", no_init); - - class_ >("torrent_plugin", no_init); - def("create_ut_pex_plugin", create_ut_pex_plugin_wrapper); - def("create_metadata_plugin", create_metadata_plugin_wrapper); - def("create_ut_metadata_plugin", create_ut_metadata_plugin_wrapper); - def("create_smart_ban_plugin", create_smart_ban_plugin_wrapper); -} - - diff --git a/libtorrent_utp/bindings/python/src/fingerprint.cpp b/libtorrent_utp/bindings/python/src/fingerprint.cpp deleted file mode 100644 index ca40f3d4d..000000000 --- a/libtorrent_utp/bindings/python/src/fingerprint.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include - -void bind_fingerprint() -{ - using namespace boost::python; - using namespace libtorrent; - - class_("fingerprint", no_init) - .def( - init( - (arg("id"), "major", "minor", "revision", "tag") - ) - ) - .def("__str__", &fingerprint::to_string) - .def_readonly("name", &fingerprint::name) - .def_readonly("major_version", &fingerprint::major_version) - .def_readonly("minor_version", &fingerprint::minor_version) - .def_readonly("revision_version", &fingerprint::revision_version) - .def_readonly("tag_version", &fingerprint::tag_version) - ; -} - diff --git a/libtorrent_utp/bindings/python/src/gil.hpp b/libtorrent_utp/bindings/python/src/gil.hpp deleted file mode 100644 index d9534c92c..000000000 --- a/libtorrent_utp/bindings/python/src/gil.hpp +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Daniel Wallin 2007. 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 GIL_070107_HPP -# define GIL_070107_HPP - -# include -# include -# include -# include - -//namespace libtorrent { namespace python { - -// RAII helper to release GIL. -struct allow_threading_guard -{ - allow_threading_guard() - : save(PyEval_SaveThread()) - {} - - ~allow_threading_guard() - { - PyEval_RestoreThread(save); - } - - PyThreadState* save; -}; - -struct lock_gil -{ - lock_gil() - : state(PyGILState_Ensure()) - {} - - ~lock_gil() - { - PyGILState_Release(state); - } - - PyGILState_STATE state; -}; - -template -struct allow_threading -{ - allow_threading(F fn) - : fn(fn) - {} - - template - R operator()(A0& a0) - { - allow_threading_guard guard; - return (a0.*fn)(); - } - - template - R operator()(A0& a0, A1& a1) - { - allow_threading_guard guard; - return (a0.*fn)(a1); - } - - template - R operator()(A0& a0, A1& a1, A2& a2) - { - allow_threading_guard guard; - return (a0.*fn)(a1,a2); - } - - template - R operator()(A0& a0, A1& a1, A2& a2, A3& a3) - { - allow_threading_guard guard; - return (a0.*fn)(a1,a2,a3); - } - - template - R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) - { - allow_threading_guard guard; - return (a0.*fn)(a1,a2,a3,a4); - } - - template - R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) - { - allow_threading_guard guard; - return (a0.*fn)(a1,a2,a3,a4,a5); - } - - F fn; -}; - -template -struct visitor : boost::python::def_visitor > -{ - visitor(F fn) - : fn(fn) - {} - - template - void visit_aux( - Class& cl, char const* name - , Options const& options, Signature const& signature) const - { - typedef typename boost::mpl::at_c::type return_type; - - cl.def( - name - , boost::python::make_function( - allow_threading(fn) - , options.policies() - , options.keywords() - , signature - ) - ); - } - - template - void visit(Class& cl, char const* name, Options const& options) const - { - this->visit_aux( - cl, name, options - , boost::python::detail::get_signature(fn, (typename Class::wrapped_type*)0) - ); - } - - F fn; -}; - -// Member function adaptor that releases and aqcuires the GIL -// around the function call. -template -visitor allow_threads(F fn) -{ - return visitor(fn); -} - -//}} // namespace libtorrent::python - -#endif // GIL_070107_HPP - diff --git a/libtorrent_utp/bindings/python/src/ip_filter.cpp b/libtorrent_utp/bindings/python/src/ip_filter.cpp deleted file mode 100644 index 74ef2e89e..000000000 --- a/libtorrent_utp/bindings/python/src/ip_filter.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Andrew Resch 2008. 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) - -#include -#include -#include "gil.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - void add_rule(ip_filter& filter, std::string start, std::string end, int flags) - { - return filter.add_rule(address::from_string(start), address::from_string(end), flags); - } - - int access0(ip_filter& filter, std::string addr) - { - return filter.access(address::from_string(addr)); - } -} - -void bind_ip_filter() -{ - class_("ip_filter") - .def("add_rule", add_rule) - .def("access", access0) - .def("export_filter", allow_threads(&ip_filter::export_filter)) - ; -} diff --git a/libtorrent_utp/bindings/python/src/magnet_uri.cpp b/libtorrent_utp/bindings/python/src/magnet_uri.cpp deleted file mode 100644 index b68fc6ec3..000000000 --- a/libtorrent_utp/bindings/python/src/magnet_uri.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright Andrew Resch 2008. 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 - -#include -#include -#include -#include -#include "gil.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace { - - torrent_handle _add_magnet_uri(session& s, std::string uri, dict params) - { - add_torrent_params p; - - std::string url; - if (params.has_key("tracker_url")) - { - url = extract(params["tracker_url"]); - p.tracker_url = url.c_str(); - } - std::string name; - if (params.has_key("name")) - { - name = extract(params["name"]); - p.name = name.c_str(); - } - p.save_path = extract(params["save_path"]); - - std::vector resume_buf; - if (params.has_key("resume_data")) - { - std::string resume = extract(params["resume_data"]); - resume_buf.resize(resume.size()); - std::memcpy(&resume_buf[0], &resume[0], resume.size()); - p.resume_data = &resume_buf; - } - p.storage_mode = extract(params["storage_mode"]); - p.paused = params["paused"]; - p.auto_managed = params["auto_managed"]; - p.duplicate_is_error = params["duplicate_is_error"]; - -#ifndef BOOST_NO_EXCEPTIONS - return add_magnet_uri(s, uri, p); -#else - error_code ec; - return add_magnet_uri(s, uri, p, ec); -#endif - } -} - -void bind_magnet_uri() -{ - def("add_magnet_uri", &_add_magnet_uri); -} diff --git a/libtorrent_utp/bindings/python/src/module.cpp b/libtorrent_utp/bindings/python/src/module.cpp deleted file mode 100644 index d0b221e95..000000000 --- a/libtorrent_utp/bindings/python/src/module.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include - -void bind_utility(); -void bind_fingerprint(); -void bind_big_number(); -void bind_session(); -void bind_entry(); -void bind_torrent_info(); -void bind_torrent_handle(); -void bind_torrent_status(); -void bind_session_settings(); -void bind_version(); -void bind_alert(); -void bind_datetime(); -void bind_extensions(); -void bind_peer_plugin(); -void bind_torrent(); -void bind_peer_info(); -void bind_ip_filter(); -void bind_magnet_uri(); -void bind_converters(); -void bind_create_torrent(); - -BOOST_PYTHON_MODULE(libtorrent) -{ - Py_Initialize(); - PyEval_InitThreads(); - - bind_utility(); - bind_fingerprint(); - bind_big_number(); - bind_entry(); - bind_session(); - bind_torrent_info(); - bind_torrent_handle(); - bind_torrent_status(); - bind_session_settings(); - bind_version(); - bind_alert(); - bind_datetime(); - bind_extensions(); -#ifndef TORRENT_NO_PYTHON_PLUGINS - bind_peer_plugin(); -#endif - bind_torrent(); - bind_peer_info(); - bind_ip_filter(); - bind_magnet_uri(); - bind_converters(); - bind_create_torrent(); -} diff --git a/libtorrent_utp/bindings/python/src/optional.hpp b/libtorrent_utp/bindings/python/src/optional.hpp deleted file mode 100644 index 63138cc68..000000000 --- a/libtorrent_utp/bindings/python/src/optional.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Daniel Wallin 2007. 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 OPTIONAL_070108_HPP -# define OPTIONAL_070108_HPP - -# include -# include - -template -struct optional_to_python -{ - optional_to_python() - { - boost::python::to_python_converter< - boost::optional, optional_to_python - >(); - } - - static PyObject* convert(boost::optional const& x) - { - if (!x) - return boost::python::incref(Py_None); - - return boost::python::incref(boost::python::object(*x).ptr()); - } -}; - -#endif // OPTIONAL_070108_HPP - diff --git a/libtorrent_utp/bindings/python/src/peer_info.cpp b/libtorrent_utp/bindings/python/src/peer_info.cpp deleted file mode 100644 index 2d677d3df..000000000 --- a/libtorrent_utp/bindings/python/src/peer_info.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright Daniel Wallin 2007. 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) - -#include -#include -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -int get_last_active(peer_info const& pi) -{ - return total_seconds(pi.last_active); -} - -int get_last_request(peer_info const& pi) -{ - return total_seconds(pi.last_request); -} - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES -str get_country(peer_info const& pi) -{ - return str(pi.country, 2); -} -#endif - -tuple get_ip(peer_info const& pi) -{ - return make_tuple(pi.ip.address().to_string(), pi.ip.port()); -} - -list get_pieces(peer_info const& pi) -{ - list ret; - - for (bitfield::const_iterator i = pi.pieces.begin() - , end(pi.pieces.end()); i != end; ++i) - { - ret.append(*i); - } - return ret; -} - -void bind_peer_info() -{ - scope pi = class_("peer_info") - .def_readonly("flags", &peer_info::flags) - .def_readonly("source", &peer_info::source) - .def_readonly("read_state", &peer_info::read_state) - .def_readonly("write_state", &peer_info::write_state) - .add_property("ip", get_ip) - .def_readonly("up_speed", &peer_info::up_speed) - .def_readonly("down_speed", &peer_info::down_speed) - .def_readonly("payload_up_speed", &peer_info::payload_up_speed) - .def_readonly("payload_down_speed", &peer_info::payload_down_speed) - .def_readonly("total_download", &peer_info::total_download) - .def_readonly("total_upload", &peer_info::total_upload) - .def_readonly("pid", &peer_info::pid) - .add_property("pieces", get_pieces) - .def_readonly("upload_limit", &peer_info::upload_limit) - .def_readonly("download_limit", &peer_info::download_limit) - .add_property("last_request", get_last_request) - .add_property("last_active", get_last_active) - .def_readonly("send_buffer_size", &peer_info::send_buffer_size) - .def_readonly("used_send_buffer", &peer_info::used_send_buffer) - .def_readonly("num_hashfails", &peer_info::num_hashfails) -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - .add_property("country", get_country) -#endif -#ifndef TORRENT_DISABLE_GEO_IP - .def_readonly("inet_as_name", &peer_info::inet_as_name) - .def_readonly("inet_as", &peer_info::inet_as) -#endif - .def_readonly("load_balancing", &peer_info::load_balancing) - .def_readonly("download_queue_length", &peer_info::download_queue_length) - .def_readonly("upload_queue_length", &peer_info::upload_queue_length) - .def_readonly("failcount", &peer_info::failcount) - .def_readonly("downloading_piece_index", &peer_info::downloading_piece_index) - .def_readonly("downloading_block_index", &peer_info::downloading_block_index) - .def_readonly("downloading_progress", &peer_info::downloading_progress) - .def_readonly("downloading_total", &peer_info::downloading_total) - .def_readonly("client", &peer_info::client) - .def_readonly("connection_type", &peer_info::connection_type) - .def_readonly("remote_dl_rate", &peer_info::remote_dl_rate) - .def_readonly("pending_disk_bytes", &peer_info::pending_disk_bytes) - .def_readonly("send_quota", &peer_info::send_quota) - .def_readonly("receive_quota", &peer_info::receive_quota) - .def_readonly("rtt", &peer_info::rtt) - .def_readonly("progress", &peer_info::progress) - ; - - // flags - pi.attr("interesting") = (int)peer_info::interesting; - pi.attr("choked") = (int)peer_info::choked; - pi.attr("remote_interested") = (int)peer_info::remote_interested; - pi.attr("remote_choked") = (int)peer_info::remote_choked; - pi.attr("supports_extensions") = (int)peer_info::supports_extensions; - pi.attr("local_connection") = (int)peer_info::local_connection; - pi.attr("handshake") = (int)peer_info::handshake; - pi.attr("connecting") = (int)peer_info::connecting; - pi.attr("queued") = (int)peer_info::queued; - pi.attr("on_parole") = (int)peer_info::on_parole; - pi.attr("seed") = (int)peer_info::seed; -#ifndef TORRENT_DISABLE_ENCRYPTION - pi.attr("rc4_encrypted") = (int)peer_info::rc4_encrypted; - pi.attr("plaintext_encrypted") = (int)peer_info::plaintext_encrypted; -#endif - - // connection_type - pi.attr("standard_bittorrent") = (int)peer_info::standard_bittorrent; - pi.attr("web_seed") = (int)peer_info::web_seed; - - // source - pi.attr("tracker") = (int)peer_info::tracker; - pi.attr("dht") = (int)peer_info::dht; - pi.attr("pex") = (int)peer_info::pex; - pi.attr("lsd") = (int)peer_info::lsd; - pi.attr("resume_data") = (int)peer_info::resume_data; - - // read/write state - pi.attr("bw_idle") = (int)peer_info::bw_idle; -#ifndef TORRENT_NO_DEPRECATE - pi.attr("bw_torrent") = (int)peer_info::bw_torrent; - pi.attr("bw_global") = (int)peer_info::bw_global; -#endif - pi.attr("bw_network") = (int)peer_info::bw_network; -} - diff --git a/libtorrent_utp/bindings/python/src/peer_plugin.cpp b/libtorrent_utp/bindings/python/src/peer_plugin.cpp deleted file mode 100644 index 6ba48ff80..000000000 --- a/libtorrent_utp/bindings/python/src/peer_plugin.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright Daniel Wallin 2007. 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) - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - struct peer_plugin_wrap : peer_plugin, wrapper - { - void add_handshake(entry& e) - { - if (override f = this->get_override("add_handshake")) - e = call(f.ptr(), e); - else - peer_plugin::add_handshake(e); - } - - void default_add_handshake(entry& e) - { - this->peer_plugin::add_handshake(e); - } - - bool on_handshake(char const* reserved_bits) - { - if (override f = this->get_override("on_handshake")) - return f(); - else - return peer_plugin::on_handshake(reserved_bits); - } - - bool default_on_handshake(char const* reserved_bits) - { - return this->peer_plugin::on_handshake(reserved_bits); - } - - bool on_extension_handshake(lazy_entry const& e) - { - if (override f = this->get_override("on_extension_handshake")) - return f(e); - else - return peer_plugin::on_extension_handshake(e); - } - - bool default_on_extension_handshake(lazy_entry const& e) - { - return this->peer_plugin::on_extension_handshake(e); - } - - bool on_choke() - { - if (override f = this->get_override("on_choke")) - return f(); - else - return peer_plugin::on_choke(); - } - - bool default_on_choke() - { - return this->peer_plugin::on_choke(); - } - - bool on_unchoke() - { - if (override f = this->get_override("on_unchoke")) - return f(); - else - return peer_plugin::on_unchoke(); - } - - bool default_on_unchoke() - { - return this->peer_plugin::on_unchoke(); - } - - bool on_interested() - { - if (override f = this->get_override("on_interested")) - return f(); - else - return peer_plugin::on_interested(); - } - - bool default_on_interested() - { - return this->peer_plugin::on_interested(); - } - - bool on_not_interested() - { - if (override f = this->get_override("on_not_interested")) - return f(); - else - return peer_plugin::on_not_interested(); - } - - bool default_on_not_interested() - { - return this->peer_plugin::on_not_interested(); - } - - bool on_have(int index) - { - if (override f = this->get_override("on_have")) - return f(index); - else - return peer_plugin::on_have(index); - } - - bool default_on_have(int index) - { - return this->peer_plugin::on_have(index); - } - - bool on_bitfield(list _bf) - { - //Convert list to a bitfield - bitfield bf(len(_bf)); - for (int i = 0; i < len(_bf); ++i) - { - if (_bf[i]) - bf.set_bit(i); - else - bf.clear_bit(i); - } - if (override f = this->get_override("on_bitfield")) - return f(bf); - else - return peer_plugin::on_bitfield(bf); - } - - bool default_on_bitfield(const bitfield &bf) - { - return this->peer_plugin::on_bitfield(bf); - } - - bool on_request(peer_request const& req) - { - if (override f = this->get_override("on_request")) - return f(req); - else - return peer_plugin::on_request(req); - } - - bool default_on_request(peer_request const& req) - { - return this->peer_plugin::on_request(req); - } - - bool on_piece(peer_request const& piece, disk_buffer_holder& data) - { - if (override f = this->get_override("on_piece")) - return f(piece, data); - else - return peer_plugin::on_piece(piece, data); - } - - bool default_on_piece(peer_request const& piece, disk_buffer_holder& data) - { - return this->peer_plugin::on_piece(piece, data); - } - - bool on_cancel(peer_request const& req) - { - if (override f = this->get_override("on_cancel")) - return f(req); - else - return peer_plugin::on_cancel(req); - } - - bool default_on_cancel(peer_request const& req) - { - return this->peer_plugin::on_cancel(req); - } - - bool on_extended(int length, int msg, buffer::const_interval body) - { - if (override f = this->get_override("on_extended")) - return f(length, msg, body); - else - return peer_plugin::on_extended(length, msg, body); - } - - bool default_on_extended(int length, int msg, buffer::const_interval body) - { - return this->peer_plugin::on_extended(length, msg, body); - } - - bool on_unknown_message(int length, int msg, buffer::const_interval body) - { - if (override f = this->get_override("on_unknown_message")) - return f(length, msg, body); - else - return peer_plugin::on_unknown_message(length, msg, body); - } - - bool default_on_unknown_message(int length, int msg, buffer::const_interval body) - { - return this->peer_plugin::on_unknown_message(length, msg, body); - } - - void on_piece_pass(int index) - { - if (override f = this->get_override("on_piece_pass")) - f(index); - else - peer_plugin::on_piece_pass(index); - } - - void default_on_piece_pass(int index) - { - this->peer_plugin::on_piece_pass(index); - } - - void on_piece_failed(int index) - { - if (override f = this->get_override("on_piece_failed")) - f(index); - else - peer_plugin::on_piece_failed(index); - } - - void default_on_piece_failed(int index) - { - this->peer_plugin::on_piece_failed(index); - } - - void tick() - { - if (override f = this->get_override("tick")) - f(); - else - peer_plugin::tick(); - } - - void default_tick() - { - this->peer_plugin::tick(); - } - - bool write_request(peer_request const& req) - { - if (override f = this->get_override("write_request")) - return f(req); - else - return peer_plugin::write_request(req); - } - - bool default_write_request(peer_request const& req) - { - return this->peer_plugin::write_request(req); - } - }; - - object get_buffer() - { - static char const data[] = "foobar"; - return object(handle<>(PyBuffer_FromMemory((void*)data, 6))); - } - -} // namespace unnamed - -void bind_peer_plugin() -{ - class_< - peer_plugin_wrap, boost::shared_ptr, boost::noncopyable - >("peer_plugin") - .def( - "add_handshake" - , &peer_plugin::add_handshake, &peer_plugin_wrap::default_add_handshake - ) - .def( - "on_handshake" - , &peer_plugin::on_handshake, &peer_plugin_wrap::default_on_handshake - ) - .def( - "on_extension_handshake" - , &peer_plugin::on_extension_handshake - , &peer_plugin_wrap::default_on_extension_handshake - ) - .def( - "on_choke" - , &peer_plugin::on_choke, &peer_plugin_wrap::default_on_choke - ) - .def( - "on_unchoke" - , &peer_plugin::on_unchoke, &peer_plugin_wrap::default_on_unchoke - ) - .def( - "on_interested" - , &peer_plugin::on_interested, &peer_plugin_wrap::default_on_interested - ) - .def( - "on_not_interested" - , &peer_plugin::on_not_interested, &peer_plugin_wrap::default_on_not_interested - ) - .def( - "on_have" - , &peer_plugin::on_have, &peer_plugin_wrap::default_on_have - ) - .def( - "on_bitfield" - , &peer_plugin::on_bitfield, &peer_plugin_wrap::default_on_bitfield - ) - .def( - "on_request" - , &peer_plugin::on_request, &peer_plugin_wrap::default_on_request - ) - .def( - "on_piece" - , &peer_plugin::on_piece, &peer_plugin_wrap::default_on_piece - ) - .def( - "on_cancel" - , &peer_plugin::on_cancel, &peer_plugin_wrap::default_on_cancel - ) - .def( - "on_piece_pass" - , &peer_plugin::on_piece_pass, &peer_plugin_wrap::default_on_piece_pass - ) - .def( - "on_piece_failed" - , &peer_plugin::on_piece_failed, &peer_plugin_wrap::default_on_piece_failed - ) - .def( - "tick" - , &peer_plugin::tick, &peer_plugin_wrap::default_tick - ) - .def( - "write_request" - , &peer_plugin::write_request, &peer_plugin_wrap::default_write_request - ) - // These seem to make VC7.1 freeze. Needs special handling. - - /*.def( - "on_extended" - , &peer_plugin::on_extended, &peer_plugin_wrap::default_on_extended - ) - .def( - "on_unknown_message" - , &peer_plugin::on_unknown_message, &peer_plugin_wrap::default_on_unknown_message - )*/ - ; - - def("get_buffer", &get_buffer); -} - diff --git a/libtorrent_utp/bindings/python/src/session.cpp b/libtorrent_utp/bindings/python/src/session.cpp deleted file mode 100644 index 0b9760a5d..000000000 --- a/libtorrent_utp/bindings/python/src/session.cpp +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright Daniel Wallin, Arvid Norberg 2006. 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) - -#include -#include -#include -#include -#include -#include -#include "gil.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - bool listen_on(session& s, int min_, int max_, char const* interface) - { - allow_threading_guard guard; - return s.listen_on(std::make_pair(min_, max_), interface); - } - - void outgoing_ports(session& s, int _min, int _max) - { - allow_threading_guard guard; - session_settings settings = s.settings(); - settings.outgoing_ports = std::make_pair(_min, _max); - s.set_settings(settings); - return; - } -#ifndef TORRENT_DISABLE_DHT - void add_dht_router(session& s, std::string router_, int port_) - { - allow_threading_guard guard; - return s.add_dht_router(std::make_pair(router_, port_)); - } -#endif - - struct invoke_extension_factory - { - invoke_extension_factory(object const& callback) - : cb(callback) - {} - - boost::shared_ptr operator()(torrent* t, void*) - { - lock_gil lock; - return extract >(cb(ptr(t)))(); - } - - object cb; - }; - - void add_extension(session& s, object const& e) - { - allow_threading_guard guard; - s.add_extension(invoke_extension_factory(e)); - } - -#ifndef BOOST_NO_EXCEPTIONS -#ifndef TORRENT_NO_DEPRECATE - torrent_handle add_torrent_depr(session& s, torrent_info const& ti - , std::string const& save, entry const& resume - , storage_mode_t storage_mode, bool paused) - { - allow_threading_guard guard; - return s.add_torrent(ti, save, resume, storage_mode, paused, default_storage_constructor); - } -#endif -#endif - - torrent_handle add_torrent(session& s, dict params) - { - add_torrent_params p; - - if (params.has_key("ti")) - p.ti = new torrent_info(extract(params["ti"])); - - std::string url; - if (params.has_key("tracker_url")) - { - url = extract(params["tracker_url"]); - p.tracker_url = url.c_str(); - } - if (params.has_key("info_hash")) - p.info_hash = extract(params["info_hash"]); - std::string name; - if (params.has_key("name")) - { - name = extract(params["name"]); - p.name = name.c_str(); - } - p.save_path = extract(params["save_path"]); - - std::vector resume_buf; - if (params.has_key("resume_data")) - { - std::string resume = extract(params["resume_data"]); - resume_buf.resize(resume.size()); - std::memcpy(&resume_buf[0], &resume[0], resume.size()); - p.resume_data = &resume_buf; - } - if (params.has_key("storage_mode")) - p.storage_mode = extract(params["storage_mode"]); - if (params.has_key("paused")) - p.paused = params["paused"]; - if (params.has_key("auto_managed")) - p.auto_managed = params["auto_managed"]; - if (params.has_key("duplicate_is_error")) - p.duplicate_is_error = params["duplicate_is_error"]; - if (params.has_key("seed_mode")) - p.seed_mode = params["seed_mode"]; - if (params.has_key("upload_mode")) - p.upload_mode = params["upload_mode"]; - if (params.has_key("share_mode")) - p.upload_mode = params["share_mode"]; - if (params.has_key("override_resume_data")) - p.override_resume_data = params["override_resume_data"]; - -#ifndef BOOST_NO_EXCEPTIONS - return s.add_torrent(p); -#else - error_code ec; - return s.add_torrent(p, ec); -#endif - } - - void start_natpmp(session& s) - { - allow_threading_guard guard; - s.start_natpmp(); - } - - void start_upnp(session& s) - { - allow_threading_guard guard; - s.start_upnp(); - } - - alert const* wait_for_alert(session& s, int ms) - { - return s.wait_for_alert(milliseconds(ms)); - } - - list get_torrents(session& s) - { - list ret; - std::vector torrents = s.get_torrents(); - - for (std::vector::iterator i = torrents.begin(); i != torrents.end(); ++i) - { - ret.append(*i); - } - return ret; - } - -#ifndef TORRENT_DISABLE_GEO_IP - void load_asnum_db(session& s, std::string file) - { - allow_threading_guard guard; - s.load_asnum_db(file.c_str()); - } - - void load_country_db(session& s, std::string file) - { - allow_threading_guard guard; - s.load_country_db(file.c_str()); - } -#endif - - entry save_state(session const& s, boost::uint32_t flags) - { - entry e; - s.save_state(e, flags); - return e; - } - -} // namespace unnamed - - -void bind_session() -{ -#ifndef TORRENT_DISABLE_DHT - void (session::*start_dht0)() = &session::start_dht; -#ifndef TORRENT_NO_DEPRECATE - void (session::*start_dht1)(entry const&) = &session::start_dht; -#endif -#endif - - void (session::*load_state0)(lazy_entry const&) = &session::load_state; -#ifndef TORRENT_NO_DEPRECATE - void (session::*load_state1)(entry const&) = &session::load_state; -#endif - - class_("session_status") - .def_readonly("has_incoming_connections", &session_status::has_incoming_connections) - - .def_readonly("upload_rate", &session_status::upload_rate) - .def_readonly("download_rate", &session_status::download_rate) - .def_readonly("total_download", &session_status::total_download) - .def_readonly("total_upload", &session_status::total_upload) - - .def_readonly("payload_upload_rate", &session_status::payload_upload_rate) - .def_readonly("payload_download_rate", &session_status::payload_download_rate) - .def_readonly("total_payload_download", &session_status::total_payload_download) - .def_readonly("total_payload_upload", &session_status::total_payload_upload) - - .def_readonly("ip_overhead_upload_rate", &session_status::ip_overhead_upload_rate) - .def_readonly("ip_overhead_download_rate", &session_status::ip_overhead_download_rate) - .def_readonly("total_ip_overhead_download", &session_status::total_ip_overhead_download) - .def_readonly("total_ip_overhead_upload", &session_status::total_ip_overhead_upload) - - .def_readonly("dht_upload_rate", &session_status::dht_upload_rate) - .def_readonly("dht_download_rate", &session_status::dht_download_rate) - .def_readonly("total_dht_download", &session_status::total_dht_download) - .def_readonly("total_dht_upload", &session_status::total_dht_upload) - - .def_readonly("tracker_upload_rate", &session_status::tracker_upload_rate) - .def_readonly("tracker_download_rate", &session_status::tracker_download_rate) - .def_readonly("total_tracker_download", &session_status::total_tracker_download) - .def_readonly("total_tracker_upload", &session_status::total_tracker_upload) - - .def_readonly("total_redundant_bytes", &session_status::total_redundant_bytes) - .def_readonly("total_failed_bytes", &session_status::total_failed_bytes) - - .def_readonly("num_peers", &session_status::num_peers) - .def_readonly("num_unchoked", &session_status::num_unchoked) - .def_readonly("allowed_upload_slots", &session_status::allowed_upload_slots) - - .def_readonly("up_bandwidth_queue", &session_status::up_bandwidth_queue) - .def_readonly("down_bandwidth_queue", &session_status::down_bandwidth_queue) - - .def_readonly("up_bandwidth_bytes_queue", &session_status::up_bandwidth_bytes_queue) - .def_readonly("down_bandwidth_bytes_queue", &session_status::down_bandwidth_bytes_queue) - - .def_readonly("optimistic_unchoke_counter", &session_status::optimistic_unchoke_counter) - .def_readonly("unchoke_counter", &session_status::unchoke_counter) - -#ifndef TORRENT_DISABLE_DHT - .def_readonly("dht_nodes", &session_status::dht_nodes) - .def_readonly("dht_node_cache", &session_status::dht_node_cache) - .def_readonly("dht_torrents", &session_status::dht_torrents) - .def_readonly("dht_global_nodes", &session_status::dht_global_nodes) - .def_readonly("active_requests", &session_status::active_requests) -#endif - ; - - class_("dht_lookup") - .def_readonly("type", &dht_lookup::type) - .def_readonly("outstanding_requests", &dht_lookup::outstanding_requests) - .def_readonly("timeouts", &dht_lookup::timeouts) - .def_readonly("response", &dht_lookup::responses) - .def_readonly("branch_factor", &dht_lookup::branch_factor) - ; - - enum_("storage_mode_t") - .value("storage_mode_allocate", storage_mode_allocate) - .value("storage_mode_sparse", storage_mode_sparse) - .value("storage_mode_compact", storage_mode_compact) - ; - - enum_("options_t") - .value("none", session::none) - .value("delete_files", session::delete_files) - ; - - enum_("session_flags_t") - .value("add_default_plugins", session::add_default_plugins) - .value("start_default_features", session::start_default_features) - ; - - class_("cache_status") - .def_readonly("blocks_written", &cache_status::blocks_written) - .def_readonly("writes", &cache_status::writes) - .def_readonly("blocks_read", &cache_status::blocks_read) - .def_readonly("blocks_read_hit", &cache_status::blocks_read_hit) - .def_readonly("reads", &cache_status::reads) - .def_readonly("cache_size", &cache_status::cache_size) - .def_readonly("read_cache_size", &cache_status::read_cache_size) - .def_readonly("total_used_buffers", &cache_status::total_used_buffers) - ; - - class_("session", no_init) - .def( - init(( - arg("fingerprint")=fingerprint("LT",0,1,0,0) - , arg("flags")=session::start_default_features | session::add_default_plugins)) - ) - .def( - "listen_on", &listen_on - , (arg("min"), "max", arg("interface") = (char const*)0) - ) - .def("outgoing_ports", &outgoing_ports) - .def("is_listening", allow_threads(&session::is_listening)) - .def("listen_port", allow_threads(&session::listen_port)) - .def("status", allow_threads(&session::status)) -#ifndef TORRENT_DISABLE_DHT - .def( - "add_dht_router", &add_dht_router - , (arg("router"), "port") - ) - .def("start_dht", allow_threads(start_dht0)) -#ifndef TORRENT_NO_DEPRECATE - .def("start_dht", allow_threads(start_dht1)) -#endif - .def("stop_dht", allow_threads(&session::stop_dht)) -#ifndef TORRENT_NO_DEPRECATE - .def("dht_state", allow_threads(&session::dht_state)) -#endif - .def("set_dht_proxy", allow_threads(&session::set_dht_proxy)) - .def("dht_proxy", allow_threads(&session::dht_proxy)) -#endif - .def("add_torrent", &add_torrent) -#ifndef BOOST_NO_EXCEPTIONS -#ifndef TORRENT_NO_DEPRECATE - .def( - "add_torrent", &add_torrent_depr - , ( - arg("resume_data") = entry(), - arg("storage_mode") = storage_mode_sparse, - arg("paused") = false - ) - ) -#endif -#endif - .def("remove_torrent", allow_threads(&session::remove_torrent), arg("option") = session::none -) - .def("set_local_download_rate_limit", allow_threads(&session::set_local_download_rate_limit)) - .def("local_download_rate_limit", allow_threads(&session::local_download_rate_limit)) - - .def("set_local_upload_rate_limit", allow_threads(&session::set_local_upload_rate_limit)) - .def("local_upload_rate_limit", allow_threads(&session::local_upload_rate_limit)) - - .def("set_download_rate_limit", allow_threads(&session::set_download_rate_limit)) - .def("download_rate_limit", allow_threads(&session::download_rate_limit)) - - .def("set_upload_rate_limit", allow_threads(&session::set_upload_rate_limit)) - .def("upload_rate_limit", allow_threads(&session::upload_rate_limit)) - - .def("set_max_uploads", allow_threads(&session::set_max_uploads)) - .def("set_max_connections", allow_threads(&session::set_max_connections)) - .def("set_max_half_open_connections", allow_threads(&session::set_max_half_open_connections)) - .def("num_connections", allow_threads(&session::num_connections)) - .def("set_settings", allow_threads(&session::set_settings)) - .def("settings", allow_threads(&session::settings)) -#ifndef TORRENT_DISABLE_ENCRYPTION - .def("set_pe_settings", allow_threads(&session::set_pe_settings)) - .def("get_pe_settings", allow_threads(&session::get_pe_settings)) -#endif -#ifndef TORRENT_DISABLE_GEO_IP - .def("load_asnum_db", &load_asnum_db) - .def("load_country_db", &load_country_db) -#endif - .def("load_state", load_state0) - .def("save_state", &save_state, (arg("entry"), arg("flags") = 0xffffffff)) -#ifndef TORRENT_NO_DEPRECATE - .def("load_state", load_state1) - .def("set_severity_level", allow_threads(&session::set_severity_level)) -#endif - .def("set_alert_mask", allow_threads(&session::set_alert_mask)) - .def("set_alert_queue_size_limit", allow_threads(&session::set_alert_queue_size_limit)) - .def("pop_alert", allow_threads(&session::pop_alert)) - .def("wait_for_alert", &wait_for_alert, return_internal_reference<>()) - .def("add_extension", &add_extension) -#ifndef TORRENT_NO_DEPRECATE - .def("set_peer_proxy", allow_threads(&session::set_peer_proxy)) - .def("set_tracker_proxy", allow_threads(&session::set_tracker_proxy)) - .def("set_web_seed_proxy", allow_threads(&session::set_web_seed_proxy)) - .def("peer_proxy", allow_threads(&session::peer_proxy)) - .def("tracker_proxy", allow_threads(&session::tracker_proxy)) - .def("web_seed_proxy", allow_threads(&session::web_seed_proxy)) -#endif - .def("set_proxy", allow_threads(&session::set_proxy)) - .def("proxy", allow_threads(&session::proxy)) - .def("start_upnp", &start_upnp) - .def("stop_upnp", allow_threads(&session::stop_upnp)) - .def("start_lsd", allow_threads(&session::start_lsd)) - .def("stop_lsd", allow_threads(&session::stop_lsd)) - .def("start_natpmp", &start_natpmp) - .def("stop_natpmp", allow_threads(&session::stop_natpmp)) - .def("set_ip_filter", allow_threads(&session::set_ip_filter)) - .def("get_ip_filter", allow_threads(&session::get_ip_filter)) - .def("find_torrent", allow_threads(&session::find_torrent)) - .def("get_torrents", &get_torrents) - .def("pause", allow_threads(&session::pause)) - .def("resume", allow_threads(&session::resume)) - .def("is_paused", allow_threads(&session::is_paused)) - .def("id", allow_threads(&session::id)) - .def("get_cache_status", allow_threads(&session::get_cache_status)) - ; - - enum_("save_state_flags_t") - .value("save_settings", session::save_settings) - .value("save_dht_settings", session::save_dht_settings) - .value("save_dht_proxy", session::save_dht_proxy) - .value("save_dht_state", session::save_dht_state) - .value("save_i2p_proxy", session::save_i2p_proxy) - .value("save_encryption_settings", session:: save_encryption_settings) - .value("save_peer_proxy", session::save_peer_proxy) - .value("save_web_proxy", session::save_web_proxy) - .value("save_tracker_proxy", session::save_tracker_proxy) - .value("save_as_map", session::save_as_map) - ; - - register_ptr_to_python >(); -} diff --git a/libtorrent_utp/bindings/python/src/session_settings.cpp b/libtorrent_utp/bindings/python/src/session_settings.cpp deleted file mode 100644 index bb6ece9ed..000000000 --- a/libtorrent_utp/bindings/python/src/session_settings.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -void bind_session_settings() -{ - class_("session_settings") - .def_readwrite("user_agent", &session_settings::user_agent) - .def_readwrite("tracker_completion_timeout", &session_settings::tracker_completion_timeout) - .def_readwrite("tracker_receive_timeout", &session_settings::tracker_receive_timeout) - .def_readwrite("stop_tracker_timeout", &session_settings::stop_tracker_timeout) - .def_readwrite("tracker_maximum_response_length", &session_settings::tracker_maximum_response_length) - .def_readwrite("piece_timeout", &session_settings::piece_timeout) - .def_readwrite("request_timeout", &session_settings::request_timeout) - .def_readwrite("request_queue_time", &session_settings::request_queue_time) - .def_readwrite("max_allowed_in_request_queue", &session_settings::max_allowed_in_request_queue) - .def_readwrite("max_out_request_queue", &session_settings::max_out_request_queue) - .def_readwrite("whole_pieces_threshold", &session_settings::whole_pieces_threshold) - .def_readwrite("peer_timeout", &session_settings::peer_timeout) - .def_readwrite("urlseed_timeout", &session_settings::urlseed_timeout) - .def_readwrite("urlseed_pipeline_size", &session_settings::urlseed_pipeline_size) - .def_readwrite("urlseed_wait_retry", &session_settings::urlseed_wait_retry) - .def_readwrite("file_pool_size", &session_settings::file_pool_size) - .def_readwrite("allow_multiple_connections_per_ip", &session_settings::allow_multiple_connections_per_ip) - .def_readwrite("max_failcount", &session_settings::max_failcount) - .def_readwrite("min_reconnect_time", &session_settings::min_reconnect_time) - .def_readwrite("peer_connect_timeout", &session_settings::peer_connect_timeout) - .def_readwrite("ignore_limits_on_local_network", &session_settings::ignore_limits_on_local_network) - .def_readwrite("connection_speed", &session_settings::connection_speed) - .def_readwrite("send_redundant_have", &session_settings::send_redundant_have) - .def_readwrite("lazy_bitfields", &session_settings::lazy_bitfields) - .def_readwrite("inactivity_timeout", &session_settings::inactivity_timeout) - .def_readwrite("unchoke_interval", &session_settings::unchoke_interval) - .def_readwrite("optimistic_unchoke_interval", &session_settings::optimistic_unchoke_interval) - .def_readwrite("num_want", &session_settings::num_want) - .def_readwrite("initial_picker_threshold", &session_settings::initial_picker_threshold) - .def_readwrite("allowed_fast_set_size", &session_settings::allowed_fast_set_size) - .def_readwrite("max_queued_disk_bytes", &session_settings::max_queued_disk_bytes) - .def_readwrite("handshake_timeout", &session_settings::handshake_timeout) -#ifndef TORRENT_DISABLE_DHT - .def_readwrite("use_dht_as_fallback", &session_settings::use_dht_as_fallback) -#endif - .def_readwrite("free_torrent_hashes", &session_settings::free_torrent_hashes) - .def_readwrite("upnp_ignore_nonrouters", &session_settings::upnp_ignore_nonrouters) - .def_readwrite("send_buffer_watermark", &session_settings::send_buffer_watermark) -#ifndef TORRENT_NO_DEPRECATE - .def_readwrite("auto_upload_slots", &session_settings::auto_upload_slots) - .def_readwrite("auto_upload_slots_rate_based", &session_settings::auto_upload_slots_rate_based) -#endif - .def_readwrite("choking_algorithm", &session_settings::choking_algorithm) - .def_readwrite("use_parole_mode", &session_settings::use_parole_mode) - .def_readwrite("cache_size", &session_settings::cache_size) - .def_readwrite("cache_buffer_chunk_size", &session_settings::cache_buffer_chunk_size) - .def_readwrite("cache_expiry", &session_settings::cache_expiry) - .def_readwrite("use_read_cache", &session_settings::use_read_cache) - .def_readwrite("disk_io_write_mode", &session_settings::disk_io_write_mode) - .def_readwrite("disk_io_read_mode", &session_settings::disk_io_read_mode) - .def_readwrite("coalesce_reads", &session_settings::coalesce_reads) - .def_readwrite("coalesce_writes", &session_settings::coalesce_writes) - .def_readwrite("outgoing_ports", &session_settings::outgoing_ports) - .def_readwrite("peer_tos", &session_settings::peer_tos) - .def_readwrite("active_downloads", &session_settings::active_downloads) - .def_readwrite("active_seeds", &session_settings::active_seeds) - .def_readwrite("active_dht_limit", &session_settings::active_dht_limit) - .def_readwrite("active_tracker_limit", &session_settings::active_tracker_limit) - .def_readwrite("active_lsd_limit", &session_settings::active_lsd_limit) - .def_readwrite("active_limit", &session_settings::active_limit) - .def_readwrite("auto_manage_prefer_seeds", &session_settings::auto_manage_prefer_seeds) - .def_readwrite("dont_count_slow_torrents", &session_settings::dont_count_slow_torrents) - .def_readwrite("auto_manage_interval", &session_settings::auto_manage_interval) - .def_readwrite("share_ratio_limit", &session_settings::share_ratio_limit) - .def_readwrite("seed_time_ratio_limit", &session_settings::seed_time_ratio_limit) - .def_readwrite("seed_time_limit", &session_settings::seed_time_limit) - .def_readwrite("peer_turnover_interval", &session_settings::peer_turnover_interval) - .def_readwrite("peer_turnover", &session_settings::peer_turnover) - .def_readwrite("peer_turnover_cutoff", &session_settings::peer_turnover_cutoff) - .def_readwrite("close_redundant_connections", &session_settings::close_redundant_connections) - .def_readwrite("auto_scrape_interval", &session_settings::auto_scrape_interval) - .def_readwrite("auto_scrape_min_interval", &session_settings::auto_scrape_min_interval) - .def_readwrite("max_peerlist_size", &session_settings::max_peerlist_size) - .def_readwrite("max_paused_peerlist_size", &session_settings::max_paused_peerlist_size) - .def_readwrite("min_announce_interval", &session_settings::min_announce_interval) - .def_readwrite("prioritize_partial_pieces", &session_settings::prioritize_partial_pieces) - .def_readwrite("auto_manage_startup", &session_settings::auto_manage_startup) - .def_readwrite("rate_limit_ip_overhead", &session_settings::rate_limit_ip_overhead) - .def_readwrite("announce_to_all_trackers", &session_settings::announce_to_all_trackers) - .def_readwrite("announce_to_all_tiers", &session_settings::announce_to_all_tiers) - .def_readwrite("prefer_udp_trackers", &session_settings::prefer_udp_trackers) - .def_readwrite("strict_super_seeding", &session_settings::strict_super_seeding) - .def_readwrite("seeding_piece_quota", &session_settings::seeding_piece_quota) - .def_readwrite("max_sparse_regions", &session_settings::max_sparse_regions) -#ifndef TORRENT_DISABLE_MLOCK - .def_readwrite("lock_disk_cache", &session_settings::lock_disk_cache) -#endif - .def_readwrite("max_rejects", &session_settings::max_rejects) - .def_readwrite("recv_socket_buffer_size", &session_settings::recv_socket_buffer_size) - .def_readwrite("send_socket_buffer_size", &session_settings::send_socket_buffer_size) - .def_readwrite("optimize_hashing_for_speed", &session_settings::optimize_hashing_for_speed) - .def_readwrite("file_checks_delay_per_block", &session_settings::file_checks_delay_per_block) - .def_readwrite("disk_cache_algorithm", &session_settings::disk_cache_algorithm) - .def_readwrite("read_cache_line_size", &session_settings::read_cache_line_size) - .def_readwrite("write_cache_line_size", &session_settings::write_cache_line_size) - .def_readwrite("optimistic_disk_retry", &session_settings::optimistic_disk_retry) - .def_readwrite("disable_hash_checks", &session_settings::disable_hash_checks) - .def_readwrite("allow_reordered_disk_operations", &session_settings::allow_reordered_disk_operations) - .def_readwrite("allow_i2p_mixed", &session_settings::allow_i2p_mixed) - .def_readwrite("max_suggest_pieces", &session_settings::max_suggest_pieces) - .def_readwrite("drop_skipped_requests", &session_settings::drop_skipped_requests) - .def_readwrite("low_prio_disk", &session_settings::low_prio_disk) - .def_readwrite("local_service_announce_interval", &session_settings::local_service_announce_interval) - .def_readwrite("dht_announce_interval", &session_settings::dht_announce_interval) - .def_readwrite("udp_tracker_token_expiry", &session_settings::udp_tracker_token_expiry) - .def_readwrite("volatile_read_cache", &session_settings::volatile_read_cache) - .def_readwrite("guided_read_cache", &session_settings::guided_read_cache) - .def_readwrite("default_cache_min_age", &session_settings::default_cache_min_age) - .def_readwrite("num_optimistic_unchoke_slots", &session_settings::num_optimistic_unchoke_slots) - .def_readwrite("no_atime_storage", &session_settings::no_atime_storage) - .def_readwrite("default_est_reciprocation_rate", &session_settings::default_est_reciprocation_rate) - .def_readwrite("increase_est_reciprocation_rate", &session_settings::increase_est_reciprocation_rate) - .def_readwrite("decrease_est_reciprocation_rate", &session_settings::decrease_est_reciprocation_rate) - .def_readwrite("incoming_starts_queued_torrents", &session_settings::incoming_starts_queued_torrents) - .def_readwrite("report_true_downoaded", &session_settings::report_true_downloaded) - .def_readwrite("strict_end_game_mode", &session_settings::strict_end_game_mode) - .def_readwrite("broadcast_lsd", &session_settings::broadcast_lsd) - .def_readwrite("ignore_resume_timestamps", &session_settings::ignore_resume_timestamps) - .def_readwrite("anonymous_mode", &session_settings::anonymous_mode) - .def_readwrite("tick_interval", &session_settings::tick_interval) - .def_readwrite("report_web_seed_downloads", &session_settings::report_web_seed_downloads) - .def_readwrite("share_mode_target", &session_settings::share_mode_target) - ; - - enum_("proxy_type") - .value("none", proxy_settings::none) - .value("socks4", proxy_settings::socks4) - .value("socks5", proxy_settings::socks5) - .value("socks5_pw", proxy_settings::socks5_pw) - .value("http", proxy_settings::http) - .value("http_pw", proxy_settings::http_pw) - ; - - enum_("disk_cache_algo_t") - .value("lru", session_settings::lru) - .value("largest_contiguous", session_settings::largest_contiguous) - ; - - enum_("choking_algorithm_t") - .value("fixed_slots_choker", session_settings::fixed_slots_choker) - .value("auto_expand_choker", session_settings::auto_expand_choker) - .value("rate_based_choker", session_settings::rate_based_choker) - .value("bittyrant_choker", session_settings::bittyrant_choker) - ; - - enum_("io_buffer_mode_t") - .value("enable_os_cache", session_settings::enable_os_cache) - .value("disable_os_cache_for_aligned_files", session_settings::disable_os_cache_for_aligned_files) - .value("disable_os_cache", session_settings::disable_os_cache) - ; - - class_("proxy_settings") - .def_readwrite("hostname", &proxy_settings::hostname) - .def_readwrite("port", &proxy_settings::port) - .def_readwrite("password", &proxy_settings::password) - .def_readwrite("username", &proxy_settings::username) - .def_readwrite("type", &proxy_settings::type) - ; - -#ifndef TORRENT_DISABLE_DHT - class_("dht_settings") - .def_readwrite("max_peers_reply", &dht_settings::max_peers_reply) - .def_readwrite("search_branching", &dht_settings::search_branching) - .def_readwrite("service_port", &dht_settings::service_port) - .def_readwrite("max_fail_count", &dht_settings::max_fail_count) - ; -#endif - -#ifndef TORRENT_DISABLE_ENCRYPTION - enum_("enc_policy") - .value("forced", pe_settings::forced) - .value("enabled", pe_settings::enabled) - .value("disabled", pe_settings::disabled) - ; - - enum_("enc_level") - .value("rc4", pe_settings::rc4) - .value("plaintext", pe_settings::plaintext) - .value("both", pe_settings::both) - ; - - class_("pe_settings") - .def_readwrite("out_enc_policy", &pe_settings::out_enc_policy) - .def_readwrite("in_enc_policy", &pe_settings::in_enc_policy) - .def_readwrite("allowed_enc_level", &pe_settings::allowed_enc_level) - .def_readwrite("prefer_rc4", &pe_settings::prefer_rc4) - ; -#endif - -} diff --git a/libtorrent_utp/bindings/python/src/torrent.cpp b/libtorrent_utp/bindings/python/src/torrent.cpp deleted file mode 100644 index 048c2257e..000000000 --- a/libtorrent_utp/bindings/python/src/torrent.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Daniel Wallin 2007. 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) - -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -void bind_torrent() -{ - class_("torrent", no_init); -} - diff --git a/libtorrent_utp/bindings/python/src/torrent_handle.cpp b/libtorrent_utp/bindings/python/src/torrent_handle.cpp deleted file mode 100644 index aefab9cad..000000000 --- a/libtorrent_utp/bindings/python/src/torrent_handle.cpp +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include -#include -#include -#include "gil.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - - list url_seeds(torrent_handle& handle) - { - list ret; - std::set urls; - { - allow_threading_guard guard; - urls = handle.url_seeds(); - } - - for (std::set::iterator i(urls.begin()) - , end(urls.end()); i != end; ++i) - ret.append(*i); - return ret; - } - - list piece_availability(torrent_handle& handle) - { - list ret; - std::vector avail; - { - allow_threading_guard guard; - handle.piece_availability(avail); - } - - for (std::vector::iterator i(avail.begin()) - , end(avail.end()); i != end; ++i) - ret.append(*i); - return ret; - } - - list piece_priorities(torrent_handle& handle) - { - list ret; - std::vector prio; - { - allow_threading_guard guard; - prio = handle.piece_priorities(); - } - - for (std::vector::iterator i(prio.begin()) - , end(prio.end()); i != end; ++i) - ret.append(*i); - return ret; - } - -} // namespace unnamed - -list file_progress(torrent_handle& handle) -{ - std::vector p; - - { - allow_threading_guard guard; - p.reserve(handle.get_torrent_info().num_files()); - handle.file_progress(p); - } - - list result; - - for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) - result.append(*i); - - return result; -} - -list get_peer_info(torrent_handle const& handle) -{ - std::vector pi; - - { - allow_threading_guard guard; - handle.get_peer_info(pi); - } - - list result; - - for (std::vector::iterator i = pi.begin(); i != pi.end(); ++i) - result.append(*i); - - return result; -} - -void prioritize_pieces(torrent_handle& info, object o) -{ - std::vector result; - try - { - object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) )); - while( 1 ) - { - object obj = extract( iter_obj.attr( "next" )() ); - result.push_back(extract( obj )); - } - } - catch( error_already_set ) - { - PyErr_Clear(); - info.prioritize_pieces(result); - return; - } -} - -void prioritize_files(torrent_handle& info, object o) -{ - std::vector result; - try - { - object iter_obj = object( handle<>( PyObject_GetIter( o.ptr() ) )); - while( 1 ) - { - object obj = extract( iter_obj.attr( "next" )() ); - result.push_back(extract( obj )); - } - } - catch( error_already_set ) - { - PyErr_Clear(); - info.prioritize_files(result); - return; - } -} - -list file_priorities(torrent_handle& handle) -{ - list ret; - std::vector priorities = handle.file_priorities(); - - for (std::vector::iterator i = priorities.begin(); i != priorities.end(); ++i) - ret.append(*i); - - return ret; -} - -void replace_trackers(torrent_handle& h, object trackers) -{ - object iter(trackers.attr("__iter__")()); - - std::vector result; - - for (;;) - { - handle<> entry(allow_null(PyIter_Next(iter.ptr()))); - - if (entry == handle<>()) - break; - - result.push_back(extract(object(entry))); - } - - allow_threading_guard guard; - h.replace_trackers(result); -} - -list trackers(torrent_handle &h) -{ - list ret; - std::vector const trackers = h.trackers(); - for (std::vector::const_iterator i = trackers.begin(), end(trackers.end()); i != end; ++i) - { - dict d; - d["url"] = i->url; - d["tier"] = i->tier; - d["fail_limit"] = i->fail_limit; - d["fails"] = i->fails; - d["source"] = i->source; - d["verified"] = i->verified; - d["updating"] = i->updating; - d["start_sent"] = i->start_sent; - d["complete_sent"] = i->complete_sent; - ret.append(d); - } - return ret; -} - -list get_download_queue(torrent_handle& handle) -{ - using boost::python::make_tuple; - - list ret; - - std::vector downloading; - - { - allow_threading_guard guard; - handle.get_download_queue(downloading); - } - - for (std::vector::iterator i = downloading.begin() - , end(downloading.end()); i != end; ++i) - { - dict partial_piece; - partial_piece["piece_index"] = i->piece_index; - partial_piece["blocks_in_piece"] = i->blocks_in_piece; - list block_list; - for (int k = 0; k < i->blocks_in_piece; ++k) - { - dict block_info; - block_info["state"] = i->blocks[k].state; - block_info["num_peers"] = i->blocks[k].num_peers; - block_info["bytes_progress"] = i->blocks[k].bytes_progress; - block_info["block_size"] = i->blocks[k].block_size; - block_info["peer"] = make_tuple( - boost::lexical_cast(i->blocks[k].peer().address()), i->blocks[k].peer().port()); - block_list.append(block_info); - } - partial_piece["blocks"] = block_list; - - ret.append(partial_piece); - } - - return ret; -} - -namespace -{ - tcp::endpoint tuple_to_endpoint(tuple const& t) - { - return tcp::endpoint(address::from_string(extract(t[0])), extract(t[1])); - } -} - -void force_reannounce(torrent_handle& th, int s) -{ - th.force_reannounce(boost::posix_time::seconds(s)); -} - -void connect_peer(torrent_handle& th, tuple ip, int source) -{ - th.connect_peer(tuple_to_endpoint(ip), source); -} - -void set_peer_upload_limit(torrent_handle& th, tuple const& ip, int limit) -{ - th.set_peer_upload_limit(tuple_to_endpoint(ip), limit); -} - -void set_peer_download_limit(torrent_handle& th, tuple const& ip, int limit) -{ - th.set_peer_download_limit(tuple_to_endpoint(ip), limit); -} - -void add_piece(torrent_handle& th, int piece, char const *data, int flags) -{ - th.add_piece(piece, data, flags); -} - -void bind_torrent_handle() -{ - void (torrent_handle::*force_reannounce0)() const = &torrent_handle::force_reannounce; - - int (torrent_handle::*piece_priority0)(int) const = &torrent_handle::piece_priority; - void (torrent_handle::*piece_priority1)(int, int) const = &torrent_handle::piece_priority; - - void (torrent_handle::*move_storage0)(std::string const&) const = &torrent_handle::move_storage; - void (torrent_handle::*rename_file0)(int, std::string const&) const = &torrent_handle::rename_file; - -#if TORRENT_USE_WSTRING - void (torrent_handle::*move_storage1)(std::wstring const&) const = &torrent_handle::move_storage; - void (torrent_handle::*rename_file1)(int, std::wstring const&) const = &torrent_handle::rename_file; -#endif - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - bool (torrent_handle::*resolve_countries0)() const = &torrent_handle::resolve_countries; - void (torrent_handle::*resolve_countries1)(bool) = &torrent_handle::resolve_countries; -#endif - -#define _ allow_threads - - class_("torrent_handle") - .def("get_peer_info", get_peer_info) - .def("status", _(&torrent_handle::status)) - .def("get_download_queue", get_download_queue) - .def("file_progress", file_progress) - .def("trackers", trackers) - .def("replace_trackers", replace_trackers) - .def("add_url_seed", _(&torrent_handle::add_url_seed)) - .def("remove_url_seed", _(&torrent_handle::remove_url_seed)) - .def("url_seeds", url_seeds) - .def("has_metadata", _(&torrent_handle::has_metadata)) - .def("get_torrent_info", _(&torrent_handle::get_torrent_info), return_internal_reference<>()) - .def("is_valid", _(&torrent_handle::is_valid)) - .def("is_seed", _(&torrent_handle::is_seed)) - .def("is_finished", _(&torrent_handle::is_finished)) - .def("is_paused", _(&torrent_handle::is_paused)) - .def("pause", _(&torrent_handle::pause), arg("flags") = 0) - .def("resume", _(&torrent_handle::resume)) - .def("clear_error", _(&torrent_handle::clear_error)) - .def("set_priority", _(&torrent_handle::set_priority)) - - .def("is_auto_managed", _(&torrent_handle::is_auto_managed)) - .def("auto_managed", _(&torrent_handle::auto_managed)) - .def("queue_position", _(&torrent_handle::queue_position)) - .def("queue_position_up", _(&torrent_handle::queue_position_up)) - .def("queue_position_down", _(&torrent_handle::queue_position_down)) - .def("queue_position_top", _(&torrent_handle::queue_position_top)) - .def("queue_position_bottom", _(&torrent_handle::queue_position_bottom)) - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - .def("resolve_countries", _(resolve_countries0)) - .def("resolve_countries", _(resolve_countries1)) -#endif - // deprecated -#ifndef TORRENT_NO_DEPRECATE - .def("filter_piece", _(&torrent_handle::filter_piece)) - .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered)) - .def("write_resume_data", _(&torrent_handle::write_resume_data)) -#endif - .def("add_piece", add_piece) - .def("read_piece", _(&torrent_handle::read_piece)) - .def("set_piece_deadline", _(&torrent_handle::set_piece_deadline) - , (arg("index"), arg("deadline"), arg("flags") = 0)) - .def("piece_availability", &piece_availability) - .def("piece_priority", _(piece_priority0)) - .def("piece_priority", _(piece_priority1)) - .def("prioritize_pieces", &prioritize_pieces) - .def("piece_priorities", &piece_priorities) - .def("prioritize_files", &prioritize_files) - .def("file_priorities", &file_priorities) - .def("use_interface", &torrent_handle::use_interface) - .def("save_resume_data", _(&torrent_handle::save_resume_data), arg("flags") = 0) - .def("need_save_resume_data", _(&torrent_handle::need_save_resume_data)) - .def("force_reannounce", _(force_reannounce0)) - .def("force_reannounce", &force_reannounce) - .def("force_dht_announce", _(&torrent_handle::force_dht_announce)) - .def("scrape_tracker", _(&torrent_handle::scrape_tracker)) - .def("name", _(&torrent_handle::name)) - .def("set_upload_mode", _(&torrent_handle::set_upload_mode)) - .def("set_share_mode", _(&torrent_handle::set_share_mode)) - .def("set_upload_limit", _(&torrent_handle::set_upload_limit)) - .def("upload_limit", _(&torrent_handle::upload_limit)) - .def("set_download_limit", _(&torrent_handle::set_download_limit)) - .def("download_limit", _(&torrent_handle::download_limit)) - .def("set_sequential_download", _(&torrent_handle::set_sequential_download)) - .def("set_peer_upload_limit", &set_peer_upload_limit) - .def("set_peer_download_limit", &set_peer_download_limit) - .def("connect_peer", &connect_peer) - .def("set_ratio", _(&torrent_handle::set_ratio)) - .def("save_path", _(&torrent_handle::save_path)) - .def("set_max_uploads", _(&torrent_handle::set_max_uploads)) - .def("set_max_connections", _(&torrent_handle::set_max_connections)) - .def("set_tracker_login", _(&torrent_handle::set_tracker_login)) - .def("move_storage", _(move_storage0)) - .def("info_hash", _(&torrent_handle::info_hash)) - .def("force_recheck", _(&torrent_handle::force_recheck)) - .def("rename_file", _(rename_file0)) -#if TORRENT_USE_WSTRING - .def("move_storage", _(move_storage1)) - .def("rename_file", _(rename_file1)) -#endif - ; - - enum_("pause_flags_t") - .value("graceful_pause", torrent_handle::graceful_pause) - ; - - enum_("save_resume_flags_t") - .value("flush_disk_cache", torrent_handle::flush_disk_cache) - ; - - enum_("deadline_flags") - .value("alert_when_available", torrent_handle::alert_when_available) - ; - -} diff --git a/libtorrent_utp/bindings/python/src/torrent_info.cpp b/libtorrent_utp/bindings/python/src/torrent_info.cpp deleted file mode 100644 index 64439b37c..000000000 --- a/libtorrent_utp/bindings/python/src/torrent_info.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include "libtorrent/intrusive_ptr_base.hpp" - -using namespace boost::python; -using namespace libtorrent; - -namespace -{ - - std::vector::const_iterator begin_trackers(torrent_info& i) - { - return i.trackers().begin(); - } - - std::vector::const_iterator end_trackers(torrent_info& i) - { - return i.trackers().end(); - } - - void add_node(torrent_info& ti, char const* hostname, int port) - { - ti.add_node(std::make_pair(hostname, port)); - } - - list nodes(torrent_info const& ti) - { - list result; - - typedef std::vector > list_type; - - for (list_type::const_iterator i = ti.nodes().begin(); i != ti.nodes().end(); ++i) - { - result.append(make_tuple(i->first, i->second)); - } - - return result; - } - - file_storage::iterator begin_files(torrent_info& i) - { - return i.begin_files(); - } - - file_storage::iterator end_files(torrent_info& i) - { - return i.end_files(); - } - - //list files(torrent_info const& ti, bool storage) { - list files(torrent_info const& ti, bool storage) { - list result; - - typedef std::vector list_type; - - for (list_type::const_iterator i = ti.begin_files(); i != ti.end_files(); ++i) - result.append(*i); - - return result; - } - - std::string metadata(torrent_info const& ti) { - std::string result(ti.metadata().get(), ti.metadata_size()); - return result; - } - - torrent_info construct0(std::string path) { - return torrent_info(path); - } - - list map_block(torrent_info& ti, int piece, size_type offset, int size) - { - std::vector p = ti.map_block(piece, offset, size); - list result; - - for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) - result.append(*i); - - return result; - } - - bool get_tier(announce_entry const& ae) - { return ae.tier; } - bool get_fail_limit(announce_entry const& ae) - { return ae.fail_limit; } - bool get_fails(announce_entry const& ae) - { return ae.fails; } - bool get_source(announce_entry const& ae) - { return ae.source; } - bool get_verified(announce_entry const& ae) - { return ae.verified; } - bool get_updating(announce_entry const& ae) - { return ae.updating; } - bool get_start_sent(announce_entry const& ae) - { return ae.start_sent; } - bool get_complete_sent(announce_entry const& ae) - { return ae.complete_sent; } - bool get_send_stats(announce_entry const& ae) - { return ae.send_stats; } - - - bool get_size(file_entry const& fe) - { return fe.size; } - bool get_offset(file_entry const& fe) - { return fe.offset; } - bool get_pad_file(file_entry const& fe) - { return fe.pad_file; } - bool get_executable_attribute(file_entry const& fe) - { return fe.executable_attribute; } - bool get_hidden_attribute(file_entry const& fe) - { return fe.hidden_attribute; } - bool get_symlink_attribute(file_entry const& fe) - { return fe.symlink_attribute; } - -} // namespace unnamed - -void bind_torrent_info() -{ - return_value_policy copy; - - void (torrent_info::*rename_file0)(int, std::string const&) = &torrent_info::rename_file; -#if TORRENT_USE_WSTRING - void (torrent_info::*rename_file1)(int, std::wstring const&) = &torrent_info::rename_file; -#endif - - class_("file_slice") - .def_readwrite("file_index", &file_slice::file_index) - .def_readwrite("offset", &file_slice::offset) - .def_readwrite("size", &file_slice::size) - ; - - class_ >("torrent_info", no_init) -#ifndef TORRENT_NO_DEPRECATE - .def(init()) -#endif - .def(init()) - .def(init()) - .def(init()) -#if TORRENT_USE_WSTRING - .def(init()) -#endif - - .def("add_tracker", &torrent_info::add_tracker, arg("url")) - .def("add_url_seed", &torrent_info::add_url_seed) - - .def("name", &torrent_info::name, copy) - .def("comment", &torrent_info::comment, copy) - .def("creator", &torrent_info::creator, copy) - .def("total_size", &torrent_info::total_size) - .def("piece_length", &torrent_info::piece_length) - .def("num_pieces", &torrent_info::num_pieces) -#ifndef TORRENT_NO_DEPRECATE - .def("info_hash", &torrent_info::info_hash, copy) -#endif - .def("hash_for_piece", &torrent_info::hash_for_piece) - .def("piece_size", &torrent_info::piece_size) - - .def("num_files", &torrent_info::num_files, (arg("storage")=false)) - .def("file_at", &torrent_info::file_at, return_internal_reference<>()) - .def("file_at_offset", &torrent_info::file_at_offset) - .def("files", &files, (arg("storage")=false)) - .def("rename_file", rename_file0) -#if TORRENT_USE_WSTRING - .def("rename_file", rename_file1) -#endif - - .def("priv", &torrent_info::priv) - .def("trackers", range(begin_trackers, end_trackers)) - - .def("creation_date", &torrent_info::creation_date) - - .def("add_node", &add_node) - .def("nodes", &nodes) - .def("metadata", &metadata) - .def("metadata_size", &torrent_info::metadata_size) - .def("map_block", map_block) - .def("map_file", &torrent_info::map_file) - ; - - class_("file_entry") - .def("filename", &file_entry::filename) - .def("set_name", &file_entry::set_name) - .add_property("pad_file", &get_pad_file) - .add_property("executable_attribute", &get_executable_attribute) - .add_property("hidden_attribute", &get_hidden_attribute) - .add_property("symlink_attribute", &get_symlink_attribute) - .add_property("offset", &get_offset) - .add_property("size", &get_size) - ; - - class_("announce_entry", init()) - .def_readwrite("url", &announce_entry::url) - .add_property("tier", &get_tier) - .add_property("fail_limit", &get_fail_limit) - .add_property("fails", &get_fails) - .add_property("source", &get_source) - .add_property("verified", &get_verified) - .add_property("updating", &get_updating) - .add_property("start_sent", &get_start_sent) - .add_property("complete_sent", &get_complete_sent) - .add_property("send_stats", &get_send_stats) - - .def("reset", &announce_entry::reset) - .def("failed", &announce_entry::failed, arg("retry_interval") = 0) - .def("can_announce", &announce_entry::can_announce) - .def("is_working", &announce_entry::is_working) - .def("trim", &announce_entry::trim) - ; - - enum_("tracker_source") - .value("source_torrent", announce_entry::source_torrent) - .value("source_client", announce_entry::source_client) - .value("source_magnet_link", announce_entry::source_magnet_link) - .value("source_tex", announce_entry::source_tex) - ; -} - diff --git a/libtorrent_utp/bindings/python/src/torrent_status.cpp b/libtorrent_utp/bindings/python/src/torrent_status.cpp deleted file mode 100644 index 471359bfc..000000000 --- a/libtorrent_utp/bindings/python/src/torrent_status.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -object pieces(torrent_status const& s) -{ - list result; - - for (bitfield::const_iterator i(s.pieces.begin()), e(s.pieces.end()); i != e; ++i) - result.append(*i); - - return result; -} - -void bind_torrent_status() -{ - scope status = class_("torrent_status") - .def_readonly("state", &torrent_status::state) - .def_readonly("paused", &torrent_status::paused) - .def_readonly("auto_managed", &torrent_status::auto_managed) - .def_readonly("sequential_download", &torrent_status::sequential_download) - .def_readonly("is_seeding", &torrent_status::is_seeding) - .def_readonly("is_finished", &torrent_status::is_finished) - .def_readonly("has_metadata", &torrent_status::has_metadata) - .def_readonly("progress", &torrent_status::progress) - .def_readonly("progress_ppm", &torrent_status::progress_ppm) - .add_property( - "next_announce" - , make_getter( - &torrent_status::next_announce, return_value_policy() - ) - ) - .add_property( - "announce_interval" - , make_getter( - &torrent_status::announce_interval, return_value_policy() - ) - ) - .def_readonly("current_tracker", &torrent_status::current_tracker) - .def_readonly("total_download", &torrent_status::total_download) - .def_readonly("total_upload", &torrent_status::total_upload) - .def_readonly("total_payload_download", &torrent_status::total_payload_download) - .def_readonly("total_payload_upload", &torrent_status::total_payload_upload) - .def_readonly("total_failed_bytes", &torrent_status::total_failed_bytes) - .def_readonly("total_redundant_bytes", &torrent_status::total_redundant_bytes) - .def_readonly("download_rate", &torrent_status::download_rate) - .def_readonly("upload_rate", &torrent_status::upload_rate) - .def_readonly("download_payload_rate", &torrent_status::download_payload_rate) - .def_readonly("upload_payload_rate", &torrent_status::upload_payload_rate) - .def_readonly("num_seeds", &torrent_status::num_seeds) - .def_readonly("num_peers", &torrent_status::num_peers) - .def_readonly("num_complete", &torrent_status::num_complete) - .def_readonly("num_incomplete", &torrent_status::num_incomplete) - .def_readonly("list_seeds", &torrent_status::list_seeds) - .def_readonly("list_peers", &torrent_status::list_peers) - .add_property("pieces", pieces) - .def_readonly("num_pieces", &torrent_status::num_pieces) - .def_readonly("total_done", &torrent_status::total_done) - .def_readonly("total_wanted_done", &torrent_status::total_wanted_done) - .def_readonly("total_wanted", &torrent_status::total_wanted) - .def_readonly("distributed_full_copies", &torrent_status::distributed_full_copies) - .def_readonly("distributed_fraction", &torrent_status::distributed_fraction) - .def_readonly("distributed_copies", &torrent_status::distributed_copies) - .def_readonly("block_size", &torrent_status::block_size) - .def_readonly("num_uploads", &torrent_status::num_uploads) - .def_readonly("num_connections", &torrent_status::num_connections) - .def_readonly("uploads_limit", &torrent_status::uploads_limit) - .def_readonly("connections_limit", &torrent_status::connections_limit) - .def_readonly("storage_mode", &torrent_status::storage_mode) - .def_readonly("up_bandwidth_queue", &torrent_status::up_bandwidth_queue) - .def_readonly("down_bandwidth_queue", &torrent_status::down_bandwidth_queue) - .def_readonly("all_time_upload", &torrent_status::all_time_upload) - .def_readonly("all_time_download", &torrent_status::all_time_download) - .def_readonly("active_time", &torrent_status::active_time) - .def_readonly("finished_time", &torrent_status::finished_time) - .def_readonly("seeding_time", &torrent_status::seeding_time) - .def_readonly("seed_rank", &torrent_status::seed_rank) - .def_readonly("last_scrape", &torrent_status::last_scrape) - .def_readonly("has_incoming", &torrent_status::has_incoming) - .def_readonly("sparse_regions", &torrent_status::sparse_regions) - .def_readonly("seed_mode", &torrent_status::seed_mode) - .def_readonly("upload_mode", &torrent_status::upload_mode) - .def_readonly("share_mode", &torrent_status::share_mode) - .def_readonly("error", &torrent_status::error) - .def_readonly("priority", &torrent_status::priority) - .def_readonly("added_time", &torrent_status::added_time) - .def_readonly("completed_time", &torrent_status::completed_time) - .def_readonly("last_seen_complete", &torrent_status::last_seen_complete) - .def_readonly("time_since_upload", &torrent_status::time_since_upload) - .def_readonly("time_since_download", &torrent_status::time_since_download) - .def_readonly("queue_position", &torrent_status::queue_position) - .def_readonly("need_save_resume", &torrent_status::need_save_resume) - ; - - enum_("states") - .value("queued_for_checking", torrent_status::queued_for_checking) - .value("checking_files", torrent_status::checking_files) - .value("downloading", torrent_status::downloading) - .value("finished", torrent_status::finished) - .value("seeding", torrent_status::seeding) - .value("allocating", torrent_status::allocating) - .export_values() - ; -} - diff --git a/libtorrent_utp/bindings/python/src/utility.cpp b/libtorrent_utp/bindings/python/src/utility.cpp deleted file mode 100644 index 9ae2d2a93..000000000 --- a/libtorrent_utp/bindings/python/src/utility.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include -#include - -using namespace boost::python; -using namespace libtorrent; - -object client_fingerprint_(peer_id const& id) -{ - boost::optional result = client_fingerprint(id); - return result ? object(*result) : object(); -} - -entry bdecode_(std::string const& data) -{ - return bdecode(data.begin(), data.end()); -} - -std::string bencode_(entry const& e) -{ - std::string result; - bencode(std::back_inserter(result), e); - return result; -} - -void bind_utility() -{ - def("identify_client", &libtorrent::identify_client); - def("client_fingerprint", &client_fingerprint_); - def("bdecode", &bdecode_); - def("bencode", &bencode_); -} - diff --git a/libtorrent_utp/bindings/python/src/version.cpp b/libtorrent_utp/bindings/python/src/version.cpp deleted file mode 100644 index aaeafd900..000000000 --- a/libtorrent_utp/bindings/python/src/version.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Daniel Wallin 2006. 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) - -#include -#include - -using namespace boost::python; - -void bind_version() -{ - scope().attr("version") = LIBTORRENT_VERSION; - scope().attr("version_major") = LIBTORRENT_VERSION_MAJOR; - scope().attr("version_minor") = LIBTORRENT_VERSION_MINOR; -} - diff --git a/libtorrent_utp/build_dist.sh b/libtorrent_utp/build_dist.sh deleted file mode 100755 index 9a5260746..000000000 --- a/libtorrent_utp/build_dist.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - - -#clear out any extended attributes that Finder may add -sudo xattr -r -d com.apple.FinderInfo * - -rm -f config.log config.report configure -rm -f m4/libtool.m4 m4/lt~obsolete.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/ltoptions.m4 -rm -fr autom4te.cache build-aux -rm -f Makefile Makefile.in -rm -f src/Makefile src/Makefile.in -rm -f include/libtorrent/Makefile include/libtorrent/Makefile.in -rm -f examples/Makefile examples/Makefile.in -rm -f test/Makefile test/Makefile.in -rm -f bindings/Makefile bindings/Makefile.in -rm -f bindings/python/Makefile bindings/python/Makefile.in -chmod a-x docs/*.rst docs/*.htm* src/*.cpp include/libtorrent/*.hpp - -./autotool.sh -./configure --enable-python-binding --enable-examples=yes --enable-tests=yes --with-boost-system=mt --with-boost-python=mt -make V=1 -j8 dist check diff --git a/libtorrent_utp/configure.ac b/libtorrent_utp/configure.ac deleted file mode 100644 index f09a60faf..000000000 --- a/libtorrent_utp/configure.ac +++ /dev/null @@ -1,759 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -# $Id$ - -AC_PREREQ([2.63]) - -AC_INIT([libtorrent-rasterbar],[0.16.0],[arvid@cs.umu.se], - [libtorrent-rasterbar],[http://www.libtorrent.org]) -AC_CONFIG_SRCDIR([src/torrent.cpp]) -AC_CONFIG_AUX_DIR([build-aux]) -AC_CONFIG_MACRO_DIR([m4]) - -# Silencing build output (automake-1.11) -m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) - - -############################################################################### -dnl --------------------------------------------------------------------------- -dnl interface version info -dnl --------------------------------------------------------------------------- -dnl Advanced information about versioning: -dnl * "Writing shared libraries" by Mike Hearn -dnl http://navi.cx/~mike/writing-shared-libraries.html -dnl * libtool.info chapter "Versioning" -dnl * libtool.info chapter "Updating library version information" -dnl --------------------------------------------------------------------------- -dnl Versioning: -dnl - CURRENT (Major): Increment if the interface has changes. AGE is always -dnl *changed* at the same time. -dnl - AGE (Micro): Increment if any interfaces have been added; set to 0 -dnl if any interfaces have been removed. Removal has -dnl precedence over adding, so set to 0 if both happened. -dnl It denotes upward compatibility. -dnl - REVISION (Minor): Increment any time the source changes; set to -dnl 0 if you incremented CURRENT. -dnl -dnl To summarize. Any interface *change* increment CURRENT. If that interface -dnl change does not break upward compatibility (ie it is an addition), -dnl increment AGE, Otherwise AGE is reset to 0. If CURRENT has changed, -dnl REVISION is set to 0, otherwise REVISION is incremented. -dnl --------------------------------------------------------------------------- - -m4_define([VERSION_INFO_CURRENT],[6]) -m4_define([VERSION_INFO_REVISION],[0]) -m4_define([VERSION_INFO_AGE],[0]) -INTERFACE_VERSION_INFO=VERSION_INFO_CURRENT:VERSION_INFO_REVISION:VERSION_INFO_AGE -AC_SUBST(INTERFACE_VERSION_INFO) -############################################################################### - - -############################################################################### -# Start -############################################################################### - -AS_ECHO -AS_ECHO "Building $PACKAGE_STRING" - - -############################################################################### -# Performing some basic checks and initializing the build system -############################################################################### - -AS_ECHO -AS_ECHO "Checking for a C/C++ compiler to use:" -AC_PROG_CC -AC_PROG_CPP -AC_PROG_CC_C_O -AC_PROG_CXX -AC_PROG_CXXCPP -AC_PROG_CXX_C_O - -AS_ECHO -AS_ECHO "Checking system type:" -AC_CANONICAL_BUILD -AC_CANONICAL_HOST -AC_CANONICAL_TARGET - -AS_ECHO -AS_ECHO "Initializing Automake:" -AM_INIT_AUTOMAKE([1.11 foreign]) - - -AS_ECHO -AS_ECHO "Initializing Libtool:" -LT_PREREQ([2.2.6]) -LT_INIT - - -############################################################################### -# Checking for needed base libraries -############################################################################### - -AS_ECHO -AS_ECHO "Checking for posix thread support:" - -AX_PTHREAD() - -LIBS="$PTHREAD_LIBS $LIBS" -CFLAGS="$PTHREAD_CFLAGS $CFLAGS" -CC="$PTHREAD_CC" - -AS_ECHO -AS_ECHO "Checking for boost libraries:" - -AX_BOOST_BASE([1.36]) - -AX_BOOST_SYSTEM() -AS_IF([test -z "$BOOST_SYSTEM_LIB"], - [AC_MSG_ERROR(Boost.System library not found. Try using --with-boost-system=lib)]) - -CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" -LDFLAGS="$BOOST_LDFLAGS $LDFLAGS" - - -############################################################################### -# Checking for functions and other stuffs -############################################################################### - - -AS_ECHO -AS_ECHO "Checking for pkg-config:" - -PKG_PROG_PKG_CONFIG([0.20]) - -AS_ECHO -AS_ECHO "Checking for functions and headers:" - -AC_SYS_LARGEFILE -AC_PROG_INSTALL -AC_PROG_LN_S -AC_PROG_MAKE_SET - -# check for gethostbyname and gethostbyname_r (used in src/GeoIP.c) -AC_CHECK_FUNCS([gethostbyname], [], - [AC_CHECK_LIB([nsl], [gethostbyname], [], - [AC_CHECK_LIB([resolv], [gethostbyname], [], - [AC_CHECK_LIB([socket], [gethostbyname], [], - [AC_CHECK_LIB([ws2_32], [main], - [AC_CHECK_LIB([wsock32], [main], - [LIBS="-lws2_32 -lwsock32 $LIBS"], - [AC_MSG_ERROR([wsock32 function not found.])])], - [AC_MSG_ERROR([gethostbyname function not found.])])])])])] -) - -AC_CHECK_FUNCS(gethostbyname_r, [ - # We look for the one that returns `int'. - # Hopefully this check is robust enough. - AC_EGREP_HEADER(int.*gethostbyname_r, netdb.h, [ - AC_DEFINE(GETHOSTBYNAME_R_RETURNS_INT)]) -]) - -AC_CHECK_FUNCS([clock_gettime], [], - [AC_CHECK_LIB([rt], [clock_gettime], [], - [AC_CHECK_LIB([posix4], [clock_gettime], [], - [AC_MSG_WARN([clock_gettime function not found.])])])] -) - - -dnl Pass some build options to setup.py and .pc file -COMPILETIME_OPTIONS="" - - -############################################################################### -# Setting configure options -############################################################################### - -AC_ARG_ENABLE( - [logging], - [AS_HELP_STRING( - [--enable-logging], - [enable logging to disk (use value "verbose" to enable verbose peer wire logging or "errors" limit logging to errors ) [default=no]])], - [[ARG_ENABLE_LOGGING=$enableval]], - [[ARG_ENABLE_LOGGING=no]] -) - -AC_ARG_ENABLE( - [debug], - [AS_HELP_STRING( - [--enable-debug], - [enable debug build [default=no]])], - [ - ARG_ENABLE_DEBUG=$enableval - ac_arg_enable_debug=$enableval - ], - [ - ARG_ENABLE_DEBUG=no - ac_arg_enable_debug=no - ] -) - -AC_ARG_ENABLE( - [dht], - [AS_HELP_STRING( - [--disable-dht], - [disable dht support (use value "logging" to add extra logging) [default=yes]])], - [[ARG_ENABLE_DHT=$enableval]], - [[ARG_ENABLE_DHT=yes]] -) - -AC_ARG_ENABLE( - [encryption], - [AS_HELP_STRING( - [--disable-encryption], - [disable encryption support (requires OpenSSL to be installed on your system, you can use --with-openssl to set the path) [default=yes]])], - [[ARG_ENABLE_ENCRYPTION=$enableval]], - [[ARG_ENABLE_ENCRYPTION=yes]] -) - -AC_ARG_ENABLE( - [pool-allocators], - [AS_HELP_STRING( - [--disable-pool-allocators], - [disable pool allocators for send buffers [default=yes]])], - [[ARG_ENABLE_POOL_ALLOC=$enableval]], - [[ARG_ENABLE_POOL_ALLOC=yes]] -) - -AC_ARG_ENABLE( - [invariant-checks], - [AS_HELP_STRING( - [--enable-invariant-checks], - [enable invariant checks (use value "full" to turn on extra expensive invariant checks) @<:@default=yes if debug is enabled, no otherwhise@:>@])], - [[ARG_ENABLE_INVARIANT=$enableval]], - [ - AS_IF([test "x$ac_arg_enable_debug" = "xyes"], - [ARG_ENABLE_INVARIANT=yes], - [ARG_ENABLE_INVARIANT=no]) - ] -) - -AC_ARG_ENABLE( - [deprecated-functions], - [AS_HELP_STRING( - [--disable-deprecated-functions], - [disable deprecated functions from the API [default=yes]])], - [[ARG_ENABLE_DEPRECATED=$enableval]], - [[ARG_ENABLE_DEPRECATED=yes]] -) - -AC_ARG_ENABLE( - [statistics], - [AS_HELP_STRING( - [--enable-statistics], - [enable statistics logging feature [default=no]])], - [[ARG_ENABLE_STATS=$enableval]], - [[ARG_ENABLE_STATS=no]] -) - -AC_ARG_ENABLE( - [disk-stats], - [AS_HELP_STRING( - [--enable-disk-stats], - [enable disk activity logging feature [default=no]])], - [[ARG_ENABLE_DISK_STATS=$enableval]], - [[ARG_ENABLE_DISK_STATS=no]] -) - -AC_ARG_ENABLE( - [geoip], - [AS_HELP_STRING( - [--disable-geoip], - [disable geoip support (if enabled, you can use --with-libgeoip to choose whether to link against shipped or system library) [default=yes]])], - [ - ARG_ENABLE_GEOIP=$enableval - ac_arg_enable_geoip=$enableval - ], - [ - ARG_ENABLE_GEOIP=yes - ac_arg_enable_geoip=yes - ] -) - -AC_ARG_ENABLE( - [examples], - [AS_HELP_STRING( - [--enable-examples], - [build example files [default=no]])], - [[ARG_ENABLE_EXAMPLES=$enableval]], - [[ARG_ENABLE_EXAMPLES=no]] -) - -AC_ARG_ENABLE( - [tests], - [AS_HELP_STRING( - [--enable-tests], - [build test files [default=no]])], - [[ARG_ENABLE_TESTS=$enableval]], - [[ARG_ENABLE_TESTS=no]] -) - -AC_ARG_ENABLE( - [python-binding], - [AS_HELP_STRING( - [--enable-python-binding], - [build python bindings [default=no]])], - [[ARG_ENABLE_PYTHON_BINDING=$enableval]], - [[ARG_ENABLE_PYTHON_BINDING=no]] -) - -AC_ARG_WITH( - [libgeoip], - [AS_HELP_STRING( - [--with-libgeoip], - [enable linking against system libgeoip [default=shipped]])], - [[ARG_WITH_LIBGEOIP=$withval]], - [[ARG_WITH_LIBGEOIP=no]] -) - - -############################################################################### -# Checking configure options -############################################################################### - -AS_ECHO -AS_ECHO "Checking build options:" - -AC_MSG_CHECKING([whether deprecated functions should be enabled]) -AS_CASE(["$ARG_ENABLE_DEPRECATED"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([TORRENT_NO_DEPRECATE],[1],[Define to exclude all deprecated functions from the API.]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_DEPRECATED]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DEPRECATED". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether debug build should be enabled]) -AS_CASE(["$ARG_ENABLE_DEBUG"], - ["yes"], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([TORRENT_DEBUG],[1],[Define to enable debug code.]) - COMPILETIME_OPTIONS+="-DTORRENT_DEBUG " - DEBUGFLAGS="-g" - ], - ["no"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([NDEBUG],[1],[Define to disable debug code.]) - #COMPILETIME_OPTIONS+="-DNDEBUG " - DEBUGFLAGS="-Os" - ], - [AC_MSG_RESULT([$ARG_ENABLE_DEBUG]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DEBUG". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether invariant check should be enabled]) #depends: $ac_arg_enable_debug -AS_CASE(["$ARG_ENABLE_INVARIANT"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - AS_IF([test "x$ac_arg_enable_debug" = "xno"], - [AC_MSG_ERROR([invariant-checks: this setting only affects debug build. Try using --enable-debug.])]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - AS_IF([test "x$ac_arg_enable_debug" = "xyes"], - [AC_DEFINE([TORRENT_DISABLE_INVARIANT_CHECKS],[1],[Define to disable internal invariant checks. Asserts are still enabled while debug is on.])]) - ], - ["full"], [ - AC_MSG_RESULT([full]) - AS_IF([test "x$ac_arg_enable_debug" = "xyes"], - [AC_DEFINE([TORRENT_EXPENSIVE_INVARIANT_CHECKS],[1],[Define to enable extra expensive invariant checks.])], - [AC_MSG_ERROR([invariant-checks: this setting only affects debug build. Try using --enable-debug.])]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_INVARIANT]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_INVARIANT". Use either "yes", "no" or "full".])] -) - -AC_MSG_CHECKING([whether logging to disk should be enabled]) -AS_CASE(["$ARG_ENABLE_LOGGING"], - ["yes"|"default"], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([TORRENT_LOGGING],[1],[Define to enable logging of the session events.]) - COMPILETIME_OPTIONS+="-DTORRENT_LOGGING " - ], - ["no"|"none"], [ - AC_MSG_RESULT([no]) - ], - ["verbose"], [ - AC_MSG_RESULT([verbose]) - AC_DEFINE([TORRENT_VERBOSE_LOGGING],[1],[Define to enable logging of the session events and every peer connection.]) - COMPILETIME_OPTIONS+="-DTORRENT_VERBOSE_LOGGING " - ], - ["errors"], [ - AC_MSG_RESULT([errors]) - AC_DEFINE([TORRENT_ERROR_LOGGING],[1],[Define to enable logging of the session events and every peer connection limited to errors.]) - COMPILETIME_OPTIONS+="-DTORRENT_ERROR_LOGGING " - ], - [AC_MSG_RESULT([$ARG_ENABLE_LOGGING]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_LOGGING". Use either "yes", "no", "verbose" or "errors".])] -) - -AC_MSG_CHECKING([whether statistics logging should be enabled]) -AS_CASE(["$ARG_ENABLE_STATS"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([TORRENT_STATS],[1],[Define to generate a log with statistics.]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_STATS]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_STATS". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether disk activity logging should be enabled]) -AS_CASE(["$ARG_ENABLE_DISK_STATS"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - AC_DEFINE([TORRENT_DISK_STATS],[1],[Define to create a log of all disk activities.]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_DISK_STATS]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DISK_STATS". Use either "yes" or "no".])] -) - -AS_ECHO -AS_ECHO "Checking features to be enabled:" - -AC_MSG_CHECKING([whether encryption support should be enabled]) -AS_CASE(["$ARG_ENABLE_ENCRYPTION"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - AC_MSG_NOTICE([encryption support: now checking for the OpenSSL library...]) - - AX_CHECK_OPENSSL([ - AC_DEFINE([TORRENT_USE_OPENSSL],[1],[Define to use OpenSSL support.]) - COMPILETIME_OPTIONS+="-DTORRENT_USE_OPENSSL " - ], [ - AC_MSG_ERROR([OpenSSL library not found. Try using --with-openssl=DIR or disabling encryption at all.]) - ]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([TORRENT_DISABLE_ENCRYPTION],[1],[Define to disable any encryption support and avoid linking against OpenSSL.]) - COMPILETIME_OPTIONS+="-DTORRENT_DISABLE_ENCRYPTION " - ], - [AC_MSG_RESULT([$ARG_ENABLE_ENCRYPTION]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_ENCRYPTION". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether geoip support should be enabled]) -AS_CASE(["$ARG_ENABLE_GEOIP"], - ["yes"], [ - AC_MSG_RESULT([yes]) - ], - ["no"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([TORRENT_DISABLE_GEO_IP],[1],[Define to disable the GeoIP support and avoid linking against LGPLed code.]) - COMPILETIME_OPTIONS+="-DTORRENT_DISABLE_GEO_IP " - ], - [AC_MSG_RESULT([$ARG_ENABLE_GEOIP]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_GEOIP". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether dht support should be enabled]) -AS_CASE(["$ARG_ENABLE_DHT"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([TORRENT_DISABLE_DHT],[1],[Define to disable the DHT support.]) - COMPILETIME_OPTIONS+="-DTORRENT_DISABLE_DHT " - ], - ["logging"], [ - AC_MSG_RESULT([logging]) - AC_DEFINE([TORRENT_DHT_VERBOSE_LOGGING],[1],[Define to enable DHT support with verbose logging.]) - COMPILETIME_OPTIONS+="-DTORRENT_DHT_VERBOSE_LOGGING " - ], - [AC_MSG_RESULT([$ARG_ENABLE_DHT]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_DHT". Use either "yes", "no" or "logging".])] -) - -AC_MSG_CHECKING([whether pool allocators should be enabled]) -AS_CASE(["$ARG_ENABLE_POOL_ALLOC"], - ["yes"|"on"], [ - AC_MSG_RESULT([yes]) - ], - ["no"|"off"], [ - AC_MSG_RESULT([no]) - AC_DEFINE([TORRENT_DISABLE_POOL_ALLOCATOR],[1],[Define to disable use of boost::pool for pool allocators.]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_POOL_ALLOC]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_POOL_ALLOC". Use either "yes" or "no".])] -) - -AS_ECHO -AS_ECHO "Checking for extra build files:" - -AC_MSG_CHECKING([whether example files should be built]) -AS_CASE(["$ARG_ENABLE_EXAMPLES"], - ["yes"], [AC_MSG_RESULT([yes])], - ["no"], [AC_MSG_RESULT([no])], - [AC_MSG_RESULT([$ARG_ENABLE_EXAMPLES]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_EXAMPLES". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether test files should be built]) -AS_CASE(["$ARG_ENABLE_TESTS"], - ["yes"], [AC_MSG_RESULT([yes])], - ["no"], [AC_MSG_RESULT([no])], - [AC_MSG_RESULT([$ARG_ENABLE_TESTS]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_TESTS". Use either "yes" or "no".])] -) - -AC_MSG_CHECKING([whether python bindings should be built]) -AS_CASE(["$ARG_ENABLE_PYTHON_BINDING"], - ["yes"], [ - AC_MSG_RESULT([yes]) - - AM_PATH_PYTHON([2.4], [], AC_MSG_ERROR([Python interpreter not found.])) - AX_PYTHON_DEVEL([>= '2.4']) - AX_BOOST_PYTHON() - - AS_IF([test -z "$BOOST_PYTHON_LIB"], - [AC_MSG_ERROR([Boost.Python library not found. Try using --with-boost-python=lib.])]) - ], - ["no"], [ - AC_MSG_RESULT([no]) - ], - [AC_MSG_RESULT([$ARG_ENABLE_PYTHON_BINDING]) - AC_MSG_ERROR([Unknown option "$ARG_ENABLE_PYTHON_BINDING". Use either "yes" or "no".])] -) - -AS_ECHO -AS_ECHO "Checking for external libraries:" - -AC_MSG_CHECKING([for FIEMAP support]) -AC_CHECK_HEADERS([linux/fiemap.h]) - -AC_MSG_CHECKING([whether to link against system libgeoip]) #depends: $ac_arg_enable_geoip -AS_CASE(["$ARG_WITH_LIBGEOIP"], - ["yes"|"system"], [ - AC_MSG_RESULT([yes]) - - AS_IF([test "x$ac_arg_enable_geoip" = "xno"], - [AC_MSG_ERROR([GeoIP support is disabled with this build configuration. Try using --enable-geoip.])]) - - AC_MSG_NOTICE([libgeoip: now checking for the libgeoip library...]) - - AC_CHECK_GEOIP([ - LIBS="$GEOIP_LIBS $LIBS" - CFLAGS="$GEOIP_CFLAGS $CFLAGS" - ],[ - AC_MSG_ERROR([GeoIP library not found. Try using --without-libgeoip to link against the shipped copy.]) - ]) - ], - ["no"|"shipped"], [ - AS_IF([test "x$ac_arg_enable_geoip" = "xno"], [ - # redundant check: session_impl.hpp just won't check for any - # GeoIP.h, so any value would be ok (ie. this AS_IF could be - # removed) - AC_MSG_RESULT([disabled]) - ARG_WITH_LIBGEOIP="disabled" - ], [ - AC_MSG_RESULT([no]) - ARG_WITH_LIBGEOIP="no" - AC_DEFINE([WITH_SHIPPED_GEOIP_H],[1],[Define to use shipped copy of GeoIP.h]) - COMPILETIME_OPTIONS+="-DWITH_SHIPPED_GEOIP_H " - ]) - ], - [AC_MSG_RESULT([$ARG_WITH_LIBGEOIP]) - AC_MSG_ERROR([Unknown option "$ARG_WITH_LIBGEOIP". Use either "yes" or "no".])] -) - - -############################################################################### -# Setting conditional variables for Makefiles -############################################################################### - -AM_CONDITIONAL([ENABLE_DHT], [test "x$ARG_ENABLE_DHT" = "xyes" -o "x$ARG_ENABLE_DHT" = "xlogging" ]) -AM_CONDITIONAL([ENABLE_EXAMPLES], [test "x$ARG_ENABLE_EXAMPLES" = "xyes"]) -AM_CONDITIONAL([ENABLE_TESTS], [test "x$ARG_ENABLE_TESTS" = "xyes"]) -AM_CONDITIONAL([ENABLE_PYTHON_BINDING], [test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"]) - -AM_CONDITIONAL([WITH_SHIPPED_GEOIP], [test "x$ARG_WITH_LIBGEOIP" = "xno" ]) - - -############################################################################### -# Other useful stuff -############################################################################### - -# this works around a bug in asio in boost-1.39 -# see: https://svn.boost.org/trac/boost/ticket/3095 -AC_DEFINE([BOOST_ASIO_HASH_MAP_BUCKETS],[1021],[Define to fix a wrong behavior in boost 1.39.]) -COMPILETIME_OPTIONS+="-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 " - -AC_DEFINE([BOOST_EXCEPTION_DISABLE],[1],[Define to disable the boost.exception features.]) -COMPILETIME_OPTIONS+="-DBOOST_EXCEPTION_DISABLE " - -AC_DEFINE([BOOST_ASIO_ENABLE_CANCELIO],[1],[Define to enable cancel support in asio on windows XP and older.]) -COMPILETIME_OPTIONS+="-DBOOST_ASIO_ENABLE_CANCELIO " - -dnl Use possibly specific python install params -AC_ARG_VAR([PYTHON_INSTALL_PARAMS], [Set specific install parameters for python bindings.]) -AS_IF([test "x$PYTHON_INSTALL_PARAMS" = "x"], - [PYTHON_INSTALL_PARAMS='--prefix=$(DESTDIR)$(prefix)']) - -dnl Set some defines if we are building a shared library -AS_IF([test "x$enable_shared" = "xyes"], - [AC_DEFINE([TORRENT_BUILDING_SHARED],[1],[Define to exports functions and classes with default visibility in GCC.]) - COMPILETIME_OPTIONS+="-DTORRENT_LINKING_SHARED "]) - -AC_SUBST(DEBUGFLAGS) -AC_SUBST(PYTHON_INSTALL_PARAMS) -AC_SUBST(COMPILETIME_OPTIONS) - - -# Try to guess real svn revision if any, fallback to hardcoded otherwise -SVN_REVISION=`svn info 2>/dev/null | sed -n -e '/^URL\:.*libtorrent.svn.sourceforge.net/,$!d;s,^Revision\: \([[0-9]]*\)$,\1,p'` -AS_IF([test -z "$SVN_REVISION"], - [SVN_REVISION=`sed -n -e 's/^#define LIBTORRENT_REVISION \"\$Rev\: \([0-9]*\) \$\" $/\1/p' include/libtorrent/version.hpp`]) - - -############################################################################### -# Generating Makefiles -############################################################################### - -AS_ECHO -AS_ECHO "Generating Makefiles:" - -AC_CONFIG_FILES( - [Makefile] - [src/Makefile] - [include/libtorrent/Makefile] - [examples/Makefile] - [test/Makefile] - [bindings/Makefile] - [bindings/python/Makefile] - [bindings/python/setup.py] - [libtorrent-rasterbar.pc] - [libtorrent-rasterbar-cmake.pc] -) - -AC_OUTPUT - - -############################################################################### -# Generating config.report -############################################################################### - -AS_ECHO -AS_ECHO "Configure script has finished system check." -AS_ECHO - -cat > config.report << END - Config results: - -=-=-=-=-=-=-=-=- - -Package: - name: ${PACKAGE_NAME} - version: ${PACKAGE_VERSION} - svn revision: ${SVN_REVISION} - -Build environment: - build system: ${build} - host system: ${host} - target system: ${target} - -Compiler and linker flags: - CPPFlags: ${CPPFLAGS} - CFlags: ${CFLAGS} - CXXFlags: ${CXXFLAGS} - LDFlags: ${LDFLAGS} - Libs: ${LIBS} - Defs: ${DEFS} - -Build options: - deprecated functions: ${ARG_ENABLE_DEPRECATED:-yes} - debug build: ${ARG_ENABLE_DEBUG:-no} - invariant checks: ${ARG_ENABLE_INVARIANT:-no} - logging support: ${ARG_ENABLE_LOGGING:-no} - statistics: ${ARG_ENABLE_STATS:-no} - disk statistics: ${ARG_ENABLE_DISK_STATS:-no} - -Features: - encryption support: ${ARG_ENABLE_ENCRYPTION:-yes} - geoip support: ${ARG_ENABLE_GEOIP:-yes} - dht support: ${ARG_ENABLE_DHT:-yes} - pool allocators: ${ARG_ENABLE_POOL_ALLOC:-yes} - -Extra builds: - examples: ${ARG_ENABLE_EXAMPLES:-no} - tests: ${ARG_ENABLE_TESTS:-no} - python bindings: ${ARG_ENABLE_PYTHON_BINDING:-no} - -Pthread library: - CFlags: ${PTHREAD_CFLAGS} - Libs: ${PTHREAD_LIBS} - -Boost libraries: - version: ${BOOST_VERSION} - CPPFlags: ${BOOST_CPPFLAGS} - LDFlags: ${BOOST_LDFLAGS} - boost.system: ${BOOST_SYSTEM_LIB} -END - -AS_IF([test "x$ARG_ENABLE_PYTHON_BINDING" = "xyes"], [ -cat >> config.report << END - boost.python: ${BOOST_PYTHON_LIB} - -Python environment: - -automake- - binary: ${PYTHON} - version: ${PYTHON_VERSION} - platform: ${PYTHON_PLATFORM} - prefix: ${PYTHON_PREFIX} - exec_prefix: ${PYTHON_EXEC_PREFIX} - pythondir: ${pythondir} - pkgpythondir: ${pkgpythondir} - pyexecdir: ${pyexecdir} - pkgpyexecdir: ${pkgpyexecdir} - - -m4- - cppflags: ${PYTHON_CPPFLAGS} - ldflags: ${PYTHON_LDFLAGS} - extra libs: ${PYTHON_EXTRA_LIBS} - extra ldflags: ${PYTHON_EXTRA_LDFLAGS} - -END -]) - -cat >> config.report << END - -External libraries: - system libgeoip: ${ARG_WITH_LIBGEOIP:-no} -END - -AS_IF([test "x$ARG_WITH_LIBGEOIP" = "xyes"], [ -cat >> config.report << END - -GeoIP library: - GeoIP CFlags: ${GEOIP_CFLAGS} - GeoIP Libs: ${GEOIP_LIBS} -END -]) - -AS_IF([test "x$ARG_ENABLE_ENCRYPTION" = "xyes"], [ -cat >> config.report << END - -OpenSSL library: - OpenSSL Libs: ${OPENSSL_LIBS} - OpenSSL LDFlags: ${OPENSSL_LDFLAGS} - OpenSSL Includes: ${OPENSSL_INCLUDES} -END -]) - -cat config.report - -AS_ECHO -AS_ECHO "Type 'make' to compile $PACKAGE_STRING" -AS_ECHO "or type 'make V=1' for verbose compiling" -AS_ECHO "and then 'make install' to install it into $prefix" -AS_ECHO diff --git a/libtorrent_utp/dht_flood.py b/libtorrent_utp/dht_flood.py deleted file mode 100755 index b9f6b98da..000000000 --- a/libtorrent_utp/dht_flood.py +++ /dev/null @@ -1,68 +0,0 @@ -#! /usr/bin/env python - -import socket -import sys -from types import StringType, IntType, LongType, DictType, ListType, TupleType -import random - -port = int(sys.argv[1]) - -# from BitTorrent 4.3.0 -def encode_bencached(x,r): - r.append(x.bencoded) - -def encode_int(x, r): - r.extend(('i', str(x), 'e')) - -def encode_string(x, r): - r.extend((str(len(x)), ':', x)) - -def encode_list(x, r): - r.append('l') - for i in x: - encode_func[type(i)](i, r) - r.append('e') - -def encode_dict(x,r): - r.append('d') - ilist = x.items() - ilist.sort() - for k, v in ilist: - r.extend((str(len(k)), ':', k)) - encode_func[type(v)](v, r) - r.append('e') - -encode_func = {} -encode_func[IntType] = encode_int -encode_func[LongType] = encode_int -encode_func[StringType] = encode_string -encode_func[ListType] = encode_list -encode_func[TupleType] = encode_list -encode_func[DictType] = encode_dict - -def bencode(x): - r = [] - encode_func[type(x)](x, r) - return ''.join(r) - -def send_dht_message(msg): - s.sendto(bencode(msg), 0, ('127.0.0.1', port)) - -def random_key(): - ret = '' - for i in range(0, 20): - ret += chr(random.randint(0, 255)) - return ret - -s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -node_id = '1' * 20; -query = 'get_peers' - -print 'test random info-hashes' -for i in xrange(1, 30000): - send_dht_message({'a': {'id': node_id, 'info_hash': random_key()}, 'q': query, 'y': 'q', 't': '%d' % i}) - -print 'test random peer-ids' -for i in xrange(1, 30000): - send_dht_message({'a': {'id': random_key(), 'info_hash': random_key()}, 'q': query, 'y': 'q', 't': '%d' % i}) - diff --git a/libtorrent_utp/docs/Linkage.png b/libtorrent_utp/docs/Linkage.png deleted file mode 100644 index 222e6c37533491f19a62ccffb7888fe8860fb685..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37932 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 LNkvXXu0mjf(giSg diff --git a/libtorrent_utp/docs/bitbuddy_thumb.jpg b/libtorrent_utp/docs/bitbuddy_thumb.jpg deleted file mode 100644 index c18138fcab482300625b509eab6972a931f6fbbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2766 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 diff --git a/libtorrent_utp/docs/bitrocket_thumb.png b/libtorrent_utp/docs/bitrocket_thumb.png deleted file mode 100644 index c6648d1d5897e4f234f6fc52826b6a9ce61188b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9566 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

&#OQr>VOQPx+9Mc%^KQ>ATtDVM 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 diff --git a/libtorrent_utp/docs/bubba.png b/libtorrent_utp/docs/bubba.png deleted file mode 100644 index 9d9ff461e712d86d68c3addd51707eebe2512ac4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29631 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 - - - - - -libtorrent manual - - - - - - - -
-
- - -
-

libtorrent manual

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
- -
-

downloading and building

-

To acquire the latest version of libtorrent, you'll have to grab it from SVN. -You'll find instructions on how to do this here (see subversion access).

-

The build systems supported "out of the box" in libtorrent are boost-build v2 -(BBv2) and autotools (for unix-like systems). If you still can't build after -following these instructions, you can usually get help in the #libtorrent -IRC channel on irc.freenode.net.

-
-

Warning

-

A common mistake when building and linking against libtorrent is -to build with one set of configuration options (#defines) and -link against it using a different set of configuration options. Since -libtorrent has some code in header files, that code will not be -compatible with the built library if they see different configurations.

-

Always make sure that the same TORRENT_* macros are defined when you -link against libtorrent as when you build it.

-

Boost-build supports propagating configuration options to dependencies. -When building using the makefiles, this is handled by setting the -configuration options in the pkg-config file. Always use pkg-config -when linking against libtorrent.

-
-
-

building from svn

-

To build libtorrent from svn you need to check out the libtorrent sources from -sourceforge and also check out the asio sources from its sourceforge cvs. -If you downloaded a release tarball, you can skip this section.

-

To prepare the directory structure for building, follow these steps:

-
    -
  • Check out libtorrent (instructions).
  • -
  • Check out asio (instructions).
  • -
  • Copy the asio/include/asio/ directory into the libtorrent/include/libtorrent/ -directory. Alternatively you can make a symbolic link.
  • -
  • Copy asio/include/asio.hpp into libtorrent/include/libtorrent.
  • -
-

Now the libtorrent directory is ready for building. Follow the steps in one -of the following sections depending on which build system you prefer to use.

-
-
-

building with BBv2

-

The primary reason to use boost-build is that it will automatically build the -dependent boost libraries with the correct compiler settings, in order to -ensure that the build targets are link compatible (see boost guidelines -for some details on this issue).

-

Since BBv2 will build the boost libraries for you, you need the full boost -source package. Having boost installed via some package system is usually not -enough (and even if it is enough, the necessary environment variables are -usually not set by the package installer).

-

If you want to build against an installed copy of boost, you can skip directly -to step 3 (assuming you also have boost build installed).

-
-

Step 1: Download boost

-

You'll find boost here.

-

Extract the archive to some directory where you want it. For the sake of this -guide, let's assume you extract the package to c:\boost_1_34_0 (I'm using -a windows path in this example since if you're on linux/unix you're more likely -to use the autotools). You'll need at least version 1.34 of the boost library -in order to build libtorrent.

-
-
-

Step 2: Setup BBv2

-

First you need to build bjam. You do this by opening a terminal (In -windows, run cmd). Change directory to -c:\boost_1_34_0\tools\jam\src. Then run the script called -build.bat or build.sh on a unix system. This will build bjam and -place it in a directory starting with bin. and then have the name of your -platform. Copy the bjam.exe (or bjam on a unix system) to a place -that's in you shell's PATH. On linux systems a place commonly used may be -/usr/local/bin or on windows c:\windows (you can also add directories -to the search paths by modifying the environment variable called PATH).

-

Now you have bjam installed. bjam can be considered an interpreter -that the boost-build system is implemented on. So boost-build uses bjam. -So, to complete the installation you need to make two more things. You need to -set the environment variable BOOST_BUILD_PATH. This is the path that tells -bjam where it can find boost-build, your configuration file and all the -toolsets (descriptions used by boost-build to know how to use different -compilers on different platforms). Assuming the boost install path above, set -it to c:\boost_1_34_0\tools\build\v2.

-

To set an environment variable in windows, type for example:

-
-set BOOST_BUILD_PATH=c:\boost_1_34_0\tools\build\v2
-
-

In a terminal window.

-

The last thing to do to complete the setup of BBv2 is to modify your -user-config.jam file. It is located in c:\boost_1_34_0\tools\build\v2. -Depending on your platform and which compiler you're using, you should add a -line for each compiler and compiler version you have installed on your system -that you want to be able to use with BBv2. For example, if you're using -Microsoft Visual Studio 7.1 (2003), just add a line:

-
-using msvc : 7.1 ;
-
-

If you use GCC, add the line:

-
-using gcc ;
-
-

If you have more than one version of GCC installed, you can add the -commandline used to invoke g++ after the version number, like this:

-
-using gcc : 3.3 : g++-3.3 ;
-using gcc : 4.0 : g++-4.0 ;
-
-

Another toolset worth mentioning is the darwin toolset (For MacOS X). -From Tiger (10.4) MacOS X comes with both GCC 3.3 and GCC 4.0. Then you can -use the following toolsets:

-
-using darwin : 3.3 : g++-3.3 ;
-using darwin : 4.0 : g++-4.0 ;
-
-

Note that the spaces around the semi-colons and colons are important!

-

Also see the official installation instructions.

-
-
-

Step 3: Building libtorrent

-

When building libtorrent, the Jamfile expects the environment variable -BOOST_ROOT to be set to the boost installation directory. It uses this to -find the boost libraries it depends on, so they can be built and their headers -files found. So, set this to c:\boost_1_34_0. You only need this if you're -building against a source distribution of boost.

-

Then the only thing left is simply to invoke bjam. If you want to specify -a specific toolset to use (compiler) you can just add that to the commandline. -For example:

-
-bjam msvc-7.1 boost=source
-bjam gcc-3.3 boost=source
-bjam darwin-4.0 boost=source
-
-

If you're building against a system installed boost, specify boost=system.

-

To build different versions you can also just add the name of the build -variant. Some default build variants in BBv2 are release, debug, -profile.

-

You can build libtorrent as a dll too, by typing link=shared, or -link=static to build a static library.

-

If you want to explicitly say how to link against the runtime library, you -can set the runtime-link feature on the commandline, either to shared -or static. Most operating systems will only allow linking shared against -the runtime, but on windows you can do both. Example:

-
-bjam msvc-7.1 link=static runtime-link=static boost=source
-
-
-

Warning

-

If you link statically to the runtime library, you cannot build libtorrent -as a shared library (DLL), since you will get separate heaps in the library -and in the client application. It will result in crashes and possibly link -errors.

-
-
-

Warning

-

With boost-build V2 (Milestone 11), the darwin toolset uses the -s linker -option to strip debug symbols. This option is buggy in Apple's GCC, and -will make the executable crash on startup. On Mac OS X, instead build -your release executables with the debug-symbols=on option, and -later strip your executable with strip.

-
-
-

Warning

-

Some linux systems requires linking against librt in order to access -the POSIX clock functions. If you get an error complaining about a missing -symbol clock_gettime, you have to give need-librt=yes on the -bjam command line. This will make libtorrent link against librt.

-
-
-

Note

-

When building on Solaris, you might have to specify stdlib=sun-stlport -on the bjam command line.

-
-

The build targets are put in a directory called bin, and under it they are -sorted in directories depending on the toolset and build variant used.

-

To build the examples, just change directory to the examples directory and -invoke bjam from there. To build and run the tests, go to the test -directory and run bjam.

-

Note that if you're building on windows using the msvc toolset, you cannot run it -from a cygwin terminal, you'll have to run it from a cmd terminal. The same goes for -cygwin, if you're building with gcc in cygwin you'll have to run it from a cygwin terminal. -Also, make sure the paths are correct in the different environments. In cygwin, the paths -(BOOST_BUILD_PATH and BOOST_ROOT) should be in the typical unix-format (e.g. -/cygdrive/c/boost_1_34_0). In the windows environment, they should have the typical -windows format (c:/boost_1_34_0).

-

The Jamfile will define NDEBUG when it's building a release build. -For more build configuration flags see Build configurations.

-

Build features:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
boost build featurevalues
boost
    -
  • system - default. Tells the Jamfile that -boost is installed and should be linked against -the system libraries.
  • -
  • source - Specifies that boost is to be built -from source. The environment variable -BOOST_ROOT must be defined to point to the -boost directory.
  • -
-
boost-link
    -
  • static - links statically against the boost -libraries.
  • -
  • shared - links dynamically against the boost -libraries.
  • -
-
logging
    -
  • none - no logging.
  • -
  • default - basic session logging.
  • -
  • verbose - verbose peer wire logging.
  • -
  • errors - like verbose, but limited to errors.
  • -
-
dht-support
    -
  • on - build with support for tracker less -torrents and DHT support.
  • -
  • logging - build with DHT support and verbose -logging of the DHT protocol traffic.
  • -
  • off - build without DHT support.
  • -
-
need-librt
    -
  • no - this platform does not need to link -against librt to have POSIX time functions.
  • -
  • yes - specify this if your linux system -requires you to link against librt.a. This is -typically the case on x86 64 bit systems.
  • -
-
asserts
    -
  • on - asserts are on if in debug mode
  • -
  • off - asserts are disabled
  • -
  • production - assertion failures are logged -to asserts.log in the current working -directory, but won't abort the process.
  • -
-
geoip
    -
  • off - geo ip lookups disabled
  • -
  • static - MaxMind geo ip lookup code linked -in statically. Note that this code is under -LGPL license.
  • -
  • shared - The MaxMind geo ip lookup library -is expected to be installed on the system and -it will be used.
  • -
-
upnp-logging
    -
  • off - default. Does not log UPnP traffic.
  • -
  • on - creates "upnp.log" with the messages -sent to and received from UPnP devices.
  • -
-
encryption
    -
  • openssl - links against openssl and -libcrypto to enable https and encrypted -bittorrent connections.
  • -
  • gcrypt - links against libgcrypt to enable -encrypted bittorrent connections.
  • -
  • tommath - uses a shipped version of -libtommath and a custom rc4 implementation -(based on libtomcrypt). This is the default -option.
  • -
  • off - turns off support for encrypted -connections. The shipped public domain SHA-1 -implementation is used.
  • -
-
pool-allocators
    -
  • on - default, uses pool allocators for send -buffers.
  • -
  • off - uses malloc() and free() -instead. Might be useful to debug buffer issues -with tools like electric fence or libgmalloc.
  • -
-
link
    -
  • static - builds libtorrent as a static -library (.a / .lib)
  • -
  • shared - builds libtorrent as a shared -library (.so / .dll).
  • -
-
runtime-link
    -
  • static - links statically against the -run-time library (if available on your -platform).
  • -
  • shared - link dynamically against the -run-time library (default).
  • -
-
variant
    -
  • debug - builds libtorrent with debug -information and invariant checks.
  • -
  • release - builds libtorrent in release mode -without invariant checks and with optimization.
  • -
  • profile - builds libtorrent with profile -information.
  • -
-
character-set

This setting will only have an affect on windows. -Other platforms are expected to support UTF-8.

-
    -
  • unicode - The unicode version of the win32 -API is used. This is default.
  • -
  • ansi - The ansi version of the win32 API is -used.
  • -
-
invariant-checks

This setting only affects debug builds (where -NDEBUG is not defined). It defaults to on.

-
    -
  • on - internal invariant checks are enabled.
  • -
  • off - internal invariant checks are -disabled. The resulting executable will run -faster than a regular debug build.
  • -
  • full - turns on extra expensive invariant -checks.
  • -
-
debug-symbols
    -
  • on - default for debug builds. This setting -is useful for building release builds with -symbols.
  • -
  • off - default for release builds.
  • -
-
deprecated-functions
    -
  • on - default. Includes deprecated functions -of the API (might produce warnings during build -when deprecated functions are used).
  • -
  • off - excludes deprecated functions from the -API. Generates build errors when deprecated -functions are used.
  • -
-
full-stats
    -
  • on - default, collects stats for IP overhead -and DHT and trackers. This uses a little bit -extra memory for each peer and torrent.
  • -
  • off - only collects the standard stats for -upload and download rate.
  • -
-
-

The variant feature is implicit, which means you don't need to specify -the name of the feature, just the value.

-

The logs created when building vlog or log mode are put in a directory called -libtorrent_logs in the current working directory.

-

When building the example client on windows, you need to build with -link=static otherwise you may get unresolved external symbols for some -boost.program-options symbols.

-

For more information, see the Boost build v2 documentation, or more -specifically the section on builtin features.

-
-
-
-

building with autotools

-

First of all, you need to install automake and autoconf. Many -unix/linux systems comes with these preinstalled.

-

The prerequisites for building libtorrent are boost.thread, boost.date_time -and boost.filesystem. Those are the compiled boost libraries needed. The -headers-only libraries needed include (but is not necessarily limited to) -boost.bind, boost.ref, boost.multi_index, boost.optional, boost.lexical_cast, -boost.integer, boost.iterator, boost.tuple, boost.array, boost.function, -boost.smart_ptr, boost.preprocessor, boost.static_assert.

-

If you want to build the client_test example, you'll also need boost.regex -and boost.program_options.

-
-

Step 1: Generating the build system

-

No build system is present if libtorrent is checked out from CVS - it -needs to be generated first. If you're building from a released tarball, -you may skip directly to Step 2: Running configure.

-

Execute the following commands, in the given order, to generate -the build system:

-
-aclocal -I m4
-autoheader
-libtoolize --copy --force
-automake --add-missing --copy --gnu
-autoconf
-
-

On darwin/OSX you have to run glibtoolize instead of libtoolize.

-
-
-

Step 2: Running configure

-

In your shell, change directory to the libtorrent directory and run -./configure. This will look for libraries and C++ features that libtorrent -is dependent on. If something is missing or can't be found it will print an -error telling you what failed.

-

The most likely problem you may encounter is that the configure script won't -find the boost libraries. Make sure you have boost installed on your system. -The easiest way to install boost is usually to use the preferred package -system on your platform. Usually libraries and headers are installed in -standard directories where the compiler will find them, but sometimes that -may not be the case. For example when installing boost on darwin using -darwinports (the package system based on BSD ports) all libraries are -installed to /opt/local/lib and headers are installed to -/opt/local/include. By default the compiler will not look in these -directories. You have to set the enviornment variables LDFLAGS and -CXXFLAGS in order to make the compiler find those libs. In this example -you'd set them like this:

-
-export LDFLAGS=-L/opt/local/lib
-export CXXFLAGS=-I/opt/local/include
-
-

It was observed on FreeBSD (release 6.0) that one needs to add '-lpthread' to -LDFLAGS, as Boost::Thread detection will fail without it, even if -Boost::Thread is installed.

-

If you need to set these variables, it may be a good idea to add those lines -to your ~/.profile or ~/.tcshrc depending on your shell.

-

If the boost libraries are named with a suffix on your platform, you may use -the --with-boost-thread= option to specify the suffix used for the thread -library in this case. For more information about these options, run:

-
-./configure --help
-
-

On gentoo the boost libraries that are built with multi-threading support have -the suffix mt.

-

You know that the boost libraries were found if you see the following output -from the configure script:

-
-checking whether the Boost::DateTime library is available... yes
-checking for main in -lboost_date_time... yes
-checking whether the Boost::Filesystem library is available... yes
-checking for main in -lboost_filesystem... yes
-checking whether the Boost::Thread library is available... yes
-checking for main in -lboost_thread... yes
-
-

Another possible source of problems may be if the path to your libtorrent -directory contains spaces. Make sure you either rename the directories with -spaces in their names to remove the spaces or move the libtorrent directory.

-
-
-

Creating a debug build

-

To tell configure to build a debug version (with debug info, asserts -and invariant checks enabled), you have to run the configure script -with the following option:

-
-./configure --enable-debug=yes
-
-
-
-

Creating a release build

-

To tell the configure to build a release version (without debug info, -asserts and invariant checks), you have to run the configure script -with the following option:

-
-./configure --enable-debug=no
-
-

The above option make use of -DNDEBUG, which is used throughout libtorrent.

-
-
-

Step 3: Building libtorrent

-

Once the configure script is run successfully, you just type make and -libtorrent, the examples and the tests will be built.

-

When libtorrent is built it may be a good idea to run the tests, you do this -by running make check.

-

If you want to build a release version (without debug info, asserts and -invariant checks), you have to rerun the configure script and rebuild, like this:

-
-./configure --disable-debug
-make clean
-make
-
-
-
-
-

building with other build systems

-

If you're building in MS Visual Studio, you may have to set the compiler -options "force conformance in for loop scope", "treat wchar_t as built-in -type" and "Enable Run-Time Type Info" to Yes.

-
-
-

build configurations

-

By default libtorrent is built In debug mode, and will have pretty expensive -invariant checks and asserts built into it. If you want to disable such checks -(you want to do that in a release build) you can see the table below for which -defines you can use to control the build.

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
macrodescription
NDEBUGIf you define this macro, all asserts, -invariant checks and general debug code will be -removed. Since there is quite a lot of code in -in header files in libtorrent, it may be -important to define the symbol consistently -across compilation units, including the clients -files. Potential problems is different -compilation units having different views of -structs and class layouts and sizes.
TORRENT_LOGGINGThis macro will enable logging of the session -events, such as tracker announces and incoming -connections (as well as blocked connections).
TORRENT_DISABLE_GEO_IPThis is defined by default by the Jamfile. It -disables the GeoIP features, and avoids linking -against LGPL:ed code.
TORRENT_VERBOSE_LOGGINGIf you define this macro, every peer connection -will log its traffic to a log file as well as -the session log.
TORRENT_STORAGE_DEBUGThis will enable extra expensive invariant -checks in the storage, including logging of -piece sorting.
TORRENT_UPNP_LOGGINGGenerates a "upnp.log" file with the UPnP -traffic. This is very useful when debugging -support for various UPnP routers. -support for various UPnP routers.
TORRENT_DISK_STATSThis will create a log of all disk activity -which later can parsed and graphed using -parse_disk_log.py.
TORRENT_STATSThis will generate a log with transfer rates, -downloading torrents, seeding torrents, peers, -connecting peers and disk buffers in use. The -log can be parsed and graphed with -parse_session_stats.py.
UNICODEIf building on windows this will make sure the -UTF-8 strings in pathnames are converted into -UTF-16 before they are passed to the file -operations.
TORRENT_DISABLE_POOL_ALLOCATORDisables use of boost::pool<>.
TORRENT_LINKING_SHAREDIf this is defined when including the -libtorrent headers, the classes and functions -will be tagged with __declspec(dllimport) -on msvc and default visibility on GCC 4 and -later. Set this in your project if you're -linking against libtorrent as a shared library. -(This is set by the Jamfile when -link=shared is set).
TORRENT_BUILDING_SHAREDIf this is defined, the functions and classes -in libtorrent are marked with -__declspec(dllexport) on msvc, or with -default visibility on GCC 4 and later. This -should be defined when building libtorrent as -a shared library. (This is set by the Jamfile -when link=shared is set).
TORRENT_DISABLE_DHTIf this is defined, the support for trackerless -torrents will be disabled.
TORRENT_DHT_VERBOSE_LOGGINGThis will enable verbose logging of the DHT -protocol traffic.
TORRENT_DISABLE_ENCRYPTIONThis will disable any encryption support and -the dependencies of a crypto library. -Encryption support is the peer connection -encrypted supported by clients such as -uTorrent, Azureus and KTorrent. -If this is not defined, either -TORRENT_USE_OPENSSL or -TORRENT_USE_GCRYPT must be defined.
_UNICODEOn windows, this will cause the file IO -use wide character API, to properly support -non-ansi characters.
TORRENT_DISABLE_RESOLVE_COUNTRIESDefining this will disable the ability to -resolve countries of origin for peer IPs.
TORRENT_DISABLE_INVARIANT_CHECKSThis will disable internal invariant checks in -libtorrent. The invariant checks can sometime -be quite expensive, they typically don't scale -very well. This option can be used to still -build in debug mode, with asserts enabled, but -make the resulting executable faster.
TORRENT_EXPENSIVE_INVARIANT_CHECKSThis will enable extra expensive invariant -checks. Useful for finding particular bugs -or for running before releases.
TORRENT_NO_DEPRECATEThis will exclude all deprecated functions from -the header files and cpp files.
-

If you experience that libtorrent uses unreasonable amounts of cpu, it will -definitely help to define NDEBUG, since it will remove the invariant checks -within the library.

-
-
-

building openssl for windows

-

To build openssl for windows with Visual Studio 7.1 (2003) execute the following commands -in a command shell:

-
-perl Configure VC-WIN32 --prefix="c:/openssl
-call ms\do_nasm
-call "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\bin\vcvars32.bat"
-nmake -f ms\nt.mak
-copy inc32\openssl "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\include\"
-copy out32\libeay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib"
-copy out32\ssleay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib"
-
-

This will also install the headers and library files in the visual studio directories to -be picked up by libtorrent.

-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/building.rst b/libtorrent_utp/docs/building.rst deleted file mode 100644 index 3e0fe3e96..000000000 --- a/libtorrent_utp/docs/building.rst +++ /dev/null @@ -1,645 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -downloading and building -======================== - -To acquire the latest version of libtorrent, you'll have to grab it from SVN. -You'll find instructions on how to do this here__ (see subversion access). - -__ http://sourceforge.net/svn/?group_id=79942 - -The build systems supported "out of the box" in libtorrent are boost-build v2 -(BBv2) and autotools (for unix-like systems). If you still can't build after -following these instructions, you can usually get help in the ``#libtorrent`` -IRC channel on ``irc.freenode.net``. - -.. warning:: - - A common mistake when building and linking against libtorrent is - to build with one set of configuration options (#defines) and - link against it using a different set of configuration options. Since - libtorrent has some code in header files, that code will not be - compatible with the built library if they see different configurations. - - Always make sure that the same TORRENT_* macros are defined when you - link against libtorrent as when you build it. - - Boost-build supports propagating configuration options to dependencies. - When building using the makefiles, this is handled by setting the - configuration options in the pkg-config file. Always use pkg-config - when linking against libtorrent. - -building from svn ------------------ - -To build libtorrent from svn you need to check out the libtorrent sources from -sourceforge and also check out the asio sources from its sourceforge cvs. -If you downloaded a release tarball, you can skip this section. - -To prepare the directory structure for building, follow these steps: - -* Check out libtorrent (instructions__). -* Check out asio (instructions__). -* Copy the ``asio/include/asio/`` directory into the ``libtorrent/include/libtorrent/`` - directory. Alternatively you can make a symbolic link. -* Copy ``asio/include/asio.hpp`` into ``libtorrent/include/libtorrent``. - -__ http://sourceforge.net/svn/?group_id=79942 -__ http://sourceforge.net/cvs/?group_id=122478 - -Now the libtorrent directory is ready for building. Follow the steps in one -of the following sections depending on which build system you prefer to use. - -building with BBv2 ------------------- - -The primary reason to use boost-build is that it will automatically build the -dependent boost libraries with the correct compiler settings, in order to -ensure that the build targets are link compatible (see `boost guidelines`__ -for some details on this issue). - -__ http://boost.org/more/separate_compilation.html - -Since BBv2 will build the boost libraries for you, you need the full boost -source package. Having boost installed via some package system is usually not -enough (and even if it is enough, the necessary environment variables are -usually not set by the package installer). - -If you want to build against an installed copy of boost, you can skip directly -to step 3 (assuming you also have boost build installed). - - -Step 1: Download boost -~~~~~~~~~~~~~~~~~~~~~~ - -You'll find boost here__. - -__ http://sourceforge.net/project/showfiles.php?group_id=7586&package_id=8041&release_id=619445 - -Extract the archive to some directory where you want it. For the sake of this -guide, let's assume you extract the package to ``c:\boost_1_34_0`` (I'm using -a windows path in this example since if you're on linux/unix you're more likely -to use the autotools). You'll need at least version 1.34 of the boost library -in order to build libtorrent. - - -Step 2: Setup BBv2 -~~~~~~~~~~~~~~~~~~ - -First you need to build ``bjam``. You do this by opening a terminal (In -windows, run ``cmd``). Change directory to -``c:\boost_1_34_0\tools\jam\src``. Then run the script called -``build.bat`` or ``build.sh`` on a unix system. This will build ``bjam`` and -place it in a directory starting with ``bin.`` and then have the name of your -platform. Copy the ``bjam.exe`` (or ``bjam`` on a unix system) to a place -that's in you shell's ``PATH``. On linux systems a place commonly used may be -``/usr/local/bin`` or on windows ``c:\windows`` (you can also add directories -to the search paths by modifying the environment variable called ``PATH``). - -Now you have ``bjam`` installed. ``bjam`` can be considered an interpreter -that the boost-build system is implemented on. So boost-build uses ``bjam``. -So, to complete the installation you need to make two more things. You need to -set the environment variable ``BOOST_BUILD_PATH``. This is the path that tells -``bjam`` where it can find boost-build, your configuration file and all the -toolsets (descriptions used by boost-build to know how to use different -compilers on different platforms). Assuming the boost install path above, set -it to ``c:\boost_1_34_0\tools\build\v2``. - -To set an environment variable in windows, type for example:: - - set BOOST_BUILD_PATH=c:\boost_1_34_0\tools\build\v2 - -In a terminal window. - -The last thing to do to complete the setup of BBv2 is to modify your -``user-config.jam`` file. It is located in ``c:\boost_1_34_0\tools\build\v2``. -Depending on your platform and which compiler you're using, you should add a -line for each compiler and compiler version you have installed on your system -that you want to be able to use with BBv2. For example, if you're using -Microsoft Visual Studio 7.1 (2003), just add a line:: - - using msvc : 7.1 ; - -If you use GCC, add the line:: - - using gcc ; - -If you have more than one version of GCC installed, you can add the -commandline used to invoke g++ after the version number, like this:: - - using gcc : 3.3 : g++-3.3 ; - using gcc : 4.0 : g++-4.0 ; - -Another toolset worth mentioning is the ``darwin`` toolset (For MacOS X). -From Tiger (10.4) MacOS X comes with both GCC 3.3 and GCC 4.0. Then you can -use the following toolsets:: - - using darwin : 3.3 : g++-3.3 ; - using darwin : 4.0 : g++-4.0 ; - -Note that the spaces around the semi-colons and colons are important! - -Also see the `official installation instructions`_. - -.. _`official installation instructions`: http://www.boost.org/doc/html/bbv2/installation.html - - -Step 3: Building libtorrent -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When building libtorrent, the ``Jamfile`` expects the environment variable -``BOOST_ROOT`` to be set to the boost installation directory. It uses this to -find the boost libraries it depends on, so they can be built and their headers -files found. So, set this to ``c:\boost_1_34_0``. You only need this if you're -building against a source distribution of boost. - -Then the only thing left is simply to invoke ``bjam``. If you want to specify -a specific toolset to use (compiler) you can just add that to the commandline. -For example:: - - bjam msvc-7.1 boost=source - bjam gcc-3.3 boost=source - bjam darwin-4.0 boost=source - -If you're building against a system installed boost, specify ``boost=system``. - -To build different versions you can also just add the name of the build -variant. Some default build variants in BBv2 are ``release``, ``debug``, -``profile``. - -You can build libtorrent as a dll too, by typing ``link=shared``, or -``link=static`` to build a static library. - -If you want to explicitly say how to link against the runtime library, you -can set the ``runtime-link`` feature on the commandline, either to ``shared`` -or ``static``. Most operating systems will only allow linking shared against -the runtime, but on windows you can do both. Example:: - - bjam msvc-7.1 link=static runtime-link=static boost=source - -.. warning:: - - If you link statically to the runtime library, you cannot build libtorrent - as a shared library (DLL), since you will get separate heaps in the library - and in the client application. It will result in crashes and possibly link - errors. - -.. warning:: - - With boost-build V2 (Milestone 11), the darwin toolset uses the ``-s`` linker - option to strip debug symbols. This option is buggy in Apple's GCC, and - will make the executable crash on startup. On Mac OS X, instead build - your release executables with the ``debug-symbols=on`` option, and - later strip your executable with ``strip``. - -.. warning:: - - Some linux systems requires linking against ``librt`` in order to access - the POSIX clock functions. If you get an error complaining about a missing - symbol ``clock_gettime``, you have to give ``need-librt=yes`` on the - bjam command line. This will make libtorrent link against ``librt``. - -.. note:: - - When building on Solaris, you might have to specify ``stdlib=sun-stlport`` - on the bjam command line. - -The build targets are put in a directory called bin, and under it they are -sorted in directories depending on the toolset and build variant used. - -To build the examples, just change directory to the examples directory and -invoke ``bjam`` from there. To build and run the tests, go to the test -directory and run ``bjam``. - -Note that if you're building on windows using the ``msvc`` toolset, you cannot run it -from a cygwin terminal, you'll have to run it from a ``cmd`` terminal. The same goes for -cygwin, if you're building with gcc in cygwin you'll have to run it from a cygwin terminal. -Also, make sure the paths are correct in the different environments. In cygwin, the paths -(``BOOST_BUILD_PATH`` and ``BOOST_ROOT``) should be in the typical unix-format (e.g. -``/cygdrive/c/boost_1_34_0``). In the windows environment, they should have the typical -windows format (``c:/boost_1_34_0``). - -The ``Jamfile`` will define ``NDEBUG`` when it's building a release build. -For more build configuration flags see `Build configurations`_. - -Build features: - -+--------------------------+----------------------------------------------------+ -| boost build feature | values | -+==========================+====================================================+ -| ``boost`` | * ``system`` - default. Tells the Jamfile that | -| | boost is installed and should be linked against | -| | the system libraries. | -| | * ``source`` - Specifies that boost is to be built | -| | from source. The environment variable | -| | ``BOOST_ROOT`` must be defined to point to the | -| | boost directory. | -+--------------------------+----------------------------------------------------+ -| ``boost-link`` | * ``static`` - links statically against the boost | -| | libraries. | -| | * ``shared`` - links dynamically against the boost | -| | libraries. | -+--------------------------+----------------------------------------------------+ -| ``logging`` | * ``none`` - no logging. | -| | * ``default`` - basic session logging. | -| | * ``verbose`` - verbose peer wire logging. | -| | * ``errors`` - like verbose, but limited to errors.| -+--------------------------+----------------------------------------------------+ -| ``dht-support`` | * ``on`` - build with support for tracker less | -| | torrents and DHT support. | -| | * ``logging`` - build with DHT support and verbose | -| | logging of the DHT protocol traffic. | -| | * ``off`` - build without DHT support. | -+--------------------------+----------------------------------------------------+ -| ``need-librt`` | * ``no`` - this platform does not need to link | -| | against librt to have POSIX time functions. | -| | * ``yes`` - specify this if your linux system | -| | requires you to link against librt.a. This is | -| | typically the case on x86 64 bit systems. | -+--------------------------+----------------------------------------------------+ -| ``asserts`` | * ``on`` - asserts are on if in debug mode | -| | * ``off`` - asserts are disabled | -| | * ``production`` - assertion failures are logged | -| | to ``asserts.log`` in the current working | -| | directory, but won't abort the process. | -+--------------------------+----------------------------------------------------+ -| ``geoip`` | * ``off`` - geo ip lookups disabled | -| | * ``static`` - MaxMind_ geo ip lookup code linked | -| | in statically. Note that this code is under | -| | LGPL license. | -| | * ``shared`` - The MaxMind_ geo ip lookup library | -| | is expected to be installed on the system and | -| | it will be used. | -+--------------------------+----------------------------------------------------+ -| ``upnp-logging`` | * ``off`` - default. Does not log UPnP traffic. | -| | * ``on`` - creates "upnp.log" with the messages | -| | sent to and received from UPnP devices. | -+--------------------------+----------------------------------------------------+ -| ``encryption`` | * ``openssl`` - links against openssl and | -| | libcrypto to enable https and encrypted | -| | bittorrent connections. | -| | * ``gcrypt`` - links against libgcrypt to enable | -| | encrypted bittorrent connections. | -| | * ``tommath`` - uses a shipped version of | -| | libtommath and a custom rc4 implementation | -| | (based on libtomcrypt). This is the default | -| | option. | -| | * ``off`` - turns off support for encrypted | -| | connections. The shipped public domain SHA-1 | -| | implementation is used. | -+--------------------------+----------------------------------------------------+ -| ``pool-allocators`` | * ``on`` - default, uses pool allocators for send | -| | buffers. | -| | * ``off`` - uses ``malloc()`` and ``free()`` | -| | instead. Might be useful to debug buffer issues | -| | with tools like electric fence or libgmalloc. | -+--------------------------+----------------------------------------------------+ -| ``link`` | * ``static`` - builds libtorrent as a static | -| | library (.a / .lib) | -| | * ``shared`` - builds libtorrent as a shared | -| | library (.so / .dll). | -+--------------------------+----------------------------------------------------+ -| ``runtime-link`` | * ``static`` - links statically against the | -| | run-time library (if available on your | -| | platform). | -| | * ``shared`` - link dynamically against the | -| | run-time library (default). | -+--------------------------+----------------------------------------------------+ -| ``variant`` | * ``debug`` - builds libtorrent with debug | -| | information and invariant checks. | -| | * ``release`` - builds libtorrent in release mode | -| | without invariant checks and with optimization. | -| | * ``profile`` - builds libtorrent with profile | -| | information. | -+--------------------------+----------------------------------------------------+ -| ``character-set`` | This setting will only have an affect on windows. | -| | Other platforms are expected to support UTF-8. | -| | | -| | * ``unicode`` - The unicode version of the win32 | -| | API is used. This is default. | -| | * ``ansi`` - The ansi version of the win32 API is | -| | used. | -+--------------------------+----------------------------------------------------+ -| ``invariant-checks`` | This setting only affects debug builds (where | -| | ``NDEBUG`` is not defined). It defaults to ``on``. | -| | | -| | * ``on`` - internal invariant checks are enabled. | -| | * ``off`` - internal invariant checks are | -| | disabled. The resulting executable will run | -| | faster than a regular debug build. | -| | * ``full`` - turns on extra expensive invariant | -| | checks. | -+--------------------------+----------------------------------------------------+ -| ``debug-symbols`` | * ``on`` - default for debug builds. This setting | -| | is useful for building release builds with | -| | symbols. | -| | * ``off`` - default for release builds. | -+--------------------------+----------------------------------------------------+ -| ``deprecated-functions`` | * ``on`` - default. Includes deprecated functions | -| | of the API (might produce warnings during build | -| | when deprecated functions are used). | -| | * ``off`` - excludes deprecated functions from the | -| | API. Generates build errors when deprecated | -| | functions are used. | -+--------------------------+----------------------------------------------------+ -| ``full-stats`` | * ``on`` - default, collects stats for IP overhead | -| | and DHT and trackers. This uses a little bit | -| | extra memory for each peer and torrent. | -| | * ``off`` - only collects the standard stats for | -| | upload and download rate. | -+--------------------------+----------------------------------------------------+ - -.. _MaxMind: http://www.maxmind.com/app/api - -The ``variant`` feature is *implicit*, which means you don't need to specify -the name of the feature, just the value. - -The logs created when building vlog or log mode are put in a directory called -``libtorrent_logs`` in the current working directory. - -When building the example client on windows, you need to build with -``link=static`` otherwise you may get unresolved external symbols for some -boost.program-options symbols. - -For more information, see the `Boost build v2 documentation`__, or more -specifically `the section on builtin features`__. - -__ http://www.boost.org/tools/build/v2/index.html -__ http://www.boost.org/doc/html/bbv2/reference.html#bbv2.advanced.builtins.features - - -building with autotools ------------------------ - -First of all, you need to install ``automake`` and ``autoconf``. Many -unix/linux systems comes with these preinstalled. - -The prerequisites for building libtorrent are boost.thread, boost.date_time -and boost.filesystem. Those are the *compiled* boost libraries needed. The -headers-only libraries needed include (but is not necessarily limited to) -boost.bind, boost.ref, boost.multi_index, boost.optional, boost.lexical_cast, -boost.integer, boost.iterator, boost.tuple, boost.array, boost.function, -boost.smart_ptr, boost.preprocessor, boost.static_assert. - -If you want to build the ``client_test`` example, you'll also need boost.regex -and boost.program_options. - -Step 1: Generating the build system -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -No build system is present if libtorrent is checked out from CVS - it -needs to be generated first. If you're building from a released tarball, -you may skip directly to `Step 2: Running configure`_. - -Execute the following commands, in the given order, to generate -the build system:: - - aclocal -I m4 - autoheader - libtoolize --copy --force - automake --add-missing --copy --gnu - autoconf - -On darwin/OSX you have to run ``glibtoolize`` instead of ``libtoolize``. - -Step 2: Running configure -~~~~~~~~~~~~~~~~~~~~~~~~~ - -In your shell, change directory to the libtorrent directory and run -``./configure``. This will look for libraries and C++ features that libtorrent -is dependent on. If something is missing or can't be found it will print an -error telling you what failed. - -The most likely problem you may encounter is that the configure script won't -find the boost libraries. Make sure you have boost installed on your system. -The easiest way to install boost is usually to use the preferred package -system on your platform. Usually libraries and headers are installed in -standard directories where the compiler will find them, but sometimes that -may not be the case. For example when installing boost on darwin using -darwinports (the package system based on BSD ports) all libraries are -installed to ``/opt/local/lib`` and headers are installed to -``/opt/local/include``. By default the compiler will not look in these -directories. You have to set the enviornment variables ``LDFLAGS`` and -``CXXFLAGS`` in order to make the compiler find those libs. In this example -you'd set them like this:: - - export LDFLAGS=-L/opt/local/lib - export CXXFLAGS=-I/opt/local/include - -It was observed on FreeBSD (release 6.0) that one needs to add '-lpthread' to -LDFLAGS, as Boost::Thread detection will fail without it, even if -Boost::Thread is installed. - -If you need to set these variables, it may be a good idea to add those lines -to your ``~/.profile`` or ``~/.tcshrc`` depending on your shell. - -If the boost libraries are named with a suffix on your platform, you may use -the ``--with-boost-thread=`` option to specify the suffix used for the thread -library in this case. For more information about these options, run:: - - ./configure --help - -On gentoo the boost libraries that are built with multi-threading support have -the suffix ``mt``. - -You know that the boost libraries were found if you see the following output -from the configure script:: - - checking whether the Boost::DateTime library is available... yes - checking for main in -lboost_date_time... yes - checking whether the Boost::Filesystem library is available... yes - checking for main in -lboost_filesystem... yes - checking whether the Boost::Thread library is available... yes - checking for main in -lboost_thread... yes - -Another possible source of problems may be if the path to your libtorrent -directory contains spaces. Make sure you either rename the directories with -spaces in their names to remove the spaces or move the libtorrent directory. - -Creating a debug build -~~~~~~~~~~~~~~~~~~~~~~ - -To tell configure to build a debug version (with debug info, asserts -and invariant checks enabled), you have to run the configure script -with the following option:: - - ./configure --enable-debug=yes - -Creating a release build -~~~~~~~~~~~~~~~~~~~~~~~~ - -To tell the configure to build a release version (without debug info, -asserts and invariant checks), you have to run the configure script -with the following option:: - - ./configure --enable-debug=no - -The above option make use of -DNDEBUG, which is used throughout libtorrent. - -Step 3: Building libtorrent -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once the configure script is run successfully, you just type ``make`` and -libtorrent, the examples and the tests will be built. - -When libtorrent is built it may be a good idea to run the tests, you do this -by running ``make check``. - -If you want to build a release version (without debug info, asserts and -invariant checks), you have to rerun the configure script and rebuild, like this:: - - ./configure --disable-debug - make clean - make - -building with other build systems ---------------------------------- - -If you're building in MS Visual Studio, you may have to set the compiler -options "force conformance in for loop scope", "treat wchar_t as built-in -type" and "Enable Run-Time Type Info" to Yes. - -build configurations --------------------- - -By default libtorrent is built In debug mode, and will have pretty expensive -invariant checks and asserts built into it. If you want to disable such checks -(you want to do that in a release build) you can see the table below for which -defines you can use to control the build. - -+----------------------------------------+-------------------------------------------------+ -| macro | description | -+========================================+=================================================+ -| ``NDEBUG`` | If you define this macro, all asserts, | -| | invariant checks and general debug code will be | -| | removed. Since there is quite a lot of code in | -| | in header files in libtorrent, it may be | -| | important to define the symbol consistently | -| | across compilation units, including the clients | -| | files. Potential problems is different | -| | compilation units having different views of | -| | structs and class layouts and sizes. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_LOGGING`` | This macro will enable logging of the session | -| | events, such as tracker announces and incoming | -| | connections (as well as blocked connections). | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_GEO_IP`` | This is defined by default by the Jamfile. It | -| | disables the GeoIP features, and avoids linking | -| | against LGPL:ed code. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_VERBOSE_LOGGING`` | If you define this macro, every peer connection | -| | will log its traffic to a log file as well as | -| | the session log. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_STORAGE_DEBUG`` | This will enable extra expensive invariant | -| | checks in the storage, including logging of | -| | piece sorting. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_UPNP_LOGGING`` | Generates a "upnp.log" file with the UPnP | -| | traffic. This is very useful when debugging | -| | support for various UPnP routers. | -| | support for various UPnP routers. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISK_STATS`` | This will create a log of all disk activity | -| | which later can parsed and graphed using | -| | ``parse_disk_log.py``. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_STATS`` | This will generate a log with transfer rates, | -| | downloading torrents, seeding torrents, peers, | -| | connecting peers and disk buffers in use. The | -| | log can be parsed and graphed with | -| | ``parse_session_stats.py``. | -+----------------------------------------+-------------------------------------------------+ -| ``UNICODE`` | If building on windows this will make sure the | -| | UTF-8 strings in pathnames are converted into | -| | UTF-16 before they are passed to the file | -| | operations. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_POOL_ALLOCATOR`` | Disables use of ``boost::pool<>``. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_LINKING_SHARED`` | If this is defined when including the | -| | libtorrent headers, the classes and functions | -| | will be tagged with ``__declspec(dllimport)`` | -| | on msvc and default visibility on GCC 4 and | -| | later. Set this in your project if you're | -| | linking against libtorrent as a shared library. | -| | (This is set by the Jamfile when | -| | ``link=shared`` is set). | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_BUILDING_SHARED`` | If this is defined, the functions and classes | -| | in libtorrent are marked with | -| | ``__declspec(dllexport)`` on msvc, or with | -| | default visibility on GCC 4 and later. This | -| | should be defined when building libtorrent as | -| | a shared library. (This is set by the Jamfile | -| | when ``link=shared`` is set). | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_DHT`` | If this is defined, the support for trackerless | -| | torrents will be disabled. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DHT_VERBOSE_LOGGING`` | This will enable verbose logging of the DHT | -| | protocol traffic. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_ENCRYPTION`` | This will disable any encryption support and | -| | the dependencies of a crypto library. | -| | Encryption support is the peer connection | -| | encrypted supported by clients such as | -| | uTorrent, Azureus and KTorrent. | -| | If this is not defined, either | -| | ``TORRENT_USE_OPENSSL`` or | -| | ``TORRENT_USE_GCRYPT`` must be defined. | -+----------------------------------------+-------------------------------------------------+ -| ``_UNICODE`` | On windows, this will cause the file IO | -| | use wide character API, to properly support | -| | non-ansi characters. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_RESOLVE_COUNTRIES`` | Defining this will disable the ability to | -| | resolve countries of origin for peer IPs. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_DISABLE_INVARIANT_CHECKS`` | This will disable internal invariant checks in | -| | libtorrent. The invariant checks can sometime | -| | be quite expensive, they typically don't scale | -| | very well. This option can be used to still | -| | build in debug mode, with asserts enabled, but | -| | make the resulting executable faster. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_EXPENSIVE_INVARIANT_CHECKS`` | This will enable extra expensive invariant | -| | checks. Useful for finding particular bugs | -| | or for running before releases. | -+----------------------------------------+-------------------------------------------------+ -| ``TORRENT_NO_DEPRECATE`` | This will exclude all deprecated functions from | -| | the header files and cpp files. | -+----------------------------------------+-------------------------------------------------+ - - -If you experience that libtorrent uses unreasonable amounts of cpu, it will -definitely help to define ``NDEBUG``, since it will remove the invariant checks -within the library. - -building openssl for windows ----------------------------- - -To build openssl for windows with Visual Studio 7.1 (2003) execute the following commands -in a command shell:: - - perl Configure VC-WIN32 --prefix="c:/openssl - call ms\do_nasm - call "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\bin\vcvars32.bat" - nmake -f ms\nt.mak - copy inc32\openssl "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\include\" - copy out32\libeay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib" - copy out32\ssleay32.lib "C:\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib" - -This will also install the headers and library files in the visual studio directories to -be picked up by libtorrent. - diff --git a/libtorrent_utp/docs/client_test.html b/libtorrent_utp/docs/client_test.html deleted file mode 100644 index 8149e67c0..000000000 --- a/libtorrent_utp/docs/client_test.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - -client_test example program - - - - - - -
-
-
- -
- -
-

client_test example program

- -

Client test is a, more or less, complete bittorrent client. It lacks most -settings and you can't start or stop torrents once you've started it. All -the settings are hardcoded. The commandline arguments are:

-
-client_test <filename1.torrent> <filename2.torrent> ...
-
-

You can start any number of torrent downloads/seeds via the commandline. -If one argument starts with http:// it is interpreted as a tracker -announce url, and it expects an info-hash as the next argument. The info-hash -has to be hex-encoded. For example: 2410d4554d5ed856d69f426c38791673c59f4418. -If you pass an announce url and info-hash, a torrent-less download is started. -It relies on that at least one peer on the tracker is running a libtorrent based -client and has the metadata (.torrent file). The metadata extension in -libtorrent will then download it from that peer (or from those peers if more -than one).

-

While running, the client_test sample will look something like this:

-client_test.png -

The commands available in the client are:

-
    -
  • q quits the client (there will be a delay while the client waits -for tracker responses)
  • -
  • l toggle log. Will display the log at the bottom, informing about -tracker and peer events.
  • -
  • i toggles torrent info. Will show the peer list for each torrent.
  • -
  • d toggle download info. Will show the block list for each torrent, -showing downloaded and requested blocks.
  • -
  • p pause all torrents.
  • -
  • u unpause all torrents.
  • -
  • r force tracker reannounce for all torrents.
  • -
  • f toggle show file progress. Displays a list of all files and the -download progress for each file.
  • -
-

The list at the bottom (shown if you press d) shows which blocks has -been requested from which peer. The green background means that it has been -downloaded. It shows that fast peers will prefer to request whole pieces -instead of dowloading parts of pieces. It may make it easier to determine -which peer that sent the corrupt data if a piece fails the hash test.

-unicode_support.png -

There's unicode support on linux, MacOS X and Windows.

-
- -
- - -
- - diff --git a/libtorrent_utp/docs/client_test.png b/libtorrent_utp/docs/client_test.png deleted file mode 100644 index 56bb8dec93d17d83f5deb2c11be74c7fc8d4b3a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110655 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 z 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 Lt?T($O`rY`In ... - -You can start any number of torrent downloads/seeds via the commandline. -If one argument starts with ``http://`` it is interpreted as a tracker -announce url, and it expects an info-hash as the next argument. The info-hash -has to be hex-encoded. For example: ``2410d4554d5ed856d69f426c38791673c59f4418``. -If you pass an announce url and info-hash, a torrent-less download is started. -It relies on that at least one peer on the tracker is running a libtorrent based -client and has the metadata (.torrent file). The metadata extension in -libtorrent will then download it from that peer (or from those peers if more -than one). - -While running, the ``client_test`` sample will look something like this: - -.. image:: client_test.png - -The commands available in the client are: - -* ``q`` quits the client (there will be a delay while the client waits - for tracker responses) -* ``l`` toggle log. Will display the log at the bottom, informing about - tracker and peer events. -* ``i`` toggles torrent info. Will show the peer list for each torrent. -* ``d`` toggle download info. Will show the block list for each torrent, - showing downloaded and requested blocks. -* ``p`` pause all torrents. -* ``u`` unpause all torrents. -* ``r`` force tracker reannounce for all torrents. -* ``f`` toggle show file progress. Displays a list of all files and the - download progress for each file. - -The list at the bottom (shown if you press ``d``) shows which blocks has -been requested from which peer. The green background means that it has been -downloaded. It shows that fast peers will prefer to request whole pieces -instead of dowloading parts of pieces. It may make it easier to determine -which peer that sent the corrupt data if a piece fails the hash test. - -.. image:: unicode_support.png - -There's unicode support on linux, MacOS X and Windows. - - diff --git a/libtorrent_utp/docs/contributing.html b/libtorrent_utp/docs/contributing.html deleted file mode 100644 index 64749656f..000000000 --- a/libtorrent_utp/docs/contributing.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - -libtorrent manual - - - - - - - -

-
-
- -
- -
-

libtorrent manual

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
-
-

Table of contents

- -
-
-

contributing to libtorrent

-

There are several ways to contribute to libtorrent at various levels. Any help is -much appreciated. If you're interested in something libtorrent related that's not -enumerated on this page, please contact arvid@rasterbar.com or the mailing list.

-
    -
  1. -
    Testing
    -

    This is not just limited to finding bugs and ways to reproduce crashes, but also -sub-optimal behavior is certain scenarios and finding ways to reproduce those. Please -report any issue to the bug tracker at google code.

    -

    New features that need testing are streaming (set_piece_deadline()), the different -choking algorithms (especially the new BitTyrant choker), the disk cache options (such -as explicit_cache).

    -
    -
    -
  2. -
-
    -
  1. -
    Documentation
    -

    Finding typos or outdated sections in the documentation. Contributing documentation -based on your own experience and experimentation with the library or with BitTorrent -in general. Non-reference documentation is very much welcome as well, higher level -descriptions on how to configure libtorrent for various situations for instance.

    -
    -
    -
  2. -
  3. -
    Code
    -

    Contributing code for new features or bug-fixes is highly welcome. If you're interested -in adding a feature but not sure where to start, please contact the mailing list or -#libtorrent @ irc.freenode.net.

    -

    New features might be better support for integrating with other services, new choking -algorithms, seeding policies, ports to new platforms etc.

    -
    -
    -
  4. -
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/contributing.rst b/libtorrent_utp/docs/contributing.rst deleted file mode 100644 index 45efb524c..000000000 --- a/libtorrent_utp/docs/contributing.rst +++ /dev/null @@ -1,45 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -contributing to libtorrent -========================== - -There are several ways to contribute to libtorrent at various levels. Any help is -much appreciated. If you're interested in something libtorrent related that's not -enumerated on this page, please contact arvid@rasterbar.com or the `mailing list`_. - -.. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss - -1. Testing - This is not just limited to finding bugs and ways to reproduce crashes, but also - sub-optimal behavior is certain scenarios and finding ways to reproduce those. Please - report any issue to the bug tracker at `google code`_. - - New features that need testing are streaming (``set_piece_deadline()``), the different - choking algorithms (especially the new BitTyrant choker), the disk cache options (such - as ``explicit_cache``). - -.. _`google code`: http://code.google.com/p/libtorrent/issues/entry - -2. Documentation - Finding typos or outdated sections in the documentation. Contributing documentation - based on your own experience and experimentation with the library or with BitTorrent - in general. Non-reference documentation is very much welcome as well, higher level - descriptions on how to configure libtorrent for various situations for instance. - -3. Code - Contributing code for new features or bug-fixes is highly welcome. If you're interested - in adding a feature but not sure where to start, please contact the `mailing list`_ or - ``#libtorrent`` @ ``irc.freenode.net``. - - New features might be better support for integrating with other services, new choking - algorithms, seeding policies, ports to new platforms etc. - diff --git a/libtorrent_utp/docs/cwnd.png b/libtorrent_utp/docs/cwnd.png deleted file mode 100644 index 9f850caa7a84b54f896447625f57a8e54ff3d04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18998 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 diff --git a/libtorrent_utp/docs/delays.png b/libtorrent_utp/docs/delays.png deleted file mode 100644 index 66db05e98bac6c83bcfc27448a7376d774492013..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10875 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 gesbs>QD*ylh diff --git a/libtorrent_utp/docs/delays_thumb.png b/libtorrent_utp/docs/delays_thumb.png deleted file mode 100644 index 84739446cfa90df6736fc1fe7f7aa367062351dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10938 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 JPDHLkV1knd^E?0m diff --git a/libtorrent_utp/docs/dht_extensions.html b/libtorrent_utp/docs/dht_extensions.html deleted file mode 100644 index a68a7dc03..000000000 --- a/libtorrent_utp/docs/dht_extensions.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - -
-
-
- -
- -
- - --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
-
-

Mainline DHT extensions

-

libtorrent implements a few extensions to the Mainline DHT protocol.

-
-

get_peers response

-

libtorrent always responds with nodes to a get_peers request. If it has -peers for the specified info-hash, it will return values as well. This is -because just because some peer announced to us, doesn't mean that we are -among the 8 closest nodes of the info hash. libtorrent also keeps traversing -nodes using get_peers until it has found the 8 closest ones, and then announces -to those nodes.

-
-
-

client identification

-

In each DHT packet, an extra key is inserted named "v". This is a string -describing the client and version used. This can help alot when debugging -and finding errors in client implementations. The string is encoded as four -characters, two characters describing the client and two characters interpreted -as a binary number describing the client version.

-

Currently known clients:

- ---- - - - - - - - - - - - - - - -
uTorrentUT
libtorrentLT
MooPoliceMP
GetRightGR
-
-
-

IPv6 support

-

The DHT messages that don't support IPv6 are the nodes replies. -They encode all the contacts as 6 bytes packed together in sequence in a -string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but -needs 18 bytes. The extension libtorrent applies is to add another key, called -nodes2.

-

nodes2 may be present in replies that contains a nodes key. It is encoded -as a list of strings. Each string represents one contact and is encoded as 20 -bytes node-id and then a variable length encoded IP address (6 bytes in IPv4 case -and 18 bytes in IPv6 case).

-

As an optimization, libtorrent does not include the extra key in case there are -only IPv4 nodes present.

-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/dht_extensions.rst b/libtorrent_utp/docs/dht_extensions.rst deleted file mode 100644 index 9761a7675..000000000 --- a/libtorrent_utp/docs/dht_extensions.rst +++ /dev/null @@ -1,55 +0,0 @@ -:Author: Arvid Norberg, arvid@rasterbar.com - -Mainline DHT extensions -======================= - -libtorrent implements a few extensions to the Mainline DHT protocol. - -get_peers response ------------------- - -libtorrent always responds with ``nodes`` to a get_peers request. If it has -peers for the specified info-hash, it will return ``values`` as well. This is -because just because some peer announced to us, doesn't mean that we are -among the 8 closest nodes of the info hash. libtorrent also keeps traversing -nodes using get_peers until it has found the 8 closest ones, and then announces -to those nodes. - -client identification ---------------------- - -In each DHT packet, an extra key is inserted named "v". This is a string -describing the client and version used. This can help alot when debugging -and finding errors in client implementations. The string is encoded as four -characters, two characters describing the client and two characters interpreted -as a binary number describing the client version. - -Currently known clients: - -+---------------+--------+ -| uTorrent | ``UT`` | -+---------------+--------+ -| libtorrent | ``LT`` | -+---------------+--------+ -| MooPolice | ``MP`` | -+---------------+--------+ -| GetRight | ``GR`` | -+---------------+--------+ - -IPv6 support ------------- - -The DHT messages that don't support IPv6 are the ``nodes`` replies. -They encode all the contacts as 6 bytes packed together in sequence in a -string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but -needs 18 bytes. The extension libtorrent applies is to add another key, called -``nodes2``. - -``nodes2`` may be present in replies that contains a ``nodes`` key. It is encoded -as a list of strings. Each string represents one contact and is encoded as 20 -bytes node-id and then a variable length encoded IP address (6 bytes in IPv4 case -and 18 bytes in IPv6 case). - -As an optimization, libtorrent does not include the extra key in case there are -only IPv4 nodes present. - diff --git a/libtorrent_utp/docs/disk_access.png b/libtorrent_utp/docs/disk_access.png deleted file mode 100644 index c236b6bb37a4fcb89294942cbc7f6bbe65d22b9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3032 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

4KrO9$$ZWL509 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 diff --git a/libtorrent_utp/docs/disk_buffer.png b/libtorrent_utp/docs/disk_buffer.png deleted file mode 100644 index fb617d0e68f786a77be8a343663be0764a374e0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5170 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 diff --git a/libtorrent_utp/docs/disk_buffer_before_optimization.png b/libtorrent_utp/docs/disk_buffer_before_optimization.png deleted file mode 100644 index dcb4c4560cc23a921b8e072a6d581a6ac0e8397e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4846 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 diff --git a/libtorrent_utp/docs/disk_io.png b/libtorrent_utp/docs/disk_io.png deleted file mode 100644 index 0ec82d167b789fa1cc5d6523896ee8c7342a26ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4393 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 UAtk4vq@*Murlz5ypdq86r1&oh5cHUX ziG_!Sg-1aMCZza(!0s;RE8~`R3HZBMQ zi1+ALBLM(G7?>bz3~XFX5Z+@95aY1}QUEp?3yzQyIjbJRBb-7Q7oJ|){F#!9O+?wo z^P8ww#BXZ$7BLAGZy#jY#_1yu9S{KeKPrGn77T14;L*oN3c$d4Q~}|F{-+8c1^`4# z#v+9IDA0_>CM=@o5&j#I{`uR(GJxPQ2!j+v3Qz#tb!nr2_YbWcPotK%i`dTp#?Uc4 zd$9ivJ^N~2jMVD&x9zI&#Ea|tj>nN1>Y7j|0fagTBwlh`e!{Yi`ci#lmh-U)SwbGS zC|Aa$^Rg_Ox5r~Iwq*^DDrI(@E=6qF#?*~y()0-SP88OZb_$A*KzJPRpuHm)4}joP z>zD24B2Q9UMJLnn$GJH)nzrSHObj(YnW#)v@U=F~hhV9A91}?k$$k84Z+6*RuT46o z{`_yz=>4J{z%OG-7#pFMSYQn$EK|!y)<+y;)X$|)`>)q!dZIXPcr$<(EW^`9W~!Iw z>5^ahsV(h0=GN@`V_{CZt6!v9*sI)hyw3S7xKKXT@K6|iBGqbGo_z8C z{o7Eby^|n9Ok;;xYDRNGHJ_(r4sTo_^43WjqqPS1vX%y2_<9m2GKNgO^zxpTR!-b6 zy}p!N-)w;_EgtmZ~56qXT!AQdP}z89a{s@tH=w@Iz|0`ZvA+jV3-8F z_tYh6iy4z8I74Gqz^sp$4^wh}zzgWAUp6Wzb5id8yJ-NfMQ%4p&$H{{015*QJFO4hiOp7wviPugSV6R zO2LNh8Ue!w$VS3>@rI#Ucs95UgIG8*b&B*uoJarCzesWi7(vq*x~vUc1r1J-n61(E z0(yDGO%izKw3+WvUky|Q$fRoX;&p0oku`D5ZVj9iM^!-+GOU9YJ`&P%O)jxYuOypQ zVH@1K#%3l)Jvj-IkNZSqSI}L6yydtr2FwXv{%hLWM?J2>Ad!h69*X1L-I8rCC!jv@&YU+(!rH!z z;x~(E4d#)5RmRJkH_muJ`m@P@5x*)7C!lBA6lQn-*m$a4pC2uU;=l_06W889=lNIY zQ9B2ozg0ptVwMSkl@DBRE@iUwh&`o~(H^nugHnWlKkZ2+E*1x{|)r<)Yg8S-~f>OpYXCfG1 zzy1Ci`G(XcjCcVe`=Qxxd+sE&p6SmHLu8ypC+%0tzh`3*5olMA2 zlF6RK?bAR zD89QX`>*Cf{BH(sqsVtQ!>;TCzBQcub~ngE+^1+@AQuy95+awg0VNDY`$(cFDVcic z8V`iHhDMHSdVICV_Fehl+Qa`YhE&p^^eQ>4oY3Ci;Pz#MeR3(k`7Gb^_c9iXh=NFH z^3A5I9>qWfw<4-7J-GC7oWIR>~6SRm9~Cc7Ea zH%}6i0>$=s5W1Jr{eAKy#`knc=T@eR+8+!3Bb+i`G8xMZuM7RNREy~NBnjH^um_tb zQj9N~BstKMgG!07FGpPI_${T${^Ajtq<3=f2MviNS(Lz|`xlo`XrqcxM%V5dyHUa+ zn<%b88;AJxqb^Sa3DHinQGHi_B_<+Bi-Cc^MKV&_e3%$)i7oP!q+(2h+%s7`5$?Ys zIXXV5>H))8ZMciysrHnodD~FDql69n`m{*v^=s9FIh`{WIUTr8&EOY89T@T(%O+QP zToW7!&-MjtFfTdE+!AZ5Hww(3C#H-A)KJMjO>X@&fK3|WD-K%o3h6ZY#bp3>*7@}> zo#L>ZfSNWM+-j=i0!}Oc!JY~>KOV;GN>Rqot(i4-_}p<)ARW8>=4v1#qo(!MH!4ce zNh~wXyy4caZM^nAMqiK{c?$fzVyMu

@%NL)e>LdJ;v<%T=T@bvlHXgf%*1asMWH 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 qo$b!bK#VOcJ9~e-g}fow - - - - - -libtorrent Examples - - - - - - - -

-
- - -
-

libtorrent Examples

- --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
-
-

Table of contents

- -
-
-

examples

-

Except for the example programs in this manual, there's also a bigger example -of a (little bit) more complete client, client_test. There are separate -instructions for how to use it here if you'd like to try it. Note that building -client_test also requires boost.regex and boost.program_options library.

-
-

dump_torrent

-

This is an example of a program that will take a torrent-file as a parameter and -print information about it to std out:

-
-#include <iostream>
-#include <fstream>
-#include <iterator>
-#include <iomanip>
-
-#include "libtorrent/entry.hpp"
-#include "libtorrent/bencode.hpp"
-#include "libtorrent/torrent_info.hpp"
-#include "libtorrent/lazy_entry.hpp"
-#include <boost/filesystem/operations.hpp>
-
-
-int main(int argc, char* argv[])
-{
-        using namespace libtorrent;
-        using namespace boost::filesystem;
-
-        if (argc != 2)
-        {
-                std::cerr << "usage: dump_torrent torrent-file\n";
-                return 1;
-        }
-#if BOOST_VERSION < 103400
-        boost::filesystem::path::default_name_check(boost::filesystem::no_check);
-#endif
-
-#ifndef BOOST_NO_EXCEPTIONS
-        try
-        {
-#endif
-
-                int size = file_size(argv[1]);
-                if (size > 10 * 1000000)
-                {
-                        std::cerr << "file too big (" << size << "), aborting\n";
-                        return 1;
-                }
-                std::vector<char> buf(size);
-                std::ifstream(argv[1], std::ios_base::binary).read(&buf[0], size);
-                lazy_entry e;
-                int ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), e);
-
-                if (ret != 0)
-                {
-                        std::cerr << "invalid bencoding: " << ret << std::endl;
-                        return 1;
-                }
-
-                std::cout << "\n\n----- raw info -----\n\n";
-                std::cout << e << std::endl;
-
-                torrent_info t(e);
-
-                // print info about torrent
-                std::cout << "\n\n----- torrent file info -----\n\n";
-                std::cout << "nodes:\n";
-                typedef std::vector<std::pair<std::string, int> > node_vec;
-                node_vec const& nodes = t.nodes();
-                for (node_vec::const_iterator i = nodes.begin(), end(nodes.end());
-                        i != end; ++i)
-                {
-                        std::cout << i->first << ":" << i->second << "\n";
-                }
-                std::cout << "trackers:\n";
-                for (std::vector<announce_entry>::const_iterator i = t.trackers().begin();
-                        i != t.trackers().end(); ++i)
-                {
-                        std::cout << i->tier << ": " << i->url << "\n";
-                }
-
-                std::cout << "number of pieces: " << t.num_pieces() << "\n";
-                std::cout << "piece length: " << t.piece_length() << "\n";
-                std::cout << "info hash: " << t.info_hash() << "\n";
-                std::cout << "comment: " << t.comment() << "\n";
-                std::cout << "created by: " << t.creator() << "\n";
-                std::cout << "files:\n";
-                int index = 0;
-                for (torrent_info::file_iterator i = t.begin_files();
-                        i != t.end_files(); ++i, ++index)
-                {
-                        int first = t.map_file(index, 0, 1).piece;
-                        int last = t.map_file(index, i->size - 1, 1).piece;
-                        std::cout << "  " << std::setw(11) << i->size
-                                << " " << i->path.string() << "[ " << first << ", "
-                                << last << " ]\n";
-                }
-
-#ifndef BOOST_NO_EXCEPTIONS
-        }
-        catch (std::exception& e)
-        {
-                std::cout << e.what() << "\n";
-        }
-#endif
-
-        return 0;
-}
-
-
-
-

simple client

-

This is a simple client. It doesn't have much output to keep it simple:

-
-int main(int argc, char* argv[])
-{
-        using namespace libtorrent;
-#if BOOST_VERSION < 103400
-        namespace fs = boost::filesystem;
-        fs::path::default_name_check(fs::no_check);
-#endif
-
-if (argc != 2)
-{
-        std::cerr << "usage: ./simple_client torrent-file\n"
-                "to stop the client, press return.\n";
-        return 1;
-}
-
-#ifndef BOOST_NO_EXCEPTIONS
-        try
-#endif
-        {
-                session s;
-                s.listen_on(std::make_pair(6881, 6889));
-                add_torrent_params p;
-                p.save_path = "./";
-                p.ti = new torrent_info(argv[1]);
-                s.add_torrent(p);
-
-                // wait for the user to end
-                char a;
-                std::cin.unsetf(std::ios_base::skipws);
-                std::cin >> a;
-        }
-#ifndef BOOST_NO_EXCEPTIONS
-        catch (std::exception& e)
-        {
-                std::cout << e.what() << "\n";
-        }
-#endif
-        return 0;
-}
-
-
-
-

make_torrent

-

Shows how to create a torrent from a directory tree:

-
-#include <iostream>
-#include <fstream>
-#include <iterator>
-#include <iomanip>
-
-#include "libtorrent/entry.hpp"
-#include "libtorrent/bencode.hpp"
-#include "libtorrent/torrent_info.hpp"
-#include "libtorrent/file.hpp"
-#include "libtorrent/storage.hpp"
-#include "libtorrent/hasher.hpp"
-#include "libtorrent/create_torrent.hpp"
-
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/bind.hpp>
-
-using namespace boost::filesystem;
-using namespace libtorrent;
-
-// do not include files and folders whose
-// name starts with a .
-bool file_filter(boost::filesystem::path const& filename)
-{
-        if (filename.leaf()[0] == '.') return false;
-        std::cerr << filename << std::endl;
-        return true;
-}
-
-void print_progress(int i, int num)
-{
-        std::cerr << "\r" << (i+1) << "/" << num;
-}
-
-int main(int argc, char* argv[])
-{
-        using namespace libtorrent;
-        using namespace boost::filesystem;
-
-        int piece_size = 256 * 1024;
-        char const* creator_str = "libtorrent";
-
-        path::default_name_check(no_check);
-
-        if (argc != 4 && argc != 5)
-        {
-                std::cerr << "usage: make_torrent <output torrent-file> "
-                "<announce url> <file or directory to create torrent from> "
-                "[url-seed]\n";
-        return 1;
-}
-
-#ifndef BOOST_NO_EXCEPTIONS
-        try
-        {
-#endif
-                file_storage fs;
-                file_pool fp;
-                path full_path = complete(path(argv[3]));
-
-                add_files(fs, full_path, file_filter);
-
-                create_torrent t(fs, piece_size);
-                t.add_tracker(argv[2]);
-                set_piece_hashes(t, full_path.branch_path()
-                        , boost::bind(&print_progress, _1, t.num_pieces()));
-                std::cerr << std::endl;
-                t.set_creator(creator_str);
-
-                if (argc == 5) t.add_url_seed(argv[4]);
-
-                // create the torrent and print it to out
-                ofstream out(complete(path(argv[1])), std::ios_base::binary);
-                bencode(std::ostream_iterator<char>(out), t.generate());
-#ifndef BOOST_NO_EXCEPTIONS
-        }
-        catch (std::exception& e)
-        {
-                std::cerr << e.what() << "\n";
-        }
-#endif
-
-        return 0;
-}
-
-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/examples.rst b/libtorrent_utp/docs/examples.rst deleted file mode 100644 index 1ad5aa012..000000000 --- a/libtorrent_utp/docs/examples.rst +++ /dev/null @@ -1,260 +0,0 @@ -=================== -libtorrent Examples -=================== - -:Author: Arvid Norberg, arvid@rasterbar.com - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -examples -======== - -Except for the example programs in this manual, there's also a bigger example -of a (little bit) more complete client, ``client_test``. There are separate -instructions for how to use it here__ if you'd like to try it. Note that building -``client_test`` also requires boost.regex and boost.program_options library. - -__ client_test.html - -dump_torrent ------------- - -This is an example of a program that will take a torrent-file as a parameter and -print information about it to std out:: - - #include - #include - #include - #include - - #include "libtorrent/entry.hpp" - #include "libtorrent/bencode.hpp" - #include "libtorrent/torrent_info.hpp" - #include "libtorrent/lazy_entry.hpp" - #include - - - int main(int argc, char* argv[]) - { - using namespace libtorrent; - using namespace boost::filesystem; - - if (argc != 2) - { - std::cerr << "usage: dump_torrent torrent-file\n"; - return 1; - } - #if BOOST_VERSION < 103400 - boost::filesystem::path::default_name_check(boost::filesystem::no_check); - #endif - - #ifndef BOOST_NO_EXCEPTIONS - try - { - #endif - - int size = file_size(argv[1]); - if (size > 10 * 1000000) - { - std::cerr << "file too big (" << size << "), aborting\n"; - return 1; - } - std::vector buf(size); - std::ifstream(argv[1], std::ios_base::binary).read(&buf[0], size); - lazy_entry e; - int ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), e); - - if (ret != 0) - { - std::cerr << "invalid bencoding: " << ret << std::endl; - return 1; - } - - std::cout << "\n\n----- raw info -----\n\n"; - std::cout << e << std::endl; - - torrent_info t(e); - - // print info about torrent - std::cout << "\n\n----- torrent file info -----\n\n"; - std::cout << "nodes:\n"; - typedef std::vector > node_vec; - node_vec const& nodes = t.nodes(); - for (node_vec::const_iterator i = nodes.begin(), end(nodes.end()); - i != end; ++i) - { - std::cout << i->first << ":" << i->second << "\n"; - } - std::cout << "trackers:\n"; - for (std::vector::const_iterator i = t.trackers().begin(); - i != t.trackers().end(); ++i) - { - std::cout << i->tier << ": " << i->url << "\n"; - } - - std::cout << "number of pieces: " << t.num_pieces() << "\n"; - std::cout << "piece length: " << t.piece_length() << "\n"; - std::cout << "info hash: " << t.info_hash() << "\n"; - std::cout << "comment: " << t.comment() << "\n"; - std::cout << "created by: " << t.creator() << "\n"; - std::cout << "files:\n"; - int index = 0; - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); ++i, ++index) - { - int first = t.map_file(index, 0, 1).piece; - int last = t.map_file(index, i->size - 1, 1).piece; - std::cout << " " << std::setw(11) << i->size - << " " << i->path.string() << "[ " << first << ", " - << last << " ]\n"; - } - - #ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - std::cout << e.what() << "\n"; - } - #endif - - return 0; - } - -simple client -------------- - -This is a simple client. It doesn't have much output to keep it simple:: - - int main(int argc, char* argv[]) - { - using namespace libtorrent; - #if BOOST_VERSION < 103400 - namespace fs = boost::filesystem; - fs::path::default_name_check(fs::no_check); - #endif - - if (argc != 2) - { - std::cerr << "usage: ./simple_client torrent-file\n" - "to stop the client, press return.\n"; - return 1; - } - - #ifndef BOOST_NO_EXCEPTIONS - try - #endif - { - session s; - s.listen_on(std::make_pair(6881, 6889)); - add_torrent_params p; - p.save_path = "./"; - p.ti = new torrent_info(argv[1]); - s.add_torrent(p); - - // wait for the user to end - char a; - std::cin.unsetf(std::ios_base::skipws); - std::cin >> a; - } - #ifndef BOOST_NO_EXCEPTIONS - catch (std::exception& e) - { - std::cout << e.what() << "\n"; - } - #endif - return 0; - } - -make_torrent ------------- - -Shows how to create a torrent from a directory tree:: - - #include - #include - #include - #include - - #include "libtorrent/entry.hpp" - #include "libtorrent/bencode.hpp" - #include "libtorrent/torrent_info.hpp" - #include "libtorrent/file.hpp" - #include "libtorrent/storage.hpp" - #include "libtorrent/hasher.hpp" - #include "libtorrent/create_torrent.hpp" - - #include - #include - #include - #include - - using namespace boost::filesystem; - using namespace libtorrent; - - // do not include files and folders whose - // name starts with a . - bool file_filter(boost::filesystem::path const& filename) - { - if (filename.leaf()[0] == '.') return false; - std::cerr << filename << std::endl; - return true; - } - - void print_progress(int i, int num) - { - std::cerr << "\r" << (i+1) << "/" << num; - } - - int main(int argc, char* argv[]) - { - using namespace libtorrent; - using namespace boost::filesystem; - - int piece_size = 256 * 1024; - char const* creator_str = "libtorrent"; - - path::default_name_check(no_check); - - if (argc != 4 && argc != 5) - { - std::cerr << "usage: make_torrent " - " " - "[url-seed]\n"; - return 1; - } - - #ifndef BOOST_NO_EXCEPTIONS - try - { - #endif - file_storage fs; - file_pool fp; - path full_path = complete(path(argv[3])); - - add_files(fs, full_path, file_filter); - - create_torrent t(fs, piece_size); - t.add_tracker(argv[2]); - set_piece_hashes(t, full_path.branch_path() - , boost::bind(&print_progress, _1, t.num_pieces())); - std::cerr << std::endl; - t.set_creator(creator_str); - - if (argc == 5) t.add_url_seed(argv[4]); - - // create the torrent and print it to out - ofstream out(complete(path(argv[1])), std::ios_base::binary); - bencode(std::ostream_iterator(out), t.generate()); - #ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - std::cerr << e.what() << "\n"; - } - #endif - - return 0; - } - diff --git a/libtorrent_utp/docs/extension_protocol.html b/libtorrent_utp/docs/extension_protocol.html deleted file mode 100644 index 093f833f8..000000000 --- a/libtorrent_utp/docs/extension_protocol.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - -
-
-
- -
- -
- - --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com -Ludvig Strigeus, ludde@utorrent.com
-
-

extension protocol for bittorrent

-

The intention of this protocol is to provide a simple and thin transport -for extensions to the bittorrent protocol. Supporting this protocol makes -it easy to add new extensions without interfering with the standard -bittorrent protocol or clients that don't support this extension or the -one you want to add.

-

To advertise to other clients that you support, one bit from the reserved -bytes is used.

-

The bit selected for the extension protocol is bit 20 from the right (counting -starts at 0). So (reserved_byte[5] & 0x10) is the expression to use for checking -if the client supports extended messaging.

-

Once support for the protocol is established, the client is supposed to -support 1 new message:

- ---- - - - - - - - - - - -
nameid
extended20
-

This message is sent as any other bittorrent message, with a 4 byte length -prefix and a single byte identifying the message (the single byte being 20 -in this case). At the start of the payload of the message, is a single byte -message identifier. This identifier can refer to different extension messages -and only one ID is specified, 0. If the ID is 0, the message is a handshake -message which is described below. The layout of a general extended message -follows (including the message headers used by the bittorrent protocol):

- ---- - - - - - - - - - - - - - - - - -
sizedescription
uint32_tlength prefix. Specifies the number of bytes for the -entire message. (Big endian)
uint8_tbittorrent message ID, = 20
uint8_textended message ID. 0 = handshake, >0 = extended -message as specified by the handshake.
-
-

handshake message

-

The payload of the handshake message is a bencoded dictionary. All items -in the dictionary are optional. Any unknown names should be ignored -by the client. All parts of the dictionary are case sensitive. -This is the defined item in the dictionary:

- ---- - - - - - - - - - - -
namedescription
m

Dictionary of supported extension messages which maps -names of extensions to an extended message ID for each -extension message. The only requirement on these IDs -is that no extension message share the same one. Setting -an extension number to zero means that the extension is -not supported/disabled. The client should ignore any -extension names it doesn't recognize.

-

The extension message IDs are the IDs used to send the -extension messages to the peer sending this handshake. -i.e. The IDs are local to this particular peer.

-
-

Here are some other items that an implementation may choose to support:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - -
namedescription
pLocal TCP listen port. Allows each side to learn about -the TCP port number of the other side. Note that there is -no need for the receiving side of the connection to send -this extension message, since its port number is already -known.
vClient name and version (as a utf-8 string). -This is a much more reliable way of identifying the -client than relying on the peer id encoding.
youripA string containing the compact representation of the ip -address this peer sees you as. i.e. this is the -receiver's external ip address (no port is included). -This may be either an IPv4 (4 bytes) or an IPv6 -(16 bytes) address.
ipv6If this peer has an IPv6 interface, this is the compact -representation of that address (16 bytes). The client may -prefer to connect back via the IPv6 address.
ipv4If this peer has an IPv4 interface, this is the compact -representation of that address (4 bytes). The client may -prefer to connect back via this interface.
reqqAn integer, the number of outstanding request messages -this client supports without dropping any. The default in -in libtorrent is 250.
-

The handshake dictionary could also include extended handshake -information, such as support for encrypted headers or anything -imaginable.

-

An example of what the payload of a handshake message could look like:

- ---- - - - - - - - - - - - - - - - -
Dictionary
m ---- - - - - - - - - - - - - -
Dictionary
LT_metadata1
ut_pex2
-
p6881
v"µTorrent 1.2"
-

and in the encoded form:

-

d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent 1.2e

-

To make sure the extension names do not collide by mistake, they should be -prefixed with the two (or one) character code that is used to identify the -client that introduced the extension. This applies for both the names of -extension messages, and for any additional information put inside the -top-level dictionary. All one and two byte identifiers are invalid to use -unless defined by this specification.

-

This message should be sent immediately after the standard bittorrent handshake -to any peer that supports this extension protocol. It is valid to send the -handshake message more than once during the lifetime of a connection, -the sending client should not be disconnected. An implementation may choose -to ignore the subsequent handshake messages (or parts of them).

-

Subsequent handshake messages can be used to enable/disable extensions -without restarting the connection. If a peer supports changing extensions -at run time, it should note that the m dictionary is additive. -It's enough that it contains the actual CHANGES to the extension list. -To disable the support for LT_metadata at run-time, without affecting -any other extensions, this message should be sent: -d11:LT_metadatai0ee. -As specified above, the value 0 is used to turn off an extension.

-

The extension IDs must be stored for every peer, becuase every peer may have -different IDs for the same extension.

-

This specification, deliberately, does not specify any extensions such as -peer-exchange or metadata exchange. This protocol is merely a transport -for the actual extensions to the bittorrent protocol and the extensions -named in the example above (such as p) are just examples of possible -extensions.

-
-
-

rationale

-

The reason why the extension messages' IDs would be defined in the handshake -is to avoid having a global registry of message IDs. Instead the names of the -extension messages requires unique names, which is much easier to do without -a global registry. The convention is to use a two letter prefix on the -extension message names, the prefix would identify the client first -implementing the extension message. e.g. LT_metadata is implemented by -libtorrent, and hence it has the LT prefix.

-

If the client supporting the extensions can decide which numbers the messages -it receives will have, it means they are constants within that client. i.e. -they can be used in switch statements. It's easy for the other end to -store an array with the ID's we expect for each message and use that for -lookups each time it sends an extension message.

-

The reason for having a dictionary instead of having an array (using -implicitly assigned index numbers to the extensions) is that if a client -want to disable some extensions, the ID numbers would change, and it wouldn't -be able to use constants (and hence, not use them in a switch). If the -messages IDs would map directly to bittorrent message IDs, It would also make -it possible to map extensions in the handshake to existing extensions with -fixed message IDs.

-

The reasoning behind having a single byte as extended message identifier is -to follow the the bittorrent spec. with its single byte message identifiers. -It is also considered to be enough. It won't limit the total number of -extensions, only the number of extensions used simultaneously.

-

The reason for using single byte identifiers for the standardized handshake -identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves -bandwidth. The only advantage of longer messages is that it makes the -protocol more readable for a human, but the BT protocol wasn't designed to -be a human readable protocol, so why bother.

-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/extension_protocol.rst b/libtorrent_utp/docs/extension_protocol.rst deleted file mode 100644 index 742944a0b..000000000 --- a/libtorrent_utp/docs/extension_protocol.rst +++ /dev/null @@ -1,202 +0,0 @@ -:Author: Arvid Norberg, arvid@rasterbar.com - Ludvig Strigeus, ludde@utorrent.com - -extension protocol for bittorrent -================================= - -The intention of this protocol is to provide a simple and thin transport -for extensions to the bittorrent protocol. Supporting this protocol makes -it easy to add new extensions without interfering with the standard -bittorrent protocol or clients that don't support this extension or the -one you want to add. - -To advertise to other clients that you support, one bit from the reserved -bytes is used. - -The bit selected for the extension protocol is bit 20 from the right (counting -starts at 0). So (reserved_byte[5] & 0x10) is the expression to use for checking -if the client supports extended messaging. - -Once support for the protocol is established, the client is supposed to -support 1 new message: - -+------------------------+----+ -|name | id | -+========================+====+ -|``extended`` | 20 | -+------------------------+----+ - -This message is sent as any other bittorrent message, with a 4 byte length -prefix and a single byte identifying the message (the single byte being 20 -in this case). At the start of the payload of the message, is a single byte -message identifier. This identifier can refer to different extension messages -and only one ID is specified, 0. If the ID is 0, the message is a handshake -message which is described below. The layout of a general ``extended`` message -follows (including the message headers used by the bittorrent protocol): - -+----------+---------------------------------------------------------+ -| size | description | -+==========+=========================================================+ -| uint32_t | length prefix. Specifies the number of bytes for the | -| | entire message. (Big endian) | -+----------+---------------------------------------------------------+ -| uint8_t | bittorrent message ID, = 20 | -+----------+---------------------------------------------------------+ -| uint8_t | extended message ID. 0 = handshake, >0 = extended | -| | message as specified by the handshake. | -+----------+---------------------------------------------------------+ - - -handshake message ------------------ - -The payload of the handshake message is a bencoded dictionary. All items -in the dictionary are optional. Any unknown names should be ignored -by the client. All parts of the dictionary are case sensitive. -This is the defined item in the dictionary: - -+-------+-----------------------------------------------------------+ -| name | description | -+=======+===========================================================+ -| m | Dictionary of supported extension messages which maps | -| | names of extensions to an extended message ID for each | -| | extension message. The only requirement on these IDs | -| | is that no extension message share the same one. Setting | -| | an extension number to zero means that the extension is | -| | not supported/disabled. The client should ignore any | -| | extension names it doesn't recognize. | -| | | -| | The extension message IDs are the IDs used to send the | -| | extension messages to the peer sending this handshake. | -| | i.e. The IDs are local to this particular peer. | -+-------+-----------------------------------------------------------+ - - -Here are some other items that an implementation may choose to support: - -+--------+-----------------------------------------------------------+ -| name | description | -+========+===========================================================+ -| p | Local TCP listen port. Allows each side to learn about | -| | the TCP port number of the other side. Note that there is | -| | no need for the receiving side of the connection to send | -| | this extension message, since its port number is already | -| | known. | -+--------+-----------------------------------------------------------+ -| v | Client name and version (as a utf-8 string). | -| | This is a much more reliable way of identifying the | -| | client than relying on the peer id encoding. | -+--------+-----------------------------------------------------------+ -| yourip | A string containing the compact representation of the ip | -| | address this peer sees you as. i.e. this is the | -| | receiver's external ip address (no port is included). | -| | This may be either an IPv4 (4 bytes) or an IPv6 | -| | (16 bytes) address. | -+--------+-----------------------------------------------------------+ -| ipv6 | If this peer has an IPv6 interface, this is the compact | -| | representation of that address (16 bytes). The client may | -| | prefer to connect back via the IPv6 address. | -+--------+-----------------------------------------------------------+ -| ipv4 | If this peer has an IPv4 interface, this is the compact | -| | representation of that address (4 bytes). The client may | -| | prefer to connect back via this interface. | -+--------+-----------------------------------------------------------+ -| reqq | An integer, the number of outstanding request messages | -| | this client supports without dropping any. The default in | -| | in libtorrent is 250. | -+--------+-----------------------------------------------------------+ - -The handshake dictionary could also include extended handshake -information, such as support for encrypted headers or anything -imaginable. - -An example of what the payload of a handshake message could look like: - -+------------------------------------------------------+ -| Dictionary | -+===================+==================================+ -| ``m`` | +--------------------------+ | -| | | Dictionary | | -| | +======================+===+ | -| | | ``LT_metadata`` | 1 | | -| | +----------------------+---+ | -| | | ``ut_pex`` | 2 | | -| | +----------------------+---+ | -| | | -+-------------------+----------------------------------+ -| ``p`` | 6881 | -+-------------------+----------------------------------+ -| ``v`` | "µTorrent 1.2" | -+-------------------+----------------------------------+ - -and in the encoded form: - -``d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent 1.2e`` - -To make sure the extension names do not collide by mistake, they should be -prefixed with the two (or one) character code that is used to identify the -client that introduced the extension. This applies for both the names of -extension messages, and for any additional information put inside the -top-level dictionary. All one and two byte identifiers are invalid to use -unless defined by this specification. - -This message should be sent immediately after the standard bittorrent handshake -to any peer that supports this extension protocol. It is valid to send the -handshake message more than once during the lifetime of a connection, -the sending client should not be disconnected. An implementation may choose -to ignore the subsequent handshake messages (or parts of them). - -Subsequent handshake messages can be used to enable/disable extensions -without restarting the connection. If a peer supports changing extensions -at run time, it should note that the ``m`` dictionary is additive. -It's enough that it contains the actual *CHANGES* to the extension list. -To disable the support for ``LT_metadata`` at run-time, without affecting -any other extensions, this message should be sent: -``d11:LT_metadatai0ee``. -As specified above, the value 0 is used to turn off an extension. - -The extension IDs must be stored for every peer, becuase every peer may have -different IDs for the same extension. - -This specification, deliberately, does not specify any extensions such as -peer-exchange or metadata exchange. This protocol is merely a transport -for the actual extensions to the bittorrent protocol and the extensions -named in the example above (such as ``p``) are just examples of possible -extensions. - -rationale ---------- - -The reason why the extension messages' IDs would be defined in the handshake -is to avoid having a global registry of message IDs. Instead the names of the -extension messages requires unique names, which is much easier to do without -a global registry. The convention is to use a two letter prefix on the -extension message names, the prefix would identify the client first -implementing the extension message. e.g. ``LT_metadata`` is implemented by -libtorrent, and hence it has the ``LT`` prefix. - -If the client supporting the extensions can decide which numbers the messages -it receives will have, it means they are constants within that client. i.e. -they can be used in ``switch`` statements. It's easy for the other end to -store an array with the ID's we expect for each message and use that for -lookups each time it sends an extension message. - -The reason for having a dictionary instead of having an array (using -implicitly assigned index numbers to the extensions) is that if a client -want to disable some extensions, the ID numbers would change, and it wouldn't -be able to use constants (and hence, not use them in a ``switch``). If the -messages IDs would map directly to bittorrent message IDs, It would also make -it possible to map extensions in the handshake to existing extensions with -fixed message IDs. - -The reasoning behind having a single byte as extended message identifier is -to follow the the bittorrent spec. with its single byte message identifiers. -It is also considered to be enough. It won't limit the total number of -extensions, only the number of extensions used simultaneously. - -The reason for using single byte identifiers for the standardized handshake -identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves -bandwidth. The only advantage of longer messages is that it makes the -protocol more readable for a human, but the BT protocol wasn't designed to -be a human readable protocol, so why bother. - diff --git a/libtorrent_utp/docs/fatrat.png b/libtorrent_utp/docs/fatrat.png deleted file mode 100644 index 64ea542ab0de0b74edb759d1a313cb7169cfdadd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45067 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 Q=+jXl=Rbs`2_V}mK{#P)onBJoX1Nh{Warzz`8 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 bq#UGHUsi~tIcPWw!~sWp7rRF`#Du>A=nax@ diff --git a/libtorrent_utp/docs/features.html b/libtorrent_utp/docs/features.html deleted file mode 100644 index 0f64ccdc8..000000000 --- a/libtorrent_utp/docs/features.html +++ /dev/null @@ -1,401 +0,0 @@ - - - - - - -libtorrent manual - - - - - - - -
-
- - -
-

libtorrent manual

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
- -
-

introduction

-

libtorrent is a feature complete C++ bittorrent implementation focusing -on efficiency and scalability. It runs on embedded devices as well as -desktops. It boasts a well documented library interface that is easy to -use. It comes with a simple bittorrent client demonstrating the use of -the library.

-
-
-

features

-

libtorrent is under active development. It is an ongoing project. Its -current state supports and includes the following features:

-
-

extensions

-
    -
  • plugin interface for implementing custom bittorrent extensions -without having to modify libtorrent
  • -
  • supports trackerless torrents (using the Mainline kademlia DHT protocol) with -some DHT extensions. BEP 5.
  • -
  • supports the bittorrent extension protocol. See extensions. BEP 10.
  • -
  • supports the uTorrent metadata transfer protocol BEP 9 (i.e. magnet links).
  • -
  • supports the uTorrent peer exchange protocol (PEX).
  • -
  • supports local peer discovery (multicasts for peers on the same local network)
  • -
  • multitracker extension support (supports both strict BEP 12 and the -uTorrent interpretation).
  • -
  • tracker scrapes
  • -
  • supports lt_trackers extension, to exchange trackers between peers
  • -
  • HTTP seeding, as specified in BEP 17 and BEP 19.
  • -
  • supports the udp-tracker protocol. (BEP 15).
  • -
  • supports the no_peer_id=1 extension that will ease the load off trackers.
  • -
  • supports the compact=1 tracker parameter.
  • -
  • super seeding/initial seeding (BEP 16).
  • -
  • private torrents (BEP 27).
  • -
  • upload-only extension (BEP 21).
  • -
  • support for IPv6, including BEP 7 and BEP 24.
  • -
  • support for merkle hash tree torrents. This makes the size of torrent files -scale well with the size of the content.
  • -
  • share-mode. This is a special mode torrents can be put in to optimize share -ratio rather than downloading the torrent.
  • -
-
-
-

disk management

-
    -
  • uses a separate disk I/O thread to not have the disk ever block on network or -client interaction. (see threads).
  • -
  • supports files > 2 gigabytes.
  • -
  • fast resume support, a way to get rid of the costly piece check at the -start of a resumed torrent. Saves the storage state, piece_picker state -as well as all local peers in a separate fast-resume file.
  • -
  • has an adjustable read and write disk cache for improved disk throughput.
  • -
  • queues torrents for file check, instead of checking all of them in parallel.
  • -
  • does not have any requirements on the piece order in a torrent that it -resumes. This means it can resume a torrent downloaded by any client.
  • -
  • supports both sparse files and compact file allocation (where pieces -are kept consolidated on disk)
  • -
  • seed mode, where the files on disk are assumed to be complete, and each -piece's hash is verified the first time it is requested.
  • -
-
-
-

network

-
    -
  • a high quality uTP implementation (BEP29_). A transport protocol with -delay based congestion control. See separate article.
  • -
  • adjusts the length of the request queue depending on download rate.
  • -
  • serves multiple torrents on a single port and in a single thread
  • -
  • piece picking on block-level (as opposed to piece-level). -This means it can download parts of the same piece from different peers. -It will also prefer to download whole pieces from single peers if the -download speed is high enough from that particular peer.
  • -
  • supports http proxies and basic proxy authentication
  • -
  • supports gzipped tracker-responses
  • -
  • can limit the upload and download bandwidth usage and the maximum number of -unchoked peers
  • -
  • possibility to limit the number of connections.
  • -
  • delays have messages if there's no other outgoing traffic to the peer, and -doesn't send have messages to peers that already has the piece. This saves -bandwidth.
  • -
  • selective downloading. The ability to select which parts of a torrent you -want to download.
  • -
  • ip filter to disallow ip addresses and ip ranges from connecting and -being connected.
  • -
  • NAT-PMP and UPnP support (automatic port mapping on routers that supports it)
  • -
  • implements automatic upload slots, to optimize download rate without spreading -upload capacity too thin. The number of upload slots is adjusted based on the -peers' download capacity to work even for connections that are orders of -magnitude faster than others.
  • -
-
-
-
-

highlighted features

-
-

disk caching

-

All disk I/O in libtorrent is done asynchronously to the network thread, by the -disk io thread. When a block is read, the disk io thread reads all subsequent -blocks from that piece into the read cache, assuming that the peer requesting -the block will also request more blocks from the same piece. This decreases the -number of syscalls for reading data. It also decreases delay from seeking.

-

Similarly, for write requests, blocks are cached and flushed to disk once one full -piece is complete or the piece is the least recently updated one when more cache -space is needed. The cache dynamically allocates space between the write and read -cache. The write cache is strictly prioritized over the read cache.

-

The cache blocks that are in used, are locked into physical memory to avoid it -being paged out to disk. Allowing the disk cache to be paged out to disk means -that it would become extremely inefficient to flush it, since it would have to be -read back into physical memory only to be flushed back out to disk again.

-

In order to conserve memory, and system calls, iovec file operations are -used to flush multiple cache blocks in a single call.

-

On low-memory systems, the disk cache can be disabled altogether or set to smaller -limit, to save memory.

-

The disk caching algorithm is configurable between 'LRU' and 'largest contiguous'. -The largest contiguous algorithm is the default and flushes the largest contiguous -block of buffers, instead of flushing all blocks belonging to the piece which was -written to least recently.

-

For version 0.15 a lot of work went into optimizing the cache algorithm, trying -to increase the cache hit rate and utilization. The graph to the left shows the -memory utilization in 0.14. This cache is a straight forward, fairly naive, implementation. -Every block read will also read all subsequent blocks in that piece into the cache. -Whenever we need more space, the entire oldest piece is evicted from the cache. Caching -writes always takes presedence over the read cache. Whenever a piece is fully downloaded, -it is flushed to disk.

-disk_buffer_before_optimization.png -disk_buffer.png -

The left graph shows the problem of evicting entire pieces at a time, and waiting until -an entire piece is downloaded until flushing it. These graphs were generated for a torrent -with fairly large pieces. This means that granularity was poor in 0.14, since it only -dealt with entire pieces. In 0.15, the granularity problem has been fixed by evicting one -block at a time from the read cache. This maximizes the read cache utilization. The write -cache is also flushed when a sufficient number of contiguous blocks have been downloaded -for a piece, which is not tied to the piece size anymore. This way the cache scales a lot -better with piece sizes.

-

The graph to the right shows the same download but with the new optimized disk cache -algorithm. It clearly shows an increased utilization, which means higher read hit rates -or smaller caches with maintained hit rate.

-
-
-

high performance disk subsystem

-

In some circumstances, the disk cache may not suffice to provide maximum performance. -One such example is high performance seeding, to a large number of peers, over a fast -up-link. In such a case, the amount of RAM may simply not be enough to cache disk -reads. When there's not enough RAM to cache disk reads, the disk throughput would -typically degrade to perform as poorly as with no cache at all, with the majority -of the time spent waiting for the disk head to seek.

-

To solve this problem, libtorrent sorts read requests by their physical offset on the -disk. They are processed by having the disk read head sweep back and forth over the drive.

-

This makes libtorrent very suitable for large scale, high-throughput seeding.

-disk_access_no_elevator.png -disk_access_elevator.png -

These plots illustrates the physical disk offset for reads over time. The left plot -is of a run where disk operation re-ordering is turned off and the righ is when it's -turned on. The right one has a relatively smooth sine wave shape whereas the left -one is more random and involves much longer seeks back and forth over the disk.

-

True physical disk offset queries are only supported on newer linux kernels, Mac OS X and -Windows 2000 and up.

-
-
-

network buffers

-

On CPUs with small L2 caches, copying memory can be expensive operations. It is important -to keep copying to a minimum on such machines. This mostly applies to embedded systems.

-

In order to minimize the number of times received data is copied, the receive buffer -for payload data is received directly into a page aligned disk buffer. If the connection -is encrypted, the buffer is decrypted in-place. The buffer is then moved into the disk -cache without being copied. Once all the blocks for a piece have been received, or the -cache needs to be flushed, all the blocks are passed directly to writev() to flush -them in a single syscall. This means a single copy into user space memory, and a single -copy back into kernel memory, as illustrated by this figure:

-write_disk_buffers.png -

When seeding and uploading in general, unnecessary copying is avoided by caching blocks -in aligned buffers, that are copied once into the peer's send buffer. The peer's send buffer -is not guaranteed to be aligned, even though it is most of the time. The send buffer is -then encrypted with the peer specific key and chained onto the iovec for sending. -This means there is one user space copy in order to allow unaligned peer requests and -peer-specific encryption. This is illustrated by the following figure:

-read_disk_buffers.png -
-
-

piece picker

-

The piece picker is a central component in a bittorrent implementation. The piece picker -in libtorrent is optimized for quickly finding the rarest pieces. It keeps a list of all -available pieces sorted by rarity, and pieces with the same rarity, shuffled. The rarest -first mode is the dominant piece picker mode. Other modes are supported as well, and -used by peers in specific situations.

-

The piece picker allows to combine the availability of a piece with a priority. Together -they determine the sort order of the piece list. Pieces with priority 0 will never be -picked, which is used for the selective download feature.

-

In order to have as few partially finished pieces as possible, peers have an affinity -towards picking blocks from the same pieces as other peers in the same speed category. -The speed category is a coarse categorization of peers based on their download rate. This -makes slow peers pick blocks from the same piece, and fast peers pick from the same piece, -and hence decreasing the likelihood of slow peers blocking the completion of pieces.

-

The piece picker can also be set to download pieces in sequential order.

-
-
-

share mode

-

The share mode feature in libtorrent is intended for users who are only interested in -helping out swarms, not downloading the torrents.

-

It works by predicting the demand for pieces, and only download pieces if there is enough -demand. New pieces will only be downloaded once the share ratio has hit a certain target.

-

This feature is especially useful when combined with RSS, so that a client can be set up -to provide additional bandwidth to an entire feed.

-
-
-

merkle hash tree torrents

-

Merkle hash tree torrents is an extension that lets a torrent file only contain the -root hash of the hash tree forming the piece hashes. The main benefit of this feature -is that regardless of how many pieces there is in a torrent, the .torrent file will -always be the same size. It will only grow with the number of files (since it still -has to contain the file names).

-

With regular torrents, clients have to request multiple blocks for pieces, typically -from different peers, before the data can be verified against the piece hash. The -larger the pieces are, the longer it will take to download a complete piece and verify -it. Before the piece is verified, it cannot be shared with the swarm, which means the -larger piece sizes, the slower turnaround data has when it is downloaded by peers. -Since on average the data has to sit around, waiting, in client buffers before it has -been verified and can be uploaded again.

-

Another problem with large piece sizes is that it is harder for a client to pinpoint -the malicious or buggy peer when a piece fails, and it will take longer to re-download -it and take more tries before the piece succeeds the larger the pieces are.

-

The piece size in regular torrents is a tradeoff between the size of the .torrent file -itself and the piece size. Often, for files that are 4 GB, the piece size is 2 or 4 MB, -just to avoid making the .torrent file too big.

-

Merkle torrents solves these problems by removing the tradeoff between .torrent size and -piece size. With merkle torrents, the piece size can be the minimum block size (16 kB), -which lets peers verify every block of data received from peers, immediately. This -gives a minimum turnaround time and completely removes the problem of identifying malicious -peers.

-merkle_tree.png -

The root hash is built by hashing all the piece hashes pair-wise, until they all collapse -down to the root.

-storage.png -
-
-

customizable file storage

-

libtorrent's storage implementation is customizable. That means a special purpose bittorrent -client can replace the default way to store files on disk.

-

When implementing a bittorrent cache, it doesn't matter how the data is stored on disk, as -long as it can be retrieved and seeded. In that case a new storage class can be implemented -(inheriting from the storage_interface class) that avoids the unnecessary step of mapping -slots to files and offsets. The storage can ignore the file boundaries and just store the -entire torrent in a single file (which will end up being all the files concatenated). The main -advantage of this, other than a slight cpu performance gain, is that all file operations would -be page (and sector) aligned. This enables efficient unbuffered I/O, and can potentially -lead to more efficient read caching (using the built in disk cache rather than relying on the -operating system's disk cache).

-

The storage interface supports operating systems where you can ask for sparse regions -(such as Windows and Solaris). The advantage of this is that when checking files, the regions -that are known to be sparse can be skipped, which can reduce the time to check a torrent -significantly.

-
-
-

easy to use API

-

One of the design goals of the libtorrent API is to make common operations simple, but still -have it possible to do complicated and advanced operations. This is best illustrated by example -code to implement a simple bittorrent client:

-
-#include <iostream>
-#include "libtorrent/session.hpp"
-
-// usage a.out [torrent-file]
-int main(int argc, char* argv[]) try
-{
-        using namespace libtorrent;
-
-        session s;
-        s.listen_on(std::make_pair(6881, 6889));
-        add_torrent_params p;
-        p.save_path = "./";
-        p.ti = new torrent_info(argv[1]);
-        s.add_torrent(p);
-
-        // wait for the user to end
-        char a;
-        std::cin.unsetf(std::ios_base::skipws);
-        std::cin >> a;
-        return 0;
-}
-catch (std::exception& e)
-{
-        std::cerr << ec.what() << std::endl;
-        return 1;
-}
-
-

This client doesn't give the user any status information or progress about the torrent, but -it is fully functional.

-

libtorrent also comes with python bindings for easy access for python developers.

-
-
-
-

portability

-

libtorrent runs on most major operating systems, including Windows, -MacOS X, Linux, BSD and Solaris. -It uses Boost.Thread, Boost.Filesystem, Boost.Date_time and various other -boost libraries as well as zlib (shipped) and asio (shipped). At least version -1.34.1 of boost is required.

-

libtorrent uses asio, hence it will take full advantage of high performance -network APIs on the most popular platforms. I/O completion ports on windows, -epoll on linux and kqueue on MacOS X and BSD.

-

libtorrent has been successfully compiled and tested on:

-
    -
  • Windows 2000, XP and Vista vc7.1, vc8
  • -
  • Linux x86 GCC 3.3, GCC 3.4.2, 4.x
  • -
  • Linux PPC GCC 4.1.1
  • -
  • MacOS X (darwin), (Apple's) GCC 3.3, (Apple's) GCC 4.0
  • -
  • SunOS 5.8 GCC 3.1 and Sunpro
  • -
  • Cygwin GCC 3.3.3
  • -
-

Fails on:

-
    -
  • GCC 2.95.4
  • -
  • msvc6
  • -
-
-
-

Docutils System Messages

-
-

System Message: ERROR/3 (features.rst, line 82); backlink

-Unknown target name: "bep29".
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/features.rst b/libtorrent_utp/docs/features.rst deleted file mode 100644 index f7c7447d4..000000000 --- a/libtorrent_utp/docs/features.rst +++ /dev/null @@ -1,405 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -introduction -============ - -libtorrent is a feature complete C++ bittorrent implementation focusing -on efficiency and scalability. It runs on embedded devices as well as -desktops. It boasts a well documented library interface that is easy to -use. It comes with a simple bittorrent client demonstrating the use of -the library. - -features -======== - -libtorrent is under active development. It is an ongoing project. Its -current state supports and includes the following features: - -extensions ----------- - -* plugin interface for implementing custom bittorrent extensions - without having to modify libtorrent -* supports trackerless torrents (using the Mainline kademlia DHT protocol) with - some `DHT extensions`_. `BEP 5`_. -* supports the bittorrent `extension protocol`_. See extensions_. `BEP 10`_. -* supports the uTorrent metadata transfer protocol `BEP 9`_ (i.e. magnet links). -* supports the uTorrent peer exchange protocol (PEX). -* supports local peer discovery (multicasts for peers on the same local network) -* multitracker extension support (supports both strict `BEP 12`_ and the - uTorrent interpretation). -* tracker scrapes -* supports lt_trackers extension, to exchange trackers between peers -* `HTTP seeding`_, as specified in `BEP 17`_ and `BEP 19`_. -* supports the udp-tracker protocol. (`BEP 15`_). -* supports the ``no_peer_id=1`` extension that will ease the load off trackers. -* supports the ``compact=1`` tracker parameter. -* super seeding/initial seeding (`BEP 16`_). -* private torrents (`BEP 27`_). -* upload-only extension (`BEP 21`_). -* support for IPv6, including `BEP 7`_ and `BEP 24`_. -* support for merkle hash tree torrents. This makes the size of torrent files - scale well with the size of the content. -* share-mode. This is a special mode torrents can be put in to optimize share - ratio rather than downloading the torrent. - -.. _article: utp.html -.. _extensions: manual.html#extensions -.. _`http seeding`: manual.html#http-seeding - -disk management ---------------- - -* uses a separate disk I/O thread to not have the disk ever block on network or - client interaction. (see threads_). -* supports files > 2 gigabytes. -* fast resume support, a way to get rid of the costly piece check at the - start of a resumed torrent. Saves the storage state, piece_picker state - as well as all local peers in a separate fast-resume file. -* has an adjustable read and write disk cache for improved disk throughput. -* queues torrents for file check, instead of checking all of them in parallel. -* does not have any requirements on the piece order in a torrent that it - resumes. This means it can resume a torrent downloaded by any client. -* supports both sparse files and compact file allocation (where pieces - are kept consolidated on disk) -* seed mode, where the files on disk are assumed to be complete, and each - piece's hash is verified the first time it is requested. - -.. _threads: manual.html#threads - -network -------- - -* a high quality uTP implementation (BEP29_). A transport protocol with - delay based congestion control. See separate article_. -* adjusts the length of the request queue depending on download rate. -* serves multiple torrents on a single port and in a single thread -* piece picking on block-level (as opposed to piece-level). - This means it can download parts of the same piece from different peers. - It will also prefer to download whole pieces from single peers if the - download speed is high enough from that particular peer. -* supports http proxies and basic proxy authentication -* supports gzipped tracker-responses -* can limit the upload and download bandwidth usage and the maximum number of - unchoked peers -* possibility to limit the number of connections. -* delays have messages if there's no other outgoing traffic to the peer, and - doesn't send have messages to peers that already has the piece. This saves - bandwidth. -* selective downloading. The ability to select which parts of a torrent you - want to download. -* ip filter to disallow ip addresses and ip ranges from connecting and - being connected. -* NAT-PMP and UPnP support (automatic port mapping on routers that supports it) -* implements automatic upload slots, to optimize download rate without spreading - upload capacity too thin. The number of upload slots is adjusted based on the - peers' download capacity to work even for connections that are orders of - magnitude faster than others. - - -.. _`DHT extensions`: dht_extensions.html -.. _`BEP 5`: http://bittorrent.org/beps/bep_0005.html -.. _`BEP 7`: http://bittorrent.org/beps/bep_0007.html -.. _`BEP 9`: http://bittorrent.org/beps/bep_0009.html -.. _`BEP 10`: http://bittorrent.org/beps/bep_0010.html -.. _`BEP 12`: http://bittorrent.org/beps/bep_0012.html -.. _`BEP 15`: http://bittorrent.org/beps/bep_0015.html -.. _`BEP 16`: http://bittorrent.org/beps/bep_0016.html -.. _`BEP 17`: http://bittorrent.org/beps/bep_0017.html -.. _`BEP 19`: http://bittorrent.org/beps/bep_0019.html -.. _`BEP 21`: http://bittorrent.org/beps/bep_0021.html -.. _`BEP 24`: http://bittorrent.org/beps/bep_0024.html -.. _`BEP 27`: http://bittorrent.org/beps/bep_0027.html -.. _`BEP 29`: http://bittorrent.org/beps/bep_0029.html -.. _`extension protocol`: extension_protocol.html - -highlighted features -==================== - -disk caching ------------- - -All disk I/O in libtorrent is done asynchronously to the network thread, by the -disk io thread. When a block is read, the disk io thread reads all subsequent -blocks from that piece into the read cache, assuming that the peer requesting -the block will also request more blocks from the same piece. This decreases the -number of syscalls for reading data. It also decreases delay from seeking. - -Similarly, for write requests, blocks are cached and flushed to disk once one full -piece is complete or the piece is the least recently updated one when more cache -space is needed. The cache dynamically allocates space between the write and read -cache. The write cache is strictly prioritized over the read cache. - -The cache blocks that are in used, are locked into physical memory to avoid it -being paged out to disk. Allowing the disk cache to be paged out to disk means -that it would become extremely inefficient to flush it, since it would have to be -read back into physical memory only to be flushed back out to disk again. - -In order to conserve memory, and system calls, iovec file operations are -used to flush multiple cache blocks in a single call. - -On low-memory systems, the disk cache can be disabled altogether or set to smaller -limit, to save memory. - -The disk caching algorithm is configurable between 'LRU' and 'largest contiguous'. -The largest contiguous algorithm is the default and flushes the largest contiguous -block of buffers, instead of flushing all blocks belonging to the piece which was -written to least recently. - -For version 0.15 a lot of work went into optimizing the cache algorithm, trying -to increase the cache hit rate and utilization. The graph to the left shows the -memory utilization in 0.14. This cache is a straight forward, fairly naive, implementation. -Every block read will also read all subsequent blocks in that piece into the cache. -Whenever we need more space, the entire oldest piece is evicted from the cache. Caching -writes always takes presedence over the read cache. Whenever a piece is fully downloaded, -it is flushed to disk. - -.. image:: disk_buffer_before_optimization.png - :width: 49% - -.. image:: disk_buffer.png - :width: 49% - -The left graph shows the problem of evicting entire pieces at a time, and waiting until -an entire piece is downloaded until flushing it. These graphs were generated for a torrent -with fairly large pieces. This means that granularity was poor in 0.14, since it only -dealt with entire pieces. In 0.15, the granularity problem has been fixed by evicting one -block at a time from the read cache. This maximizes the read cache utilization. The write -cache is also flushed when a sufficient number of contiguous blocks have been downloaded -for a piece, which is not tied to the piece size anymore. This way the cache scales a lot -better with piece sizes. - -The graph to the right shows the same download but with the new optimized disk cache -algorithm. It clearly shows an increased utilization, which means higher read hit rates -or smaller caches with maintained hit rate. - -high performance disk subsystem -------------------------------- - -In some circumstances, the disk cache may not suffice to provide maximum performance. -One such example is high performance seeding, to a large number of peers, over a fast -up-link. In such a case, the amount of RAM may simply not be enough to cache disk -reads. When there's not enough RAM to cache disk reads, the disk throughput would -typically degrade to perform as poorly as with no cache at all, with the majority -of the time spent waiting for the disk head to seek. - -To solve this problem, libtorrent sorts read requests by their physical offset on the -disk. They are processed by having the disk read head sweep back and forth over the drive. - -This makes libtorrent very suitable for large scale, high-throughput seeding. - -.. image:: disk_access_no_elevator.png - :width: 49% - -.. image:: disk_access_elevator.png - :width: 49% - -These plots illustrates the physical disk offset for reads over time. The left plot -is of a run where disk operation re-ordering is turned off and the righ is when it's -turned on. The right one has a relatively smooth sine wave shape whereas the left -one is more random and involves much longer seeks back and forth over the disk. - -True physical disk offset queries are only supported on newer linux kernels, Mac OS X and -Windows 2000 and up. - -network buffers ---------------- - -On CPUs with small L2 caches, copying memory can be expensive operations. It is important -to keep copying to a minimum on such machines. This mostly applies to embedded systems. - -In order to minimize the number of times received data is copied, the receive buffer -for payload data is received directly into a page aligned disk buffer. If the connection -is encrypted, the buffer is decrypted in-place. The buffer is then moved into the disk -cache without being copied. Once all the blocks for a piece have been received, or the -cache needs to be flushed, all the blocks are passed directly to ``writev()`` to flush -them in a single syscall. This means a single copy into user space memory, and a single -copy back into kernel memory, as illustrated by this figure: - -.. image:: write_disk_buffers.png - :width: 100% - -When seeding and uploading in general, unnecessary copying is avoided by caching blocks -in aligned buffers, that are copied once into the peer's send buffer. The peer's send buffer -is not guaranteed to be aligned, even though it is most of the time. The send buffer is -then encrypted with the peer specific key and chained onto the ``iovec`` for sending. -This means there is one user space copy in order to allow unaligned peer requests and -peer-specific encryption. This is illustrated by the following figure: - -.. image:: read_disk_buffers.png - :width: 100% - - -piece picker ------------- - -The piece picker is a central component in a bittorrent implementation. The piece picker -in libtorrent is optimized for quickly finding the rarest pieces. It keeps a list of all -available pieces sorted by rarity, and pieces with the same rarity, shuffled. The rarest -first mode is the dominant piece picker mode. Other modes are supported as well, and -used by peers in specific situations. - -The piece picker allows to combine the availability of a piece with a priority. Together -they determine the sort order of the piece list. Pieces with priority 0 will never be -picked, which is used for the selective download feature. - -In order to have as few partially finished pieces as possible, peers have an affinity -towards picking blocks from the same pieces as other peers in the same speed category. -The speed category is a coarse categorization of peers based on their download rate. This -makes slow peers pick blocks from the same piece, and fast peers pick from the same piece, -and hence decreasing the likelihood of slow peers blocking the completion of pieces. - -The piece picker can also be set to download pieces in sequential order. - -share mode ----------- - -The share mode feature in libtorrent is intended for users who are only interested in -helping out swarms, not downloading the torrents. - -It works by predicting the demand for pieces, and only download pieces if there is enough -demand. New pieces will only be downloaded once the share ratio has hit a certain target. - -This feature is especially useful when combined with RSS, so that a client can be set up -to provide additional bandwidth to an entire feed. - -merkle hash tree torrents -------------------------- - -Merkle hash tree torrents is an extension that lets a torrent file only contain the -root hash of the hash tree forming the piece hashes. The main benefit of this feature -is that regardless of how many pieces there is in a torrent, the .torrent file will -always be the same size. It will only grow with the number of files (since it still -has to contain the file names). - -With regular torrents, clients have to request multiple blocks for pieces, typically -from different peers, before the data can be verified against the piece hash. The -larger the pieces are, the longer it will take to download a complete piece and verify -it. Before the piece is verified, it cannot be shared with the swarm, which means the -larger piece sizes, the slower turnaround data has when it is downloaded by peers. -Since on average the data has to sit around, waiting, in client buffers before it has -been verified and can be uploaded again. - -Another problem with large piece sizes is that it is harder for a client to pinpoint -the malicious or buggy peer when a piece fails, and it will take longer to re-download -it and take more tries before the piece succeeds the larger the pieces are. - -The piece size in regular torrents is a tradeoff between the size of the .torrent file -itself and the piece size. Often, for files that are 4 GB, the piece size is 2 or 4 MB, -just to avoid making the .torrent file too big. - -Merkle torrents solves these problems by removing the tradeoff between .torrent size and -piece size. With merkle torrents, the piece size can be the minimum block size (16 kB), -which lets peers verify every block of data received from peers, immediately. This -gives a minimum turnaround time and completely removes the problem of identifying malicious -peers. - -.. image:: merkle_tree.png - -The root hash is built by hashing all the piece hashes pair-wise, until they all collapse -down to the root. - -.. image:: storage.png - :align: right - -customizable file storage -------------------------- - -libtorrent's storage implementation is customizable. That means a special purpose bittorrent -client can replace the default way to store files on disk. - -When implementing a bittorrent cache, it doesn't matter how the data is stored on disk, as -long as it can be retrieved and seeded. In that case a new storage class can be implemented -(inheriting from the ``storage_interface`` class) that avoids the unnecessary step of mapping -slots to files and offsets. The storage can ignore the file boundaries and just store the -entire torrent in a single file (which will end up being all the files concatenated). The main -advantage of this, other than a slight cpu performance gain, is that all file operations would -be page (and sector) aligned. This enables efficient unbuffered I/O, and can potentially -lead to more efficient read caching (using the built in disk cache rather than relying on the -operating system's disk cache). - -The storage interface supports operating systems where you can ask for sparse regions -(such as Windows and Solaris). The advantage of this is that when checking files, the regions -that are known to be sparse can be skipped, which can reduce the time to check a torrent -significantly. - -easy to use API ---------------- - -One of the design goals of the libtorrent API is to make common operations simple, but still -have it possible to do complicated and advanced operations. This is best illustrated by example -code to implement a simple bittorrent client:: - - #include - #include "libtorrent/session.hpp" - - // usage a.out [torrent-file] - int main(int argc, char* argv[]) try - { - using namespace libtorrent; - - session s; - s.listen_on(std::make_pair(6881, 6889)); - add_torrent_params p; - p.save_path = "./"; - p.ti = new torrent_info(argv[1]); - s.add_torrent(p); - - // wait for the user to end - char a; - std::cin.unsetf(std::ios_base::skipws); - std::cin >> a; - return 0; - } - catch (std::exception& e) - { - std::cerr << ec.what() << std::endl; - return 1; - } - -This client doesn't give the user any status information or progress about the torrent, but -it is fully functional. - -libtorrent also comes with python bindings for easy access for python developers. - - -portability -=========== - -libtorrent runs on most major operating systems, including Windows, -MacOS X, Linux, BSD and Solaris. -It uses Boost.Thread, Boost.Filesystem, Boost.Date_time and various other -boost libraries as well as zlib_ (shipped) and asio_ (shipped). At least version -1.34.1 of boost is required. - -.. _zlib: http://www.zlib.org -.. _asio: http://asio.sf.net - -libtorrent uses asio, hence it will take full advantage of high performance -network APIs on the most popular platforms. I/O completion ports on windows, -epoll on linux and kqueue on MacOS X and BSD. - -libtorrent has been successfully compiled and tested on: - -* Windows 2000, XP and Vista vc7.1, vc8 -* Linux x86 GCC 3.3, GCC 3.4.2, 4.x -* Linux PPC GCC 4.1.1 -* MacOS X (darwin), (Apple's) GCC 3.3, (Apple's) GCC 4.0 -* SunOS 5.8 GCC 3.1 and Sunpro -* Cygwin GCC 3.3.3 - -Fails on: - -* GCC 2.95.4 -* msvc6 - - diff --git a/libtorrent_utp/docs/firetorrent.png b/libtorrent_utp/docs/firetorrent.png deleted file mode 100644 index 90b56b762bf60c204c2103de688ea4a611d0d2a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26385 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Ǻ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 X`+to*r#(^vBH`qv!BUkHpM(DoI3P6) diff --git a/libtorrent_utp/docs/flush.jpg b/libtorrent_utp/docs/flush.jpg deleted file mode 100644 index c4c34e7fc5a01edaa8d701c1bd208382a1c2b5df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9581 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 B*Sr7# diff --git a/libtorrent_utp/docs/folx.png b/libtorrent_utp/docs/folx.png deleted file mode 100644 index 521317d2376db8f56699430d06033c69e07badfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14818 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 IM6N<$g0;LVI{*Lx diff --git a/libtorrent_utp/docs/halite_thumb.png b/libtorrent_utp/docs/halite_thumb.png deleted file mode 100644 index 391150b09c258bde7a39039957762be0317e78f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20926 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 diff --git a/libtorrent_utp/docs/im_thumb.jpg b/libtorrent_utp/docs/im_thumb.jpg deleted file mode 100644 index 1dcfd75d3de9d878ca56be943a72284415e3498a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12093 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 - - - - - - - - - - - - -
-
-
- -
- -
- - - -
-

libtorrent

-

libtorrent is a feature complete C++ bittorrent implementation focusing -on efficiency and scalability. It runs on embedded devices as well as -desktops. It boasts a well documented library interface that is easy to -use. It comes with a simple bittorrent client demonstrating the use of -the library.

-

The main goals of libtorrent are:

-
    -
  • to be cpu efficient
  • -
  • to be memory efficient
  • -
  • to be very easy to use
  • -
- -
-

Feedback

-

There's a mailing list, general libtorrent discussion.

-

You can usually find me as hydri in #libtorrent on irc.freenode.net.

-
-
-

license

-

libtorrent is released under the BSD-license.

-

This means that you can use the library in your project without having to -release its source code. The only requirement is that you give credit -to the author of the library by including the libtorrent license in your -software or documentation.

-

It is however greatly appreciated if additional features are contributed -back to the open source project. Patches can be emailed to the mailing -list or posted to the bug tracker.

-
-
-

Acknowledgements

-

Written by Arvid Norberg. Copyright © 2003-2009

-

Contributions by Magnus Jonsson, Daniel Wallin and Cory Nelson

-

Thanks to Reimond Retz for bugfixes, suggestions and testing

-

Thanks to UmeĂĄ University for providing development and test hardware.

-

Project is hosted by sourceforge.

-

sf_logo

-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/index.rst b/libtorrent_utp/docs/index.rst deleted file mode 100644 index 33b12110c..000000000 --- a/libtorrent_utp/docs/index.rst +++ /dev/null @@ -1,169 +0,0 @@ -.. raw:: html - -
- -* download_ -* features_ -* contributing_ -* `building libtorrent`_ -* examples_ -* `api documentation`_ -* `create torrents`_ -* `running tests`_ -* `tuning`_ -* screenshot_ -* `mailing list`_ (archive_) -* `who's using libtorrent?`_ -* `report bugs`_ -* `sourceforge page`_ -* `wiki`_ - --------- - -Extensions - -* `uTP`_ -* `extensions protocol`_ -* `plugin interface`_ -* `DHT extensions`_ -* `UDP tracker protocol`_ -* `HTTP seed`_ -* multitracker_ - --------- - -Bindings - -* `ruby bindings`_ -* `python bindings`_ - --------- - -* `Introduction, slides`_ - -.. raw:: html - -
-
- -========== -libtorrent -========== - -.. _download: http://code.google.com/p/libtorrent/downloads/list -.. _features: features.html -.. _contributing: contributing.html -.. _`building libtorrent`: building.html -.. _examples: examples.html -.. _`api documentation`: manual.html -.. _`create torrents`: make_torrent.html -.. _`running tests`: running_tests.html -.. _`tuning`: tuning.html -.. _screenshot: client_test.png -.. _`uTP`: utp.html -.. _`extensions protocol`: extension_protocol.html -.. _`plugin interface`: libtorrent_plugins.html -.. _`DHT extensions`: dht_extensions.html -.. _`UDP tracker protocol`: udp_tracker_protocol.html -.. _`HTTP seed`: http://www.getright.com/seedtorrent.html -.. _multitracker: http://bittorrent.org/beps/bep_0012.html -.. _mailing list: http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss -.. _archive: http://dir.gmane.org/gmane.network.bit-torrent.libtorrent -.. _`who's using libtorrent?`: projects.html -.. _`report bugs`: http://code.google.com/p/libtorrent/issues/entry -.. _sourceforge page: http://www.sourceforge.net/projects/libtorrent -.. _wiki: http://code.google.com/p/libtorrent/wiki/index - -.. _`ruby bindings`: http://libtorrent-ruby.rubyforge.org/ -.. _`python bindings`: python_binding.html - -.. _`Introduction, slides`: bittorrent.pdf - -libtorrent is a feature complete C++ bittorrent implementation focusing -on efficiency and scalability. It runs on embedded devices as well as -desktops. It boasts a well documented library interface that is easy to -use. It comes with a `simple bittorrent client`__ demonstrating the use of -the library. - -__ client_test.html - -The main goals of libtorrent are: - -* to be cpu efficient -* to be memory efficient -* to be very easy to use - - -Donate -====== - -Support the development of libtorrent - -.. raw:: html - -
- - - - - - - -
- - - -Feedback -======== - -There's a `mailing list`__, general libtorrent discussion. - -__ http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss - -You can usually find me as hydri in ``#libtorrent`` on ``irc.freenode.net``. - -license -======= - -libtorrent is released under the BSD-license_. - -.. _BSD-license: http://www.opensource.org/licenses/bsd-license.php - -This means that you can use the library in your project without having to -release its source code. The only requirement is that you give credit -to the author of the library by including the libtorrent license in your -software or documentation. - -It is however greatly appreciated if additional features are contributed -back to the open source project. Patches can be emailed to the mailing -list or posted to the `bug tracker`_. - -.. _`bug tracker`: http://code.rasterbar.com/libtorrent/newticket - -Acknowledgements -================ - -Written by Arvid Norberg. Copyright |copy| 2003-2009 - -Contributions by Magnus Jonsson, Daniel Wallin and Cory Nelson - -Thanks to Reimond Retz for bugfixes, suggestions and testing - -Thanks to `UmeĂĄ University`__ for providing development and test hardware. - -__ http://www.cs.umu.se - -Project is hosted by sourceforge. - -|sf_logo|__ - -__ http://sourceforge.net - -.. |sf_logo| image:: http://sourceforge.net/sflogo.php?group_id=7994 -.. |copy| unicode:: 0xA9 .. copyright sign - -.. raw:: html - -
- diff --git a/libtorrent_utp/docs/leechcraft.png b/libtorrent_utp/docs/leechcraft.png deleted file mode 100644 index a36eb2820e4a5bc928731fe2c41cf27a9bac0e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28376 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 diff --git a/libtorrent_utp/docs/libtorrent_plugins.html b/libtorrent_utp/docs/libtorrent_plugins.html deleted file mode 100644 index ab9586ab4..000000000 --- a/libtorrent_utp/docs/libtorrent_plugins.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/libtorrent_utp/docs/libtorrent_plugins.rst b/libtorrent_utp/docs/libtorrent_plugins.rst deleted file mode 100644 index 5dc3b5e5e..000000000 --- a/libtorrent_utp/docs/libtorrent_plugins.rst +++ /dev/null @@ -1,240 +0,0 @@ -:Author: Arvid Norberg, arvid@rasterbar.com - -libtorrent plugins -================== - -.. contents:: - -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. - -.. _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 main 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, use an internal queue, and do the actual sending in ``tick()``. - -.. _`boost thread's mutex`: http://www.boost.org/doc/html/mutex.html - - -plugin interface -================ - -The plugin interface consists of two base classes that the plugin may -implement. These are called ``torrent_plugin`` and ``peer_plugin``. They are -both found in the ```` header. - -These plugins are instantiated for each torrent and possibly each peer, -respectively. - -This is done by passing in a 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. - - -torrent_plugin -============== - -The synopsis for ``torrent_plugin`` follows:: - - struct torrent_plugin - { - virtual ~torrent_plugin(); - virtual boost::shared_ptr new_connection(peer_connection*); - - virtual void on_piece_pass(int index); - virtual void on_piece_failed(int index); - - virtual void tick(); - - virtual bool on_pause(); - virtual bool on_resume(); - - virtual void on_files_checked(); - }; - -This is the base class for a torrent_plugin. Your derived class is (if added -as an extension) instantiated for each torrent in the session. The callback -hook functions are defined as follows. - - -new_connection() ----------------- - -:: - - boost::shared_ptr new_connection(peer_connection*); - -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. - -on_piece_pass() on_piece_fail() -------------------------------- - -:: - - void on_piece_pass(int index); - void on_piece_failed(int index); - -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``. - -tick() ------- - -:: - - void tick(); - -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. - - -on_pause() on_resume() ----------------------- - -:: - - bool on_pause(); - bool on_resume(); - -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. - -on_files_checked() ------------------- - -:: - - void on_files_checked(); - -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. - - -peer_plugin -=========== - -:: - - struct peer_plugin - { - virtual ~peer_plugin(); - - virtual void add_handshake(entry&); - virtual bool on_handshake(char const* reserved_bits); - virtual bool on_extension_handshake(lazy_entry const& h); - - virtual bool on_choke(); - virtual bool on_unchoke(); - virtual bool on_interested(); - virtual bool on_not_interested(); - virtual bool on_have(int index); - virtual bool on_bitfield(bitfield const& bits); - virtual bool on_have_all(); - virtual bool on_have_none(); - virtual bool on_allowed_fast(int index); - virtual bool on_request(peer_request const& req); - virtual bool on_piece(peer_request const& piece, disk_buffer_holder& buffer); - virtual bool on_cancel(peer_request const& req); - virtual bool on_reject(peer_request const& req); - virtual bool on_suggest(int index); - virtual bool on_extended(int length - , int msg, buffer::const_interval body); - virtual bool on_unknown_message(int length, int msg - , buffer::const_interval body); - virtual void on_piece_pass(int index); - virtual void on_piece_failed(int index); - - virtual void tick(); - - virtual bool write_request(peer_request const& r); - }; - -disk_buffer_holder -================== - -:: - - struct disk_buffer_holder - { - disk_buffer_holder(aux::session_impl& s, char* b); - ~disk_buffer_holder(); - char* release(); - char* buffer(); - }; - -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. - diff --git a/libtorrent_utp/docs/libtorrent_screen.png b/libtorrent_utp/docs/libtorrent_screen.png deleted file mode 100644 index 19d98526196d7256a7d496e772a0152bfd3bf1ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53067 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 diff --git a/libtorrent_utp/docs/limewire.png b/libtorrent_utp/docs/limewire.png deleted file mode 100644 index a45d37fc9714261314389526f0a296c3bfe7599e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20970 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 - - - - - -creating torrents - - - - - - - -
-
-
- -
- -
-

creating torrents

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
- -
-

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. -
  3. the torrent properties are set, such as tracker url, web seeds, -DHT nodes etc.
  4. -
  5. Read through all the files in the torrent, SHA-1 all the data -and set the piece hashes.
  6. -
  7. The torrent is bencoded into a file or buffer.
  8. -
-

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.

-
-
-

high level example

-
-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<char>(out), t.generate());
-
-
-
-

add_files

-
-
-template <class Pred>
-void add_files(file_storage& fs, boost::filesystem::path const& path, Pred p
-        , boost::uint32_t flags = 0);
-template <class Pred>
-void add_files(file_storage& fs, boost::filesystem::wpath const& path, Pred p
-        , boost::uint32_t flags = 0);
-
-void add_files(file_storage& fs, boost::filesystem::path const& path
-        , boost::uint32_t flags = 0);
-void add_files(file_storage& fs, boost::filesystem::wpath const& path
-        , boost::uint32_t flags = 0);
-
-
-

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(boost::filesystem::path const& p);
-
-

and for the wpath version:

-
-bool Pred(boost::filesystem::wpath 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.

-
-
-

set_piece_hashes()

-
-
-template <class Fun>
-void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f);
-template <class Fun>
-void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f);
-template <class Fun>
-void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f
-        , error_code& ec);
-template <class Fun>
-void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f
-        , error_code& ec);
-
-void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p);
-void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p);
-void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p
-        , error_code& ec);
-void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p
-        , error_code& ec);
-
-
-

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.

-
-
-

file_storage

-

The file_storage class represents a file list and the piece -size. Everything necessary to interpret a regular bittorrent storage -file structure. Its synopsis:

-
-class file_storage
-{
-public:
-
-        bool is_valid() const;
-
-        enum flags_t
-        {
-                pad_file = 1,
-                attribute_hidden = 2,
-                attribute_executable = 4
-        };
-
-        void add_file(file_entry const& e);
-        void add_file(fs::path const& p, size_type size, int flags = 0);
-        void add_file(fs::wpath const& p, size_type size, int flags = 0);
-        void rename_file(int index, std::string const& new_filename);
-        void rename_file(int index, std::wstring const& new_filename);
-
-        std::vector<file_slice> map_block(int piece, size_type offset
-                , int size) const;
-        peer_request map_file(int file, size_type offset, int size) const;
-
-        typedef std::vector<file_entry>::const_iterator iterator;
-        typedef std::vector<file_entry>::const_reverse_iterator reverse_iterator;
-
-        iterator begin() const;
-        iterator end() const;
-        reverse_iterator rbegin();
-        reverse_iterator rend() const;
-        int num_files() const;
-
-        file_entry const& at(int index) const;
-
-        size_type total_size() const;
-        void set_num_pieces(int n);
-        int num_pieces() const;
-        void set_piece_length(int l);
-        int piece_length() const;
-        int piece_size(int index) const;
-
-        void set_name(std::string const& n);
-        void set_name(std::wstring const& n);
-        const std::string& name() const;
-
-        void swap(file_storage& ti);
-}
-
-
-

add_file()

-
-
-void add_file(file_entry const& e);
-void add_file(fs::path const& p, size_type size, int flags = 0);
-void add_file(fs::wpath const& p, size_type size, int flags = 0);
-
-
-

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. -The possible arreibutes are:

-
-pad_file
-attribute_hidden
-attribute_executable
-
-
-
-

add_file

-
-
-void add_file(file_entry const& e);
-void add_file(fs::path const& p, size_type size);
-
-
-

Adds a file to the file storage. 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 built in functions to traverse a directory to add files will -make sure this requirement is fulfilled.

-
-
-
-

create_torrent

-

The create_torrent class has the following synopsis:

-
-struct create_torrent
-{
-        enum {
-                optimize = 1
-                , merkle = 2
-                , modification_time = 4
-                , symlink = 8
-                , calculate_file_hashes = 16
-        };
-        create_torrent(file_storage& fs, int piece_size = 0, int pad_size_limit = -1
-                , int flags = optimize);
-        create_torrent(torrent_info const& ti);
-
-        entry generate() const;
-
-        file_storage const& files() const;
-
-        void set_comment(char const* str);
-        void set_creator(char const* str);
-        void set_hash(int index, sha1_hash const& h);
-        void set_file_hash(int index, sha1_hash const& h);
-        void add_url_seed(std::string const& url);
-        void add_http_seed(std::string const& url);
-        void add_node(std::pair<std::string, int> const& node);
-        void add_tracker(std::string const& url, int tier = 0);
-        void set_priv(bool p);
-
-        int num_pieces() const;
-        int piece_length() const;
-        int piece_size(int i) const;
-        bool priv() const;
-};
-
-
-

create_torrent()

-
-
-enum {
-        optimize = 1
-        , merkle = 2
-        , modification_time = 4
-        , symlink = 8
-        , calculate_file_hashes = 16
-};
-create_torrent(file_storage& fs, int piece_size = 0, int pad_size_limit = -1
-        , int flags = optimize);
-create_torrent(torrent_info const& ti);
-
-
-

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.

-

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 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 following flags:

-
-
optimize
-
This will insert pad files to align the files to piece boundaries, for -optimized disk-I/O.
-
merkle
-
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.
-
modification_time
-
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.
-
symlink
-
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.
-
calculate_file_hashes
-
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.
-
-
-
-

generate()

-
-
-entry generate() const;
-
-
-

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:

-
-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.

-
-
-

set_comment()

-
-
-void set_comment(char const* str);
-
-
-

Sets the comment for the torrent. The string str should be utf-8 encoded. -The comment in a torrent file is optional.

-
-
-

set_creator()

-
-
-void set_creator(char const* str);
-
-
-

Sets the creator of the torrent. The string str should be utf-8 encoded. -This is optional.

-
-
-

set_hash()

-
-
-void set_hash(int index, sha1_hash const& h);
-
-
-

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().

-
-
-

set_file_hash()

-
-
-void set_file_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.

-
-
-

add_url_seed() add_http_seed()

-
-
-void add_url_seed(std::string const& url);
-void add_http_seed(std::string const& url);
-
-
-

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.

-
-
-

add_node()

-
-
-void add_node(std::pair<std::string, int> const& node);
-
-
-

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.

-
-
-

add_tracker()

-
-
-void add_tracker(std::string const& url, int tier = 0);
-
-
-

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.

-
-
-

set_priv() priv()

-
-
-void set_priv(bool p);
-bool priv() const;
-
-
-

Sets and queries the private flag of the torrent.

-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/make_torrent.rst b/libtorrent_utp/docs/make_torrent.rst deleted file mode 100644 index 9ff28d8c5..000000000 --- a/libtorrent_utp/docs/make_torrent.rst +++ /dev/null @@ -1,498 +0,0 @@ -================= -creating torrents -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -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``. - -high level example -================== - -:: - - 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()); - -add_files -========= - - :: - - template - void add_files(file_storage& fs, boost::filesystem::path const& path, Pred p - , boost::uint32_t flags = 0); - template - void add_files(file_storage& fs, boost::filesystem::wpath const& path, Pred p - , boost::uint32_t flags = 0); - - void add_files(file_storage& fs, boost::filesystem::path const& path - , boost::uint32_t flags = 0); - void add_files(file_storage& fs, boost::filesystem::wpath const& path - , boost::uint32_t flags = 0); - -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(boost::filesystem::path const& p); - -and for the wpath version:: - - bool Pred(boost::filesystem::wpath 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. - -set_piece_hashes() -================== - - :: - - template - void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f); - template - void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f); - template - void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f - , error_code& ec); - template - void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f - , error_code& ec); - - void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p); - void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p); - void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p - , error_code& ec); - void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p - , error_code& ec); - -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. - -file_storage -============ - -The ``file_storage`` class represents a file list and the piece -size. Everything necessary to interpret a regular bittorrent storage -file structure. Its synopsis:: - - class file_storage - { - public: - - bool is_valid() const; - - enum flags_t - { - pad_file = 1, - attribute_hidden = 2, - attribute_executable = 4 - }; - - void add_file(file_entry const& e); - void add_file(fs::path const& p, size_type size, int flags = 0); - void add_file(fs::wpath const& p, size_type size, int flags = 0); - void rename_file(int index, std::string const& new_filename); - void rename_file(int index, std::wstring const& new_filename); - - std::vector map_block(int piece, size_type offset - , int size) const; - peer_request map_file(int file, size_type offset, int size) const; - - typedef std::vector::const_iterator iterator; - typedef std::vector::const_reverse_iterator reverse_iterator; - - iterator begin() const; - iterator end() const; - reverse_iterator rbegin(); - reverse_iterator rend() const; - int num_files() const; - - file_entry const& at(int index) const; - - size_type total_size() const; - void set_num_pieces(int n); - int num_pieces() const; - void set_piece_length(int l); - int piece_length() const; - int piece_size(int index) const; - - sha1_hash const& hash(file_entry const& fe) const; - std::string const& symlink(file_entry const& fe) const; - time_t mtime(file_entry const& fe) const; - int file_index(file_entry const& fe) const; - size_type file_base(file_entry const& fe) const; - void set_file_base(file_entry const& fe, size_type off); - - void set_name(std::string const& n); - void set_name(std::wstring const& n); - const std::string& name() const; - - void swap(file_storage& ti); - } - -add_file() ----------- - - :: - - void add_file(file_entry const& e); - void add_file(fs::path const& p, size_type size, int flags = 0); - void add_file(fs::wpath const& p, size_type size, int flags = 0); - -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. -The possible arreibutes are:: - - pad_file - attribute_hidden - attribute_executable - -add_file --------- - - :: - - void add_file(file_entry const& e); - void add_file(fs::path const& p, size_type size); - -Adds a file to the file storage. 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 built in functions to traverse a directory to add files will -make sure this requirement is fulfilled. - -hash() symlink() mtime() file_index() -------------------------------------- - - :: - - sha1_hash hash(file_entry const& fe) const; - std::string const& symlink(file_entry const& fe) const; - time_t mtime(file_entry const& fe) const; - int file_index(file_entry const& fe) const; - -These functions are used to query the symlink, file hash, -modification time and the file-index from a ``file_entry``. - -For these functions to function, the file entry must be an -actual object from this same ``file_storage`` object. It may -not be a copy. - -The file 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 modification time is the posix time when a file was last -modified when the torrent was created, or 0 if it was not provided. - -The file index of a file is simply a 0 based index of the -file as they are ordered in the torrent file. - -file_base() set_file_base() ---------------------------- - - :: - - size_type file_base(file_entry const& fe) const; - void set_file_base(file_entry const& fe, size_type off); - -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. - -create_torrent -============== - -The ``create_torrent`` class has the following synopsis:: - - - struct create_torrent - { - enum { - optimize = 1 - , merkle = 2 - , modification_time = 4 - , symlink = 8 - , calculate_file_hashes = 16 - }; - create_torrent(file_storage& fs, int piece_size = 0, int pad_size_limit = -1 - , int flags = optimize); - create_torrent(torrent_info const& ti); - - entry generate() const; - - file_storage const& files() const; - - void set_comment(char const* str); - void set_creator(char const* str); - void set_hash(int index, sha1_hash const& h); - void set_file_hash(int index, sha1_hash const& h); - void add_url_seed(std::string const& url); - void add_http_seed(std::string const& url); - void add_node(std::pair const& node); - void add_tracker(std::string const& url, int tier = 0); - void set_priv(bool p); - - int num_pieces() const; - int piece_length() const; - int piece_size(int i) const; - bool priv() const; - }; - -create_torrent() ----------------- - - :: - - enum { - optimize = 1 - , merkle = 2 - , modification_time = 4 - , symlink = 8 - , calculate_file_hashes = 16 - }; - create_torrent(file_storage& fs, int piece_size = 0, int pad_size_limit = -1 - , int flags = optimize); - create_torrent(torrent_info const& ti); - -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. - -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 ``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 following flags: - -optimize - This will insert pad files to align the files to piece boundaries, for - optimized disk-I/O. - -merkle - 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. - -modification_time - 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. - -symlink - 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. - -calculate_file_hashes - 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. - -generate() ----------- - - :: - - entry generate() const; - -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:: - - 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. - -set_comment() -------------- - - :: - - void set_comment(char const* str); - -Sets the comment for the torrent. The string ``str`` should be utf-8 encoded. -The comment in a torrent file is optional. - -set_creator() -------------- - - :: - - void set_creator(char const* str); - -Sets the creator of the torrent. The string ``str`` should be utf-8 encoded. -This is optional. - -set_hash() ----------- - - :: - - void set_hash(int index, sha1_hash const& h); - -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()`_. - -set_file_hash() ---------------- - - :: - - void set_file_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. - -add_url_seed() add_http_seed() ------------------------------- - - :: - - void add_url_seed(std::string const& url); - void add_http_seed(std::string const& url); - -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. - -add_node() ----------- - - :: - - void add_node(std::pair const& node); - -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. - -add_tracker() -------------- - - :: - - void add_tracker(std::string const& url, int tier = 0); - -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. - -set_priv() priv() ------------------ - - :: - - void set_priv(bool p); - bool priv() const; - -Sets and queries the private flag of the torrent. - diff --git a/libtorrent_utp/docs/makefile b/libtorrent_utp/docs/makefile deleted file mode 100644 index 65f68313e..000000000 --- a/libtorrent_utp/docs/makefile +++ /dev/null @@ -1,44 +0,0 @@ -# this makefile assumes that you have docutils and rst2pdf installed - -WEB_PATH = ~/Documents/rasterbar/web/products/libtorrent - -TARGETS = index \ - udp_tracker_protocol \ - client_test \ - manual \ - building \ - features \ - contributing\ - examples \ - extension_protocol \ - make_torrent \ - dht_extensions \ - libtorrent_plugins \ - python_binding \ - projects \ - running_tests \ - utp \ - tuning - -FIGURES = read_disk_buffers write_disk_buffers - -html: $(TARGETS:=.html) $(FIGURES:=.png) - -pdf: $(TARGETS:=.pdf) $(FIGURES:=.eps) - -all: html - -%.pdf:%.rst - rst2pdf $? -o $@ --stylesheets stylesheet - -%.html:%.rst - rst2html.py --template=template.txt --stylesheet-path=style.css --link-stylesheet --no-toc-backlinks $? > $@ - cp $@ $(WEB_PATH)/$@ - -%.eps:%.dot - dot -Teps $? >$@ - cp $@ $(WEB_PATH)/$@ - -clean: - rm -f $(TARGETS:=.html) $(TARGETS:=.pdf) - diff --git a/libtorrent_utp/docs/manual.html b/libtorrent_utp/docs/manual.html deleted file mode 100644 index 79bce1d05..000000000 --- a/libtorrent_utp/docs/manual.html +++ /dev/null @@ -1,7862 +0,0 @@ - - - - - - -libtorrent API Documentation - - - - - - - -
-
-
- -
- -
-

libtorrent API Documentation

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
-
-

Table of contents

- -
-
-

overview

-

The interface of libtorrent consists of a few classes. The main class is -the session, it contains the main loop that serves all torrents.

-

The basic usage is as follows:

- -

Each class and function is described in this manual.

-

For a description on how to create torrent files, see make_torrent.

-
-
-

things to keep in mind

-

A common problem developers are facing is torrents stopping without explanation. -Here is a description on which conditions libtorrent will stop your torrents, -how to find out about it and what to do about it.

-

Make sure to keep track of the paused state, the error state and the upload -mode of your torrents. By default, torrents are auto-managed, which means -libtorrent will pause them, unpause them, scrape them and take them out -of upload-mode automatically.

-

Whenever a torrent encounters a fatal error, it will be stopped, and the -torrent_status::error will describe the error that caused it. If a torrent -is auto managed, it is scraped periodically and paused or resumed based on -the number of downloaders per seed. This will effectively seed torrents that -are in the greatest need of seeds.

-

If a torrent hits a disk write error, it will be put into upload mode. This -means it will not download anything, but only upload. The assumption is that -the write error is caused by a full disk or write permission errors. If the -torrent is auto-managed, it will periodically be taken out of the upload -mode, trying to write things to the disk again. This means torrent will recover -from certain disk errors if the problem is resolved. If the torrent is not -auto managed, you have to call set_upload_mode() to turn -downloading back on again.

-
-
-

network primitives

-

There are a few typedefs in the libtorrent namespace which pulls -in network types from the asio namespace. These are:

-
-typedef asio::ip::address address;
-typedef asio::ip::address_v4 address_v4;
-typedef asio::ip::address_v6 address_v6;
-using asio::ip::tcp;
-using asio::ip::udp;
-
-

These are declared in the <libtorrent/socket.hpp> header.

-

The using statements will give easy access to:

-
-tcp::endpoint
-udp::endpoint
-
-

Which are the endpoint types used in libtorrent. An endpoint is an address -with an associated port.

-

For documentation on these types, please refer to the asio documentation.

-
-
-

session

-

The session class has the following synopsis:

-
-class session: public boost::noncopyable
-{
-
-        session(fingerprint const& print
-                = libtorrent::fingerprint(
-                "LT", 0, 1, 0, 0)
-                , int flags = start_default_features
-                        | add_default_plugins
-                , int alert_mask = alert::error_notification);
-
-        session(
-                fingerprint const& print
-                , std::pair<int, int> listen_port_range
-                , char const* listen_interface = 0
-                , int flags = start_default_features
-                        | add_default_plugins
-                , int alert_mask = alert::error_notification);
-
-        enum save_state_flags_t
-        {
-                save_settings = 0x001,
-                save_dht_settings = 0x002,
-                save_dht_state = 0x004,
-                save_proxy = 0x008,
-                save_i2p_proxy = 0x010,
-                save_encryption_settings = 0x020,
-                save_as_map = 0x040,
-        };
-
-        void load_state(lazy_entry const& e);
-        void save_state(entry& e, boost::uint32_t flags) const;
-
-        torrent_handle add_torrent(
-                add_torrent_params const& params);
-        torrent_handle add_torrent(
-                add_torrent_params const& params
-                , error_code& ec);
-
-        void pause();
-        void resume();
-
-        session_proxy abort();
-
-        enum options_t
-        {
-                none = 0,
-                delete_files = 1
-        };
-
-        enum session_flags_t
-        {
-                add_default_plugins = 1,
-                start_default_features = 2
-        };
-
-        void remove_torrent(torrent_handle const& h
-                , int options = none);
-        torrent_handle find_torrent(sha_hash const& ih);
-        std::vector<torrent_handle> get_torrents() const;
-
-        void set_settings(session_settings const& settings);
-        void set_pe_settings(pe_settings const& settings);
-
-        void set_upload_rate_limit(int bytes_per_second);
-        int upload_rate_limit() const;
-        void set_download_rate_limit(int bytes_per_second);
-        int download_rate_limit() const;
-
-        void set_local_upload_rate_limit(int bytes_per_second);
-        int local_upload_rate_limit() const;
-        void set_local_download_rate_limit(int bytes_per_second);
-        int local_download_rate_limit() const;
-
-        void set_max_uploads(int limit);
-        void set_max_connections(int limit);
-        int max_connections() const;
-        void set_max_half_open_connections(int limit);
-        int max_half_open_connections() const;
-
-        void set_proxy(proxy_settings const& s);
-        proxy_settings proxy() const;
-
-        int num_uploads() const;
-        int num_connections() const;
-
-        void load_asnum_db(char const* file);
-        void load_asnum_db(wchar_t const* file);
-        void load_country_db(char const* file);
-        void load_country_db(wchar_t const* file);
-        int as_for_ip(address const& adr);
-
-        void set_ip_filter(ip_filter const& f);
-        ip_filter get_ip_filter() const;
-
-        session_status status() const;
-        cache_status get_cache_status() const;
-
-        bool is_listening() const;
-        unsigned short listen_port() const;
-
-        enum { listen_reuse_address = 1 };
-        bool listen_on(
-                std::pair<int, int> const& port_range
-                , char const* interface = 0
-                , int flags = 0);
-
-        std::auto_ptr<alert> pop_alert();
-        alert const* wait_for_alert(time_duration max_wait);
-        void set_alert_mask(int m);
-        size_t set_alert_queue_size_limit(
-                size_t queue_size_limit_);
-
-        void add_extension(boost::function<
-                boost::shared_ptr<torrent_plugin>(torrent*)> ext);
-
-        void start_dht();
-        void stop_dht();
-        void set_dht_settings(
-                dht_settings const& settings);
-        entry dht_state() const;
-        void add_dht_node(std::pair<std::string
-                , int> const& node);
-        void add_dht_router(std::pair<std::string
-                , int> const& node);
-        bool is_dht_running() const;
-
-        void start_lsd();
-        void stop_lsd();
-
-        upnp* start_upnp();
-        void stop_upnp();
-
-        natpmp* start_natpmp();
-        void stop_natpmp();
-};
-
-

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.

-
-

session()

-
-
-session(fingerprint const& print
-        = libtorrent::fingerprint("LT", 0, 1, 0, 0)
-        , int flags = start_default_features
-                | add_default_plugins
-        , int alert_mask = alert::error_notification);
-
-session(fingerprint const& print
-        , std::pair<int, int> listen_port_range
-        , char const* listen_interface = 0
-        , int flags = start_default_features
-                | add_default_plugins
-        , int alert_mask = alert::error_notification);
-
-
-

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()

-

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().

-
-
-

load_state() save_state()

-
-
-void load_state(lazy_entry const& e);
-void save_state(entry& e, boost::uint32_t flags) const;
-
-
-

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). These are the possible flags. A flag that's set, means those settings are saved:

-
-enum save_state_flags_t
-{
-        save_settings =     0x001,
-        save_dht_settings = 0x002,
-        save_dht_state =    0x004,
-        save_proxy =        0x008,
-        save_i2p_proxy =    0x010,
-        save_encryption_settings = 0x020,
-        save_as_map =       0x040,
-};
-
-
-
-

pause() resume() is_paused()

-
-
-void pause();
-void resume();
-bool is_paused() const;
-
-
-

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.

-
-
-

abort()

-
-
-session_proxy abort();
-
-
-

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()
-};
-
-
-
-

add_torrent()

-
-
-typedef storage_interface* (&storage_constructor_type)(
-        file_storage const&, file_storage const*, fs::path const&, file_pool&
-        , std::vector<boost::uint8_t> const&);
-
-struct add_torrent_params
-{
-        add_torrent_params(storage_constructor_type s);
-
-        int version;
-        boost::intrusive_ptr<torrent_info> ti;
-        char const* tracker_url;
-        sha1_hash info_hash;
-        char const* name;
-        fs::path save_path;
-        std::vector<char>* resume_data;
-        storage_mode_t storage_mode;
-        bool paused;
-        bool auto_managed;
-        bool duplicate_is_error;
-        storage_constructor_type storage;
-        void* userdata;
-        bool seed_mode;
-        bool override_resume_data;
-        bool upload_mode;
-        std::vector<boost::uint8_t> const* file_priorities;
-        bool share_mode;
-};
-
-torrent_handle add_torrent(add_torrent_params const& params);
-torrent_handle add_torrent(add_torrent_params const& params
-        , error_code& ec);
-
-
-

You add torrents through the add_torrent() function where you give an -object with all the parameters.

-

The overload that does not take an error_code throws an exception on -error and is not available when building without exception support.

-

The only mandatory parameter is save_path which is the directory where you -want the files to be saved. You also need to specify either the ti (the -torrent file) or info_hash (the info hash of the torrent). If you 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 0 in case no -name should be assigned to the torrent. In case it's not 0, the name is used for -the torrent as long as it doesn't have metadata. See torrent_handle::name.

-

If the torrent doesn't have a tracker, but relies on the DHT to find peers, the -tracker_url can be 0, otherwise you might specify a tracker url that tracks this -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.

-

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().

-

The storage_mode parameter refers to the layout of the storage for this torrent. -There are 3 different modes:

-
-
storage_mode_sparse
-
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_allocate
-
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_compact
-
The storage will grow as more pieces are downloaded, and pieces -are rearranged to finally be in their correct places once the entire torrent has been -downloaded.
-
-

For more information, see storage allocation.

-

paused is a boolean that 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.

-

If you pass in resume data, the paused state of the torrent when the resume data -was saved will override the paused state you pass in here. You can override this -by setting override_resume_data.

-

If auto_managed is true, this 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.

-

storage 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.

-

The userdata parameter is optional and will be passed on to the extension -constructor functions, if any (see add_extension()).

-

If seed_mode is set to true, 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 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.

-

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 override_resume_data is set to true, the paused and auto_managed -state of the torrent are not loaded from the resume data, but the states requested -by this add_torrent_params will override it.

-

If upload_mode is set to true, 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.

-

share_mode 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 -merlely 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.

-

file_priorities can be set to control the initial file priorities when adding -a torrent. The semantics are the same as for torrent_handle::prioritize_files().

-

version is filled in by the constructor and should be left untouched. It -is used for forward binary compatibility.

-
-
-

remove_torrent()

-
-
-void remove_torrent(torrent_handle const& h, int options = none);
-
-
-

remove_torrent() will close all peer connections associated with the torrent and tell -the tracker that we've stopped participating in the swarm. The optional second argument -options can be used to delete all the files downloaded by this torrent. To do this, 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.

-
-
-

find_torrent() get_torrents()

-
-
-torrent_handle find_torrent(sha_hash const& ih);
-std::vector<torrent_handle> get_torrents() const;
-
-
-

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.

-
-
-

load_asnum_db() load_country_db() as_for_ip()

-
-
-void load_asnum_db(char const* file);
-void load_asnum_db(wchar_t const* file);
-void load_country_db(char const* file);
-void load_country_db(wchar_t const* file);
-int as_for_ip(address const& adr);
-
-
-

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.

-

The wchar_t overloads are for wide character paths.

-
-
-

set_ip_filter()

-
-
-void set_ip_filter(ip_filter const& filter);
-
-
-

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()

-
-
-ip_filter get_ip_filter() const;
-
-
-

Returns the ip_filter currently in the session. See ip_filter.

-
-
-

status()

-
-
-session_status status() const;
-
-
-

status() returns session wide-statistics and status. The session_status -struct has the following members:

-
-struct dht_lookup
-{
-        char const* type;
-        int outstanding_requests;
-        int timeouts;
-        int responses;
-        int branch_factor;
-};
-
-struct utp_status
-{
-        int num_idle;
-        int num_syn_sent;
-        int num_connected;
-        int num_fin_sent;
-        int num_close_wait;
-};
-
-struct session_status
-{
-        bool has_incoming_connections;
-
-        int upload_rate;
-        int download_rate;
-        size_type total_download;
-        size_type total_upload;
-
-        int payload_upload_rate;
-        int payload_download_rate;
-        size_type total_payload_download;
-        size_type total_payload_upload;
-
-        int ip_overhead_upload_rate;
-        int ip_overhead_download_rate;
-        size_type total_ip_overhead_download;
-        size_type total_ip_overhead_upload;
-
-        int dht_upload_rate;
-        int dht_download_rate;
-        size_type total_dht_download;
-        size_type total_dht_upload;
-
-        int tracker_upload_rate;
-        int tracker_download_rate;
-        size_type total_tracker_download;
-        size_type total_tracker_upload;
-
-        size_type total_redundant_bytes;
-        size_type total_failed_bytes;
-
-        int num_peers;
-        int num_unchoked;
-        int allowed_upload_slots;
-
-        int optimistic_unchoke_counter;
-        int unchoke_counter;
-
-        int dht_nodes;
-        int dht_node_cache;
-        int dht_torrents;
-        size_type dht_global_nodes;
-        std::vector<dht_lookup> active_requests;
-        int dht_total_allocations;
-
-        utp_status utp_stats;
-};
-
-

has_incoming_connections is 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.

-

upload_rate, download_rate are the total download and upload rates accumulated -from all torrents. This includes bittorrent protocol, DHT and an estimated TCP/IP -protocol overhead.

-

total_download and total_upload are the total number of bytes downloaded and -uploaded to and from all torrents. This also includes all the protocol overhead.

-

payload_download_rate and payload_upload_rate is the rate of the payload -down- and upload only.

-

total_payload_download and total_payload_upload is 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.

-

ip_overhead_upload_rate, ip_overhead_download_rate, total_ip_overhead_download -and total_ip_overhead_upload is the estimated TCP/IP overhead in each direction.

-

dht_upload_rate, dht_download_rate, total_dht_download and total_dht_upload -is the DHT bandwidth usage.

-

total_redundant_bytes is 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.

-

total_failed_bytes is the number of bytes that was downloaded which later failed -the hash-check.

-

num_peers is 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.

-

num_unchoked is the current number of unchoked peers. -allowed_upload_slots is the current allowed number of unchoked peers.

-

optimistic_unchoke_counter and unchoke_counter 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.

-

dht_nodes, dht_node_cache and dht_torrents are 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.

-

dht_torrents are the number of torrents tracked by the DHT at the moment.

-

dht_global_nodes is an estimation of the total number of nodes in the DHT -network.

-

active_requests is a vector of the currently running DHT lookups.

-

dht_total_allocations is the number of nodes allocated dynamically for a -particular DHT lookup. This represents roughly the amount of memory used -by the DHT.

-

utp_stats contains statistics on the uTP sockets.

-
-
-

get_cache_status()

-
-
-cache_status get_cache_status() const;
-
-
-

Returns status of the disk cache for this session.

-
-
-struct cache_status
-{
-        size_type blocks_written;
-        size_type writes;
-        size_type blocks_read;
-        size_type blocks_read_hit;
-        size_type reads;
-        int cache_size;
-        int read_cache_size;
-        int total_used_buffers;
-        int average_queue_time;
-        int average_read_time;
-        int job_queue_length;
-};
-
-
-

blocks_written is the total number of 16 KiB blocks written to disk -since this session was started.

-

writes is 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.

-

blocks_read is the number of blocks that were requested from the -bittorrent engine (from peers), that were served from disk or cache.

-

blocks_read_hit is the number of blocks that were served from cache.

-

The ratio blocks_read_hit / blocks_read is the cache hit ratio -for the read cache.

-

cache_size is the number of 16 KiB blocks currently in the disk cache. -This includes both read and write cache.

-

read_cache_size is the number of 16KiB blocks in the read cache.

-

total_used_buffers is 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.

-

average_queue_time is the number of microseconds an average disk I/O job -has to wait in the job queue before it get processed.

-

average_read_time is the number of microseconds a read job takes to -wait in the queue and complete, in microseconds. This only includes -cache misses.

-

job_queue_length is the number of jobs in the job queue.

-
-
-

get_cache_info()

-
-
-void get_cache_info(sha1_hash const& ih
-        , std::vector<cached_piece_info>& ret) const;
-
-
-

get_cache_info() 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).

-
-
-struct cached_piece_info
-{
-        int piece;
-        std::vector<bool> blocks;
-        ptime last_use;
-        enum kind_t { read_cache = 0, write_cache = 1 };
-        kind_t kind;
-};
-
-
-

piece is the piece index for this cache entry.

-

blocks has 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.

-

last_use is 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.

-

kind specifies if this piece is part of the read cache or the write cache.

-
-
-

is_listening() listen_port() listen_on()

-
-
-bool is_listening() const;
-unsigned short listen_port() const;
-bool listen_on(
-        std::pair<int, int> const& port_range
-        , char const* interface = 0
-        , int flags = 0);
-
-
-

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 true -if it managed to open the socket, and false if it failed. 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.

-

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).

-
-
-

set_alert_mask()

-
-
-void set_alert_mask(int m);
-
-
-

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 alerts for mor information on the alert categories.

-
-
-

pop_alert() wait_for_alert() set_alert_queue_size_limit()

-
-
-std::auto_ptr<alert> pop_alert();
-alert const* wait_for_alert(time_duration max_wait);
-size_t set_alert_queue_size_limit(size_t queue_size_limit_);
-
-
-

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.

-

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.

-

In the python binding, wait_for_alert takes the number of milliseconds to wait as an integer.

-

set_alert_queue_size_limit() you can specify how many alerts can be awaiting for dispatching. -If this limit is reached, new incoming alerts can not be received until alerts are popped -by calling pop_alert. Default value is 1000.

-

save_resume_data_alert and save_resume_data_failed_alert are always posted, regardelss -of the alert mask.

-
-
-

add_extension()

-
-
-void add_extension(boost::function<
-        boost::shared_ptr<torrent_plugin>(torrent*, void*)> ext);
-
-
-

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<torrent_plugin>. 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 <libtorrent/extensions/metadata_transfer.hpp>
-ses.add_extension(&libtorrent::create_metadata_plugin);
-
-
-
uTorrent metadata
-
Same as metadata extension but compatible with uTorrent.
-
-
-#include <libtorrent/extensions/ut_metadata.hpp>
-ses.add_extension(&libtorrent::create_ut_metadata_plugin);
-
-
-
uTorrent peer exchange
-
Exchanges peers between clients.
-
-
-#include <libtorrent/extensions/ut_pex.hpp>
-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 <libtorrent/extensions/smart_ban.hpp>
-ses.add_extension(&libtorrent::create_smart_ban_plugin);
-
-
-
-

set_settings() set_pe_settings()

-
-
-void set_settings(session_settings const& settings);
-void set_pe_settings(pe_settings const& settings);
-
-
-

Sets the session settings and the packet encryption settings respectively. -See session_settings and pe_settings for more information on available -options.

-
-
-

set_proxy() proxy()

-
-
-void set_proxy(proxy_settings const& s);
-proxy_setting proxy() 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.

-
-
-

set_i2p_proxy() i2p_proxy()

-
-
-void set_i2p_proxy(proxy_settings const&);
-proxy_settings const& i2p_proxy();
-
-
-

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.

-
-
-

start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running()

-
-
-void start_dht(entry const& startup_state);
-void stop_dht();
-void set_dht_settings(dht_settings const& settings);
-entry dht_state() const;
-bool is_dht_running() const;
-
-
-

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. The -struct has the following members:

-
-struct dht_settings
-{
-        int max_peers_reply;
-        int search_branching;
-        int max_fail_count;
-};
-
-

max_peers_reply is the maximum number of peers the node will send in -response to a get_peers message from another node.

-

search_branching is 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.

-

max_fail_count is 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.

-

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.

-

is_dht_running() returns true if the DHT support has been started and false -otherwise.

-
-
-

add_dht_node() add_dht_router()

-
-
-void add_dht_node(std::pair<std::string, int> const& node);
-void add_dht_router(std::pair<std::string, int> const& node);
-
-
-

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.

-
-
-

start_lsd() stop_lsd()

-
-
-void start_lsd();
-void stop_lsd();
-
-
-

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.

-
-
-

start_upnp() stop_upnp()

-
-
-upnp* start_upnp();
-void stop_upnp();
-
-
-

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.

-
-
-

start_natpmp() stop_natpmp()

-
-
-natpmp* start_natpmp();
-void stop_natpmp();
-
-
-

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.

-
-
-
-

entry

-

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. This is its synopsis:

-
-class entry
-{
-public:
-
-        typedef std::map<std::string, entry> dictionary_type;
-        typedef std::string string_type;
-        typedef std::list<entry> list_type;
-        typedef size_type integer_type;
-
-        enum data_type
-        {
-                int_t,
-                string_t,
-                list_t,
-                dictionary_t,
-                undefined_t
-        };
-
-        data_type type() const;
-
-        entry(dictionary_type const&);
-        entry(string_type const&);
-        entry(list_type const&);
-        entry(integer_type const&);
-
-        entry();
-        entry(data_type t);
-        entry(entry const& e);
-        ~entry();
-
-        void operator=(entry const& e);
-        void operator=(dictionary_type const&);
-        void operator=(string_type const&);
-        void operator=(list_type const&);
-        void operator=(integer_type const&);
-
-        integer_type& integer();
-        integer_type const& integer() const;
-        string_type& string();
-        string_type const& string() const;
-        list_type& list();
-        list_type const& list() const;
-        dictionary_type& dict();
-        dictionary_type const& dict() const;
-
-        // these functions requires that the entry
-        // is a dictionary, otherwise they will throw
-        entry& operator[](char const* key);
-        entry& operator[](std::string const& key);
-        entry const& operator[](char const* key) const;
-        entry const& operator[](std::string const& key) const;
-        entry* find_key(char const* key);
-        entry const* find_key(char const* key) const;
-
-        void print(std::ostream& os, int indent = 0) const;
-};
-
-

TODO: finish documentation of entry.

-
-

integer() string() list() dict() type()

-
-
-integer_type& integer();
-integer_type const& integer() const;
-string_type& string();
-string_type const& string() const;
-list_type& list();
-list_type const& list() const;
-dictionary_type& dict();
-dictionary_type const& dict() 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.

-

The print() function is there for debug purposes only.

-

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:

-
-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:

-
-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.

-
-
-

operator[]

-
-
-entry& operator[](char const* key);
-entry& operator[](std::string const& key);
-entry const& operator[](char const* key) const;
-entry const& operator[](std::string const& key) const;
-
-
-

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.

-
-
-

find_key()

-
-
-entry* find_key(char const* key);
-entry const* find_key(char const* key) const;
-
-
-

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.

-
-
-
-

torrent_info

-

In previous versions of libtorrent, this class was also used for creating -torrent files. This functionality has been moved to create_torrent, see -make_torrent.

-

The torrent_info has the following synopsis:

-
-class torrent_info
-{
-public:
-
-        // these constructors throws exceptions on error
-        torrent_info(sha1_hash const& info_hash);
-        torrent_info(lazy_entry const& torrent_file);
-        torrent_info(char const* buffer, int size);
-        torrent_info(boost::filesystem::path const& filename);
-        torrent_info(boost::filesystem::wpath const& filename);
-
-        // these constructors sets the error code on error
-        torrent_info(sha1_hash const& info_hash, error_code& ec);
-        torrent_info(lazy_entry const& torrent_file, error_code& ec);
-        torrent_info(char const* buffer, int size, error_code& ec);
-        torrent_info(fs::path const& filename, error_code& ec);
-        torrent_info(fs::wpath const& filename, error_code& ec);
-
-        void add_tracker(std::string const& url, int tier = 0);
-        std::vector<announce_entry> const& trackers() const;
-
-        file_storage const& files() const;
-        file_storage const& orig_files() const;
-
-        void remap_files(file_storage const& f);
-
-        void rename_file(int index, std::string const& new_filename);
-        void rename_file(int index, std::wstring const& new_filename);
-
-        typedef file_storage::iterator file_iterator;
-        typedef file_storage::reverse_iterator reverse_file_iterator;
-
-        file_iterator begin_files() const;
-        file_iterator end_files() const;
-        reverse_file_iterator rbegin_files() const;
-        reverse_file_iterator rend_files() const;
-
-        int num_files() const;
-        file_entry const& file_at(int index) const;
-
-        std::vector<file_slice> map_block(int piece, size_type offset
-                , int size) const;
-        peer_request map_file(int file_index, size_type file_offset
-                , int size) const;
-
-        bool priv() const;
-
-        void add_url_seed(std::string const& url);
-        void add_http_seed(std::string const& url);
-        std::vector<web_seed_entry> const& web_seeds() const;
-
-        size_type total_size() const;
-        int piece_length() const;
-        int num_pieces() const;
-        sha1_hash const& info_hash() const;
-        std::string const& name() const;
-        std::string const& comment() const;
-        std::string const& creator() const;
-
-        std::vector<std::pair<std::string, int> > const& nodes() const;
-        void add_node(std::pair<std::string, int> const& node);
-
-        boost::optional<time_t> creation_date() const;
-
-        int piece_size(unsigned int index) const;
-        sha1_hash const& hash_for_piece(unsigned int index) const;
-        char const* hash_for_piece_ptr(unsigned int index) const;
-
-        boost::shared_array<char> metadata() const;
-        int metadata_size() const;
-};
-
-
-

torrent_info()

-
-
-torrent_info(sha1_hash const& info_hash);
-torrent_info(lazy_entry const& torrent_file);
-torrent_info(char const* buffer, int size);
-torrent_info(boost::filesystem::path const& filename);
-torrent_info(boost::filesystem::wpath const& filename);
-
-torrent_info(sha1_hash const& info_hash, error_code& ec);
-torrent_info(lazy_entry const& torrent_file, error_code& ec);
-torrent_info(char const* buffer, int size, error_code& ec);
-torrent_info(fs::path const& filename, error_code& ec);
-torrent_info(fs::wpath const& filename, error_code& ec);
-
-
-

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(), see bdecode() bencode().

-

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.

-

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.

-
-
-

add_tracker()

-
-
-void add_tracker(std::string const& url, int tier = 0);
-
-
-

add_tracker() adds a tracker to the announce-list. The tier determines the order in -which the trackers are to be tried. For more information see trackers().

-
-
-

files() orig_files()

-
-
-file_storage const& files() const;
-file_storage const& orig_files() const;
-
-
-

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.

-
-
-

remap_files()

-
-
-void remap_files(file_storage const& f);
-
-
-

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.

-
-
-

rename_file()

-
-
-void rename_file(int index, std::string const& new_filename);
-void rename_file(int index, std::wstring const& new_filename);
-
-
-

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() orig_files()), change the name, and -then use remap_files().

-
-
-

begin_files() end_files() rbegin_files() rend_files()

-
-
-file_iterator begin_files() const;
-file_iterator end_files() const;
-reverse_file_iterator rbegin_files() const;
-reverse_file_iterator rend_files() const;
-
-
-

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 file_entry.

-
-struct file_entry
-{
-        boost::filesystem::path path;
-        size_type offset;
-        size_type size;
-        size_type file_base;
-        std::string symlink_path;
-        boost::shared_ptr<sha1_hash> filehash;
-        bool pad_file:1;
-        bool hidden_attribute:1;
-        bool executable_attribute:1;
-        bool symlink_attribute:1;
-};
-
-

The path is the full (relative) path of each file. i.e. if it is a multi-file -torrent, all the files starts with a directory with the same name as torrent_info::name(). -The filenames are encoded with UTF-8.

-

size is 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.

-

file_base is 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.

-

pad_file is 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.

-

hidden_attribute is true if the file was marked as hidden (on windows).

-

executable_attribute is true if the file was marked as executable (posix)

-

symlink_attribute is true if the file was a symlink. If this is the case -the symlink_path specifies the original location where the data for this file -was found.

-

filehash is a pointer that is set in case the torrent file included a sha1 hash -for this file. This may be use to look up more sources for this file on other networks.

-
-
-

num_files() file_at()

-
-
-int num_files() const;
-file_entry const& file_at(int index) const;
-
-
-

If you need index-access to files you can use the num_files() and file_at() -to access files using indices.

-
-
-

map_block()

-
-
-std::vector<file_slice> map_block(int piece, size_type offset
-        , int size) const;
-
-
-

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.

-

The file slice struct looks like this:

-
-struct file_slice
-{
-        int file_index;
-        size_type offset;
-        size_type size;
-};
-
-

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.

-
-
-

map_file()

-
-
-peer_request map_file(int file_index, size_type file_offset
-        , int size) const;
-
-
-

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. -The peer_request structure looks like this:

-
-struct peer_request
-{
-        int piece;
-        int start;
-        int length;
-        bool operator==(peer_request const& r) const;
-};
-
-

piece is the index of the piece in which the range starts. -start is the offset within that piece where the range starts. -length is the size of the range, in bytes.

-

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().

-
-
-

add_url_seed() add_http_seed()

-
-
-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<web_seed_entry> const& web_seeds() const;
-
-
-

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.

-

The web_seed_entry has the following members:

-
-struct web_seed_entry
-{
-        enum type_t { url_seed, http_seed };
-
-        typedef std::vector<std::pair<std::string, std::string> > 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());
-
-        bool operator==(web_seed_entry const& e) const;
-        bool operator<(web_seed_entry const& e) const;
-
-        std::string url;
-        type_t type;
-        std::string auth;
-        headers_t extra_headers;
-
-        // ...
-};
-
-
-
-

trackers()

-
-
-std::vector<announce_entry> const& trackers() const;
-
-
-

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.

-
-struct announce_entry
-{
-        announce_entry(std::string const& url);
-        std::string url;
-
-        int next_announce_in() const;
-        int min_announce_in() const;
-
-        error_code last_error;
-
-        std::string message;
-
-        boost::uint8_t tier;
-        boost::uint8_t fail_limit;
-        boost::uint8_t fails;
-
-        enum tracker_source
-        {
-                source_torrent = 1,
-                source_client = 2,
-                source_magnet_link = 4,
-                source_tex = 8
-        };
-        boost::uint8_t source;
-
-        bool verified:1;
-        bool updating:1;
-        bool start_sent:1;
-        bool complete_sent:1;
-};
-
-

next_announce_in() 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.

-

If the last time this tracker was contacted, the tracker returned a warning -or error message, message contains that message.

-

fail_limit is the max number of failures to announce to this tracker in -a row, before this tracker is not used anymore.

-

fails is the number of times in a row we have failed to announce to this -tracker.

-

source is a bitmask specifying which sources we got this tracker from.

-

verified is set to true the first time we receive a valid response -from this tracker.

-

updating is true while we're waiting for a response from the tracker.

-

start_sent is 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.

-

complete_sent is set to true when we send a event=completed.

-
-
-

total_size() piece_length() piece_size() num_pieces()

-
-
-size_type total_size() const;
-int piece_length() const;
-int piece_size(unsigned int index) const;
-int num_pieces() const;
-
-
-

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.

-
-
-

hash_for_piece() hash_for_piece_ptr() info_hash()

-
-
-size_type piece_size(unsigned int index) const;
-sha1_hash const& hash_for_piece(unsigned int index) const;
-char const* hash_for_piece_ptr(unsigned int index) const;
-
-
-

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. For more information on the sha1_hash, see the big_number class. -hash_for_piece_ptr() returns a pointer to the 20 byte sha1 digest for the piece. -Note that the string is not null-terminated.

-
-
-

name() comment() creation_date() creator()

-
-
-std::string const& name() const;
-std::string const& comment() const;
-std::string const& creator() const;
-boost::optional<time_t> creation_date() const;
-
-
-

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.

-
-
-

priv()

-
-
-bool priv() const;
-
-
-

priv() returns true if this torrent is private. i.e., it should not be -distributed on the trackerless network (the kademlia DHT).

-
-
-

nodes()

-
-
-std::vector<std::pair<std::string, int> > const& nodes() const;
-
-
-

If this torrent contains any DHT nodes, they are put in this vector in their original -form (host name and port number).

-
-
-

add_node()

-
-
-void add_node(std::pair<std::string, int> const& node);
-
-
-

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.

-
-
-

metadata() metadata_size()

-
-
-boost::shared_array<char> metadata() const;
-int metadata_size() const;
-
-
-

metadata() returns a the raw info section of the torrent file. The size -of the metadata is returned by metadata_size().

-
-
-
-

torrent_handle

-

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.

-
-

Its declaration looks like this:

-
-struct torrent_handle
-{
-        torrent_handle();
-
-        enum status_flags_t
-        {
-                query_distributed_copies = 1,
-                query_accurate_download_counters = 2,
-                query_last_seen_complete = 4
-        };
-
-        torrent_status status(boost::uint32_t flags = 0xffffffff);
-        void file_progress(std::vector<size_type>& fp, int flags = 0);
-        void get_download_queue(std::vector<partial_piece_info>& queue) const;
-        void get_peer_info(std::vector<peer_info>& v) const;
-        torrent_info const& get_torrent_info() const;
-        bool is_valid() const;
-
-        std::string name() const;
-
-        void save_resume_data(int flags = 0) const;
-        bool need_save_resume_data() const;
-        void force_reannounce() const;
-        void force_dht_announce() const;
-        void force_reannounce(boost::posix_time::time_duration) const;
-        void scrape_tracker() const;
-        void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const;
-
-        void set_tracker_login(std::string const& username
-                , std::string const& password) const;
-
-        std::vector<announce_entry> trackers() const;
-        void replace_trackers(std::vector<announce_entry> const&);
-        void add_tracker(announce_entry const& url);
-
-        void add_url_seed(std::string const& url);
-        void remove_url_seed(std::string const& url);
-        std::set<std::string> url_seeds() const;
-
-        void add_http_seed(std::string const& url);
-        void remove_http_seed(std::string const& url);
-        std::set<std::string> http_seeds() const;
-
-        void set_ratio(float ratio) const;
-        int max_uploads() const;
-        void set_max_uploads(int max_uploads) const;
-        void set_max_connections(int max_connections) const;
-        int max_connections() const;
-        void set_upload_limit(int limit) const;
-        int upload_limit() const;
-        void set_download_limit(int limit) const;
-        int download_limit() const;
-        void set_sequential_download(bool sd) const;
-        bool is_sequential_download() const;
-
-        int get_peer_upload_limit(tcp::endpoint ip);
-        int get_peer_download_limit(tcp::endpoint ip);
-        void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const;
-        void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const;
-
-        int queue_position() const;
-        void queue_position_up() const;
-        void queue_position_down() const;
-        void queue_position_top() const;
-        void queue_position_bottom() const;
-
-        void set_priority(int prio) const;
-
-        void use_interface(char const* net_interface) const;
-
-        enum pause_flags_t { graceful_pause = 1 };
-        void pause(int flags = 0) const;
-        void resume() const;
-        bool is_seed() const;
-        void force_recheck() const;
-        void clear_error() const;
-        void set_upload_mode(bool m) const;
-        void set_share_mode(bool m) const;
-
-        void flush_cache() const;
-
-        void resolve_countries(bool r);
-        bool resolve_countries() const;
-
-        enum deadline_flags { alert_when_available = 1 };
-        void set_piece_deadline(int index, int deadline, int flags = 0) const;
-
-        void piece_availability(std::vector<int>& avail) const;
-        void piece_priority(int index, int priority) const;
-        int piece_priority(int index) const;
-        void prioritize_pieces(std::vector<int> const& pieces) const;
-        std::vector<int> piece_priorities() const;
-
-        void file_priority(int index, int priority) const;
-        int file_priority(int index) const;
-        void prioritize_files(std::vector<int> const& files) const;
-        std::vector<int> file_priorities() const;
-
-        void auto_managed(bool m) const;
-
-        bool set_metadata(char const* buf, int size) const;
-
-        boost::filesystem::path save_path() const;
-        void move_storage(boost::filesystem::path const& save_path) const;
-        void move_storage(boost::filesystem::wpath const& save_path) const;
-        void rename_file(int index, boost::filesystem::path) const;
-        void rename_file(int index, boost::filesystem::wpath) const;
-        storage_interface* get_storage_impl() const;
-
-        bool super_seeding() const;
-        void super_seeding(bool on) const;
-
-        enum flags_t { overwrite_existing = 1 };
-        void add_piece(int piece, char const* data, int flags = 0) const;
-        void read_piece(int piece) const;
-
-        sha1_hash info_hash() const;
-
-        bool operator==(torrent_handle const&) const;
-        bool operator!=(torrent_handle const&) const;
-        bool operator<(torrent_handle const&) const;
-};
-
-

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.

-
-
-

set_piece_deadline()

-
-
-enum deadline_flags { alert_when_available = 1 };
-void set_piece_deadline(int index, int deadline, int flags = 0) const;
-
-
-

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.

-
-
-

piece_availability()

-
-
-void piece_availability(std::vector<int>& avail) const;
-
-
-

Fills the specified std::vector<int> 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.

-
-
-

piece_priority() prioritize_pieces() piece_priorities()

-
-
-void piece_priority(int index, int priority) const;
-int piece_priority(int index) const;
-void prioritize_pieces(std::vector<int> const& pieces) const;
-std::vector<int> piece_priorities() 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:

-
-
    -
  1. piece is not downloaded at all
  2. -
  3. normal priority. Download order is dependent on availability
  4. -
  5. higher than normal priority. Pieces are preferred over pieces with -the same availability, but not over pieces with lower availability
  6. -
  7. pieces are as likely to be picked as partial pieces.
  8. -
  9. pieces are preferred over partial pieces, but not over pieces with -lower availability
  10. -
  11. currently the same as 4
  12. -
  13. piece is as likely to be picked as any piece with availability 1
  14. -
  15. maximum priority, availability is disregarded, the piece is preferred -over any other piece with lower priority
  16. -
-
-

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.

-
-
-

file_priority() prioritize_files() file_priorities()

-
-
-void file_priority(int index, int priority) const;
-int file_priority(int index) const;
-void prioritize_files(std::vector<int> const& files) const;
-std::vector<int> file_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 sepcial 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.

-
-
-

file_progress()

-
-
-void file_progress(std::vector<size_type>& fp, int flags = 0);
-
-
-

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.

-
-
-

save_path()

-
-
-boost::filesystem::path save_path() const;
-
-
-

save_path() returns the path that was given to add_torrent() when this torrent -was started.

-
-
-

move_storage()

-
-
-void move_storage(boost::filesystem::path const& save_path) const;
-void move_storage(boost::filesystem::wpath const& save_path) 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.

-
-
-

rename_file()

-
-
-void rename_file(int index, boost::filesystem::path) const;
-void rename_file(int index, boost::filesystem::wpath) 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.

-
-
-

get_storage_impl()

-
-
-storage_interface* get_storage_impl() const;
-
-
-

Returns the storage implementation for this torrent. This depends on the -storage contructor function that was passed to session::add_torrent.

-
-
-

super_seeding()

-
-
-bool super_seeding() const;
-void super_seeding(bool on) const;
-
-
-

Enables or disabled super seeding/initial seeding for this torrent. The torrent -needs to be a seed for this to take effect. The overload that returns a bool -tells you of super seeding is enabled or not.

-
-
-

add_piece()

-
-
-enum flags_t { overwrite_existing = 1 };
-void add_piece(int piece, char const* data, int flags = 0) const;
-
-
-

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 has_failed_alert.

-
-
-

read_piece()

-
-
-void read_piece(int piece) 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. In order to receive this alert, you must enable -alert::storage_notification in your alert mask (see set_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.

-
-
-

force_reannounce() force_dht_announce()

-
-
-void force_reannounce() const;
-void force_reannounce(boost::posix_time::time_duration) const;
-void force_dht_announce() const;
-
-
-

force_reannounce() will force this torrent to do another tracker request, to receive new -peers. The second overload of force_reannounce that takes a time_duration as -argument will schedule a reannounce in that amount of time from now.

-

force_dht_announce will announce the torrent to the DHT immediately.

-
-
-

scrape_tracker()

-
-
-void scrape_tracker() const;
-
-
-

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.

-
-
-

connect_peer()

-
-
-void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) 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.

-
-
-

name()

-
-
-std::string name() const;
-
-
-

Returns the name of the torrent. i.e. the name from the metadata associated with it. 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.

-
-
-

set_ratio()

-
-
-void set_ratio(float ratio) const;
-
-
-

set_ratio() sets the desired download / upload ratio. If set to 0, it is considered being -infinite. i.e. the client will always upload as much as it can, no matter how much it gets back -in return. With this setting it will work much like the standard clients.

-

Besides 0, the ratio can be set to any number greater than or equal to 1. It means how much to -attempt to upload in return for each download. e.g. if set to 2, the client will try to upload -2 bytes for every byte received. The default setting for this is 0, which will make it work -as a standard client.

-
-
-

set_upload_limit() set_download_limit() upload_limit() download_limit()

-
-
-void set_upload_limit(int limit) const;
-void set_download_limit(int limit) const;
-int upload_limit() const;
-int download_limit() 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::set_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.

-
-
-

set_sequential_download()

-
-
-void set_sequential_download(bool sd);
-
-
-

set_sequential_download() enables or disables sequential download. When enabled, the piece -picker will pick pieces in sequence instead of rarest first.

-

Enabling sequential download will affect the piece distribution negatively in the swarm. It should be -used sparingly.

-
-
-

get_peer_download_limit() get_peer_upload_limit() set_peer_upload_limit() set_peer_download_limit()

-
-
-int get_peer_upload_limit(tcp::endpoint ip);
-int get_peer_download_limit(tcp::endpoint ip);
-void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const;
-void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const;
-
-
-

Works like get_upload_limit, get_download_limit, set_upload_limit and -set_download_limit respectively, but controls individual peer instead of the -whole torrent.

-
-
-

pause() resume()

-
-
-enum pause_flags_t { graceful_pause = 1 };
-void pause(int flags) const;
-void resume() const;
-
-
-

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.

-
-
-

flush_cache()

-
-
-void flush_cache() 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.

-
-
-

force_recheck()

-
-
-void force_recheck() 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.

-
-
-

clear_error()

-
-
-void clear_error() 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.

-
-
-

set_upload_mode()

-
-void set_upload_mode(bool m) 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.

-
-
-

set_share_mode()

-
-
-void set_share_mode(bool m) 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.

-
-
-

resolve_countries()

-
-
-void resolve_countries(bool r);
-bool resolve_countries() 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.

-
-
-

is_seed()

-
-
-bool is_seed() const;
-
-
-

Returns true if the torrent is in seed mode (i.e. if it has finished downloading).

-
-
-

auto_managed()

-
-
-void auto_managed(bool m) const;
-
-
-

auto_managed() changes whether the torrent is auto managed or not. For more info, -see queuing.

-
-
-

set_metadata()

-
-
-bool set_metadata(char const* buf, int size) const;
-
-
-

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.

-
-
-

set_tracker_login()

-
-
-void set_tracker_login(std::string const& username
-        , std::string const& password) const;
-
-
-

set_tracker_login() 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.

-
-
-

trackers() replace_trackers() add_tracker()

-
-
-std::vector<announce_entry> trackers() const;
-void replace_trackers(std::vector<announce_entry> const&) const;
-void add_tracker(announc_entry const& url);
-
-
-

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() force_dht_announce(). See trackers() for the definition of 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.

-
-
-

add_url_seed() remove_url_seed() url_seeds()

-
-
-void add_url_seed(std::string const& url);
-void remove_url_seed(std::string const& url);
-std::set<std::string> url_seeds() 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.

-
-
-

add_http_seed() remove_http_seed() http_seeds()

-
-
-void add_http_seed(std::string const& url);
-void remove_http_seed(std::string const& url);
-std::set<std::string> http_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.

-
-
-

queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom()

-
-
-int queue_position() const;
-void queue_position_up() const;
-void queue_position_down() const;
-void queue_position_top() const;
-void queue_position_bottom() 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.

-
-
-

set_priority()

-
-
-void set_priority(int prio) 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.

-

Torrents with higher priority are also more likely to have its peers unchoked, to -distribute more upload capacity to them.

-
-
-

use_interface()

-
-
-void use_interface(char const* net_interface) const;
-
-
-

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.

-
-
-

info_hash()

-
-
-sha1_hash info_hash() const;
-
-
-

info_hash() returns the info-hash for the torrent.

-
-
-

set_max_uploads() max_uploads()

-
-
-void set_max_uploads(int max_uploads) const;
-int max_uploads() 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.

-

max_uploads() returns the current settings.

-
-
-

set_max_connections() max_connections()

-
-
-void set_max_connections(int max_connections) const;
-int max_connections() 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.

-

max_connections() returns the current settings.

-
-
-

save_resume_data()

-
-
-void save_resume_data(int flags = 0) const;
-
-
-

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 may be set to torrent_handle::flush_cache. Doing so will flush the disk -cache 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.

-

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. -
  3. The torrent is checking (or is queued for checking) its storage, it will obviously -not be ready to write resume data.
  4. -
  5. The torrent hasn't received valid metadata and was started without metadata -(see libtorrent's metadata from peers extension)
  6. -
-
-

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:

-
-int num_resume_data = 0;
-std::vector<torrent_handle> handles = ses.get_torrents();
-ses.pause();
-for (std::vector<torrent_handle>::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;
-
-        h.save_resume_data();
-        ++num_resume_data;
-}
-
-while (num_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<alert> holder = ses.pop_alert();
-
-        if (alert_cast<save_resume_data_failed_alert>(a))
-        {
-                process_alert(a);
-                --num_resume_data;
-                continue;
-        }
-
-        save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(a);
-        if (rd == 0)
-        {
-                process_alert(a);
-                continue;
-        }
-
-        torrent_handle h = rd->handle;
-        boost::filesystem::ofstream out(h.save_path()
-                / (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
-        out.unsetf(std::ios_base::skipws);
-        bencode(std::ostream_iterator<char>(out), *rd->resume_data);
-        --num_resume_data;
-}
-
-
-
-

need_save_resume_data()

-
-
-bool need_save_resume_data() 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.

-
-
-

status()

-
-
-torrent_status status(boost::uint32_t flags = 0xffffffff) const;
-
-
-

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:

-
    -
  • -
    query_distributed_copies
    -

    calculates distributed_copies, distributed_full_copies and distributed_fraction.

    -
    -
    -
  • -
  • -
    query_accurate_download_counters
    -

    includes partial downloaded blocks in total_done and total_wanted_done.

    -
    -
    -
  • -
  • -
    query_last_seen_complete
    -

    includes last_seen_complete.

    -
    -
    -
  • -
-
-
-

get_download_queue()

-
-
-void get_download_queue(std::vector<partial_piece_info>& queue) 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. The entry in the vector (partial_piece_info) looks like this:

-
-struct partial_piece_info
-{
-        int piece_index;
-        int blocks_in_piece;
-        enum state_t { none, slow, medium, fast };
-        state_t piece_state;
-        block_info* blocks;
-};
-
-

piece_index is 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.

-

piece_state is 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.

-
-struct block_info
-{
-        enum block_state_t
-        { none, requested, writing, finished };
-
-        void set_peer(tcp::endpoint const& ep);
-        tcp::endpoint peer() const;
-
-        unsigned bytes_progress:15;
-        unsigned block_size:15;
-        unsigned state:2;
-        unsigned num_peers:14;
-};
-
-

The blocks field points to an array of blocks_in_piece elements. This pointer is -only valid until the next call to get_download_queue() for any torrent in the same session. -They all share the storaga for the block arrays in their session object.

-

The block_info array contains data for each individual block in the piece. Each block has -a state (state) which is any of:

-
    -
  • none - This block has not been downloaded or requested form any peer.
  • -
  • requested - The block has been requested, but not completely downloaded yet.
  • -
  • writing - The block has been downloaded and is currently queued for being written to disk.
  • -
  • finished - The block has been written to disk.
  • -
-

The peer field is the ip address of the peer this block was downloaded from. -num_peers is 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. -bytes_progress is the number of bytes that have been received for this block, and -block_size is the total number of bytes in this block.

-
-
-

get_peer_info()

-
-
-void get_peer_info(std::vector<peer_info>&) const;
-
-
-

get_peer_info() 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.

-
-
-

get_torrent_info()

-
-
-torrent_info const& get_torrent_info() const;
-
-
-

Returns a const reference to the torrent_info object associated with this torrent. -This reference is valid as long as the torrent_handle is valid, no longer. If the -torrent_handle is invalid or if it doesn't have any metadata, libtorrent_exception -exception will be thrown. The torrent may be in a state without metadata only if -it was started without a .torrent file, i.e. by using the libtorrent extension of -just supplying a tracker and info-hash.

-
-
-

is_valid()

-
-
-bool is_valid() 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.

-
-
-
-

torrent_status

-

It contains the following fields:

-
-struct torrent_status
-{
-        enum state_t
-        {
-                queued_for_checking,
-                checking_files,
-                downloading_metadata,
-                downloading,
-                finished,
-                seeding,
-                allocating,
-                checking_resume_data
-        };
-
-        state_t state;
-        bool paused;
-        bool auto_managed;
-        bool sequential_download;
-        bool seeding;
-        bool finished;
-        float progress;
-        int progress_ppm;
-        std::string error;
-
-        boost::posix_time::time_duration next_announce;
-        boost::posix_time::time_duration announce_interval;
-
-        std::string current_tracker;
-
-        size_type total_download;
-        size_type total_upload;
-
-        size_type total_payload_download;
-        size_type total_payload_upload;
-
-        size_type total_failed_bytes;
-        size_type total_redundant_bytes;
-
-        int download_rate;
-        int upload_rate;
-
-        int download_payload_rate;
-        int upload_payload_rate;
-
-        int num_peers;
-
-        int num_complete;
-        int num_incomplete;
-
-        int list_seeds;
-        int list_peers;
-
-        int connect_candidates;
-
-        bitfield pieces;
-        int num_pieces;
-
-        size_type total_done;
-        size_type total_wanted_done;
-        size_type total_wanted;
-
-        int num_seeds;
-
-        int distributed_full_copies;
-        int distributed_fraction;
-
-        float distributed_copies;
-
-        int block_size;
-
-        int num_uploads;
-        int num_connections;
-        int uploads_limit;
-        int connections_limit;
-
-        storage_mode_t storage_mode;
-
-        int up_bandwidth_queue;
-        int down_bandwidth_queue;
-
-        size_type all_time_upload;
-        size_type all_time_download;
-
-        int active_time;
-        int finished_time;
-        int seeding_time;
-
-        int seed_rank;
-
-        int last_scrape;
-
-        bool has_incoming;
-
-        int sparse_regions;
-
-        bool seed_mode;
-        bool upload_mode;
-        bool share_mode;
-
-        int priority;
-
-        time_t added_time;
-        time_t completed_time;
-        time_t last_seen_complete;
-
-        int time_since_upload;
-        int time_since_download;
-
-        int queue_position;
-        bool need_save_resume;
-};
-
-

progress is a value in the range [0, 1], that represents the progress of the -torrent's current task. It may be checking files or downloading.

-

progress_ppm 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.

-

The torrent's current task is in the state member, it will be one of the following:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - -
checking_resume_dataThe 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.
queued_for_checkingThe 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.
checking_filesThe torrent has not started its download yet, and is -currently checking existing files.
downloading_metadataThe torrent is trying to download metadata from peers. -This assumes the metadata_transfer extension is in use.
downloadingThe 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.
finishedIn 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.
seedingIn this state the torrent has finished downloading and -is a pure seeder.
allocatingIf the torrent was started in full allocation mode, this -indicates that the (disk) storage for the torrent is -allocated.
-

When downloading, the progress is total_wanted_done / total_wanted. This takes -into account files whose priority have been set to 0. They are not considered.

-

paused is 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().

-

auto_managed is 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

-

sequential_download is true when the torrent is in sequential download mode. In -this mode pieces are downloaded in order rather than rarest first.

-

is_seeding is true if all pieces have been downloaded.

-

is_finished is 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.

-

has_metadata is 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). Note that if the torrent doesn't have metadata, the member -get_torrent_info() will throw.

-

error 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.

-

next_announce is the time until the torrent will announce itself to the tracker. And -announce_interval is the time the tracker want us to wait until we announce ourself -again the next time.

-

current_tracker is the URL of the last working tracker. If no tracker request has -been successful yet, it's set to an empty string.

-

total_download and total_upload is 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.

-

total_payload_download and total_payload_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.

-

total_failed_bytes is 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.

-

total_redundant_bytes is 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.

-

pieces is the 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.

-

num_pieces is 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.

-

download_rate and upload_rate are 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. The -download_payload_rate and upload_payload_rate respectively is 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.

-

num_peers is 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().

-

num_complete and num_incomplete are set to -1 if the tracker did not -send any scrape data in its announce reply. This data is optional and may -not be available from all trackers. If these are not -1, they are the total -number of peers that are seeding (complete) and the total number of peers -that are still downloading (incomplete) this torrent.

-

list_seeds and list_peers are the number of seeds in our peer list -and the total number of peers (including seeds) respectively. 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.

-

connect_candidates is 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.

-

total_done is 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).

-

total_wanted_done is 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).

-

total_wanted is the total number of bytes we want to download. This is also -excluding pieces whose priorities have been set to 0.

-

num_seeds is the number of peers that are seeding that this client is -currently connected to.

-

distributed_full_copies is 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.

-

distributed_fraction 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.

-

distributed_copies is a floating point representation of the -distributed_full_copies as the integer part and distributed_fraction -/ 1000 as the fraction part. If floating point operations are disabled -this value is always -1.

-

block_size is 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.

-

num_uploads is the number of unchoked peers in this torrent.

-

num_connections is 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.

-

uploads_limit is the set limit of upload slots (unchoked peers) for this torrent.

-

connections_limit is the set limit of number of connections for this torrent.

-

storage_mode is one of storage_mode_allocate, storage_mode_sparse or -storage_mode_compact. Identifies which storage mode this torrent is being saved -with. See Storage allocation.

-

up_bandwidth_queue and down_bandwidth_queue are 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.

-

all_time_upload and all_time_download are accumulated upload and download -payload byte counters. They are saved in and restored from resume data to keep totals -across sessions.

-

active_time, finished_time and seeding_time are second counters. -They 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.

-

seed_rank is 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.

-

last_scrape is the number of seconds since this torrent acquired scrape data. -If it has never done that, this value is -1.

-

has_incoming is true if there has ever been an incoming connection attempt -to this torrent.'

-

sparse_regions 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.

-

seed_mode is 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.

-

upload_mode is 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.

-

share_mode is true if the torrent is currently in share-mode, i.e. -not downloading the torrent, but just helping the swarm out.

-

added_time is the posix-time when this torrent was added. i.e. what -time(NULL) returned at the time.

-

completed_time is the posix-time when this torrent was finished. If -the torrent is not yet finished, this is 0.

-

last_seen_complete is the time when we, or one of our peers, last -saw a complete copy of this torrent.

-

time_since_upload and time_since_download are the number of -seconds since any peer last uploaded from this torrent and the last -time a downloaded piece passed the hash check, respectively.

-

queue_position is the position this torrent has in the download -queue. If the torrent is a seed or finished, this is -1.

-

need_save_resume is true if this torrent has unsaved changes -to its download state and statistics since the last resume data -was saved.

-
-
-

peer_info

-

It contains the following fields:

-
-struct peer_info
-{
-        enum
-        {
-                interesting = 0x1,
-                choked = 0x2,
-                remote_interested = 0x4,
-                remote_choked = 0x8,
-                supports_extensions = 0x10,
-                local_connection = 0x20,
-                handshake = 0x40,
-                connecting = 0x80,
-                queued = 0x100,
-                on_parole = 0x200,
-                seed = 0x400,
-                optimistic_unchoke = 0x800,
-                snubbed = 0x1000,
-                upload_only = 0x2000,
-                holepunched = 0x4000,
-                rc4_encrypted = 0x100000,
-                plaintext_encrypted = 0x200000
-        };
-
-        unsigned int flags;
-
-        enum peer_source_flags
-        {
-                tracker = 0x1,
-                dht = 0x2,
-                pex = 0x4,
-                lsd = 0x8
-        };
-
-        int source;
-
-        enum bw_state { bw_idle, bw_limit, bw_network, bw_disk };
-
-        char read_state;
-        char write_state;
-
-        asio::ip::tcp::endpoint ip;
-        int up_speed;
-        int down_speed;
-        int payload_up_speed;
-        int payload_down_speed;
-        size_type total_download;
-        size_type total_upload;
-        peer_id pid;
-        bitfield pieces;
-        int upload_limit;
-        int download_limit;
-
-        time_duration last_request;
-        time_duration last_active;
-        int request_timeout;
-
-        int send_buffer_size;
-        int used_send_buffer;
-
-        int receive_buffer_size;
-        int used_receive_buffer;
-
-        int num_hashfails;
-
-        char country[2];
-
-        std::string inet_as_name;
-        int inet_as;
-
-        size_type load_balancing;
-
-        int requests_in_buffer;
-        int download_queue_length;
-        int upload_queue_length;
-
-        int failcount;
-
-        int downloading_piece_index;
-        int downloading_block_index;
-        int downloading_progress;
-        int downloading_total;
-
-        std::string client;
-
-        enum
-        {
-                standard_bittorrent = 0,
-                web_seed = 1
-        };
-        int connection_type;
-
-        int remote_dl_rate;
-
-        int pending_disk_bytes;
-
-        int send_quota;
-        int receive_quota;
-
-        int rtt;
-
-        int num_pieces;
-
-        int download_rate_peak;
-        int upload_rate_peak;
-
-        float progress;
-        int progress_ppm;
-
-        tcp::endpoint local_endpoint;
-};
-
-

The flags attribute tells you in which state the peer is. It is set to -any combination of the enums above. The following table describes each flag:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
interestingwe are interested in pieces from this peer.
chokedwe have choked this peer.
remote_interestedthe peer is interested in us
remote_chokedthe peer has choked us.
support_extensionsmeans that this peer supports the -extension protocol.
local_connectionThe 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.
handshakeThe connection is opened, and waiting for the -handshake. Until the handshake is done, the peer -cannot be identified.
connectingThe connection is in a half-open state (i.e. it is -being connected).
queuedThe 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.
on_paroleThe 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.
seedThis peer is a seed (it has all the pieces).
optimistic_unchokeThis 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.
snubbedThis 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.
upload_onlyThis 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.
holepunchedThis 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.
-

source is a combination of flags describing from which sources this peer -was received. The flags are:

- ---- - - - - - - - - - - - - - - - - - -
trackerThe peer was received from the tracker.
dhtThe peer was received from the kademlia DHT.
pexThe peer was received from the peer exchange -extension.
lsdThe peer was received from the local service -discovery (The peer is on the local network).
resume_dataThe peer was added from the fast resume data.
-

read_state and write_state indicates what state this peer is in with regards -to sending and receiving data. The states are declared in the bw_state enum and -defines as follows:

- ---- - - - - - - - - - - - - - - -
bw_idleThe peer is not waiting for any external events to -send or receive data.
bw_limitThe peer is waiting for the rate limiter.
bw_networkThe 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_diskThe peer is waiting for the disk I/O thread to catch -up writing buffers to disk before downloading more.
-

The ip field is the IP-address to this peer. The type is an asio endpoint. For -more info, see the asio documentation.

-

up_speed and down_speed contains the current upload and download speed -we have to and from this peer (including any protocol messages). The transfer rates -of payload data only are found in payload_up_speed and payload_down_speed. -These figures are updated approximately once every second.

-

total_download and total_upload are 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.

-

pid is 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()_

-

pieces is 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).

-

seed is true if this peer is a seed.

-

upload_limit is the number of bytes per second we are allowed to send to this -peer every second. It may be -1 if there's no local limit on the peer. The global -limit and the torrent limit is always enforced anyway.

-

download_limit is the number of bytes per second this peer is allowed to -receive. -1 means it's unlimited.

-

last_request and last_active is the time since we last sent a request -to this peer and since any transfer occurred with this peer, respectively.

-

request_timeout is 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.

-

send_buffer_size and used_send_buffer is the number of bytes allocated -and used for the peer's send buffer, respectively.

-

receive_buffer_size and used_receive_buffer are the number of bytes -allocated and used as receive buffer, respectively.

-

num_hashfails is the number of pieces this peer has participated in -sending us that turned out to fail the hash check.

-

country is 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().

-

inet_as_name is 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.

-

inet_as is the AS number the peer is located in.

-

load_balancing is a measurement of the balancing of free download (that we get) -and free upload that we give. Every peer gets a certain amount of free upload, but -this member says how much extra free upload this peer has got. If it is a negative -number it means that this was a peer from which we have got this amount of free -download.

-

requests_in_buffer is the number of requests messages that are currently in the -send buffer waiting to be sent.

-

download_queue_length is the number of piece-requests we have sent to this peer -that hasn't been answered with a piece yet.

-

upload_queue_length is the number of piece-requests we have received from this peer -that we haven't answered with a piece yet.

-

failcount is 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.

-

You can know which piece, and which part of that piece, that is currently being -downloaded from a specific peer by looking at the next 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.

-

client is 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.

-

connection_type can currently be one of:

- ---- - - - - - - - - - - - - - - - - - - - -
typemeaning
peer_info::standard_bittorrentRegular bittorrent connection over TCP
peer_info::bittorrent_utpBittorrent connection over uTP
peer_info::web_sesedHTTP connection using the BEP 19 protocol
peer_info::http_seedHTTP connection using the BEP 17 protocol
-

remote_dl_rate is an estimate of the rate this peer is downloading at, in -bytes per second.

-

pending_disk_bytes is 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.

-

send_quota and receive_quota are 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.

-

rtt is 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.

-

num_pieces is the number of pieces this peer has.

-

download_rate_peak and upload_rate_peak are 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.

-

progress is the progress of the peer in the range [0, 1]. This is always 0 when -floating point operations are diabled, instead use progress_ppm.

-

progress_ppm indicates the download progress of the peer in the range [0, 1000000] -(parts per million).

-

local_endpoint is 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.

-
-
-

session customization

-

You have some control over session configuration through the session_settings object. You -create it and fill it with your settings and then use session::set_settings() -to apply them.

-

You have control over proxy and authorization settings and also the user-agent -that will be sent to the tracker. The user-agent will also be used to identify the -client with other peers.

-
-

presets

-

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.

-
-session_settings min_memory_usage();
-session_settings high_performance_seed();
-
-

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.

-
-
-

session_settings

-
-struct session_settings
-{
-        session_settings();
-        int version;
-        std::string user_agent;
-        int tracker_completion_timeout;
-        int tracker_receive_timeout;
-        int stop_tracker_timeout;
-        int tracker_maximum_response_length;
-
-        int piece_timeout;
-        float request_queue_time;
-        int max_allowed_in_request_queue;
-        int max_out_request_queue;
-        int whole_pieces_threshold;
-        int peer_timeout;
-        int urlseed_timeout;
-        int urlseed_pipeline_size;
-        int file_pool_size;
-        bool allow_multiple_connections_per_ip;
-        int max_failcount;
-        int min_reconnect_time;
-        int peer_connect_timeout;
-        bool ignore_limits_on_local_network;
-        int connection_speed;
-        bool send_redundant_have;
-        bool lazy_bitfields;
-        int inactivity_timeout;
-        int unchoke_interval;
-        int optimistic_unchoke_interval;
-        std::string announce_ip;
-        int num_want;
-        int initial_picker_threshold;
-        int allowed_fast_set_size;
-
-        enum { no_piece_suggestions = 0, suggest_read_cache = 1 };
-        int suggest_mode;
-        int max_queued_disk_bytes;
-        int handshake_timeout;
-        bool use_dht_as_fallback;
-        bool free_torrent_hashes;
-        bool upnp_ignore_nonrouters;
-        int send_buffer_watermark;
-        int send_buffer_watermark_factor;
-
-#ifndef TORRENT_NO_DEPRECATE
-        bool auto_upload_slots;
-        bool auto_upload_slots_rate_based;
-#endif
-
-        enum choking_algorithm_t
-        {
-                fixed_slots_choker,
-                auto_expand_choker,
-                rate_based_choker,
-                bittyrant_choker
-        };
-
-        int choking_algorithm;
-
-        enum seed_choking_algorithm_t
-        {
-                round_robin,
-                fastest_upload,
-                anti_leech
-        };
-
-        int seed_choking_algorithm;
-
-        bool use_parole_mode;
-        int cache_size;
-        int cache_buffer_chunk_size;
-        int cache_expiry;
-        bool use_read_cache;
-        bool explicit_read_cache;
-        int explicit_cache_interval;
-
-        enum io_buffer_mode_t
-        {
-                enable_os_cache = 0,
-                disable_os_cache_for_aligned_files = 1,
-                disable_os_cache = 2
-        };
-        int disk_io_write_mode;
-        int disk_io_read_mode;
-
-        std::pair<int, int> outgoing_ports;
-        char peer_tos;
-
-        int active_downloads;
-        int active_seeds;
-        int active_dht_limit;
-        int active_tracker_limit;
-        int active_limit;
-        bool auto_manage_prefer_seeds;
-        bool dont_count_slow_torrents;
-        int auto_manage_interval;
-        float share_ratio_limit;
-        float seed_time_ratio_limit;
-        int seed_time_limit;
-        int peer_turnover_interval;
-        float peer_turnover;
-        float peer_turnover_cutoff;
-        bool close_redundant_connections;
-
-        int auto_scrape_interval;
-        int auto_scrape_min_interval;
-
-        int max_peerlist_size;
-
-        int min_announce_interval;
-
-        bool prioritize_partial_pieces;
-        int auto_manage_startup;
-
-        bool rate_limit_ip_overhead;
-
-        bool announce_to_all_trackers;
-        bool announce_to_all_tiers;
-
-        bool prefer_udp_trackers;
-        bool strict_super_seeding;
-
-        int seeding_piece_quota;
-
-        int max_sparse_regions;
-
-        bool lock_disk_cache;
-
-        int max_rejects;
-
-        int recv_socket_buffer_size;
-        int send_socket_buffer_size;
-
-        bool optimize_hashing_for_speed;
-
-        int file_checks_delay_per_block;
-
-        enum disk_cache_algo_t
-        { lru, largest_contiguous };
-
-        disk_cache_algo_t disk_cache_algorithm;
-
-        int read_cache_line_size;
-        int write_cache_line_size;
-
-        int optimistic_disk_retry;
-        bool disable_hash_check;
-
-        int max_suggest_pieces;
-
-        bool drop_skipped_requests;
-
-        bool low_prio_disk;
-        int local_service_announce_interval;
-        int dht_announce_interval;
-
-        int udp_tracker_token_expiry;
-        bool volatile_read_cache;
-        bool guided_read_cache;
-        bool default_min_cache_age;
-
-        int num_optimistic_unchoke_slots;
-        bool no_atime_storage;
-        int default_est_reciprocation_rate;
-        int increase_est_reciprocation_rate;
-        int decrease_est_reciprocation_rate;
-        bool incoming_starts_queued_torrents;
-        bool report_true_downloaded;
-        bool strict_end_game_mode;
-
-        int default_peer_upload_rate;
-        int default_peer_download_rate;
-        bool broadcast_lsd;
-
-        bool enable_outgoing_utp;
-        bool enable_incoming_utp;
-        bool enable_outgoing_tcp;
-        bool enable_incoming_tcp;
-        int max_pex_peers;
-        bool ignore_resume_timestamps;
-        bool anonymous_mode;
-        int tick_interval;
-        int share_mode_target;
-
-        int upload_rate_limit;
-        int download_rate_limit;
-        int local_upload_rate_limit;
-        int local_download_rate_limit;
-        int unchoke_slots_limit;
-        int half_open_limit;
-        int connections_limit;
-
-        int utp_target_delay;
-        int utp_gain_factor;
-        int utp_min_timeout;
-        int utp_syn_resends;
-        int utp_num_resends;
-        int utp_connect_timeout;
-        int utp_delayed_ack;
-        bool utp_dynamic_sock_buf;
-
-        enum bandwidth_mixed_algo_t
-        {
-                prefer_tcp = 0,
-                peer_proportional = 1
-
-        };
-        int mixed_mode_algorithm;
-        bool rate_limit_utp;
-
-        int listen_queue_size;
-};
-
-

version is automatically set to the libtorrent version you're using -in order to be forward binary compatible. This field should not be changed.

-

user_agent this is 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.

-

tracker_completion_timeout is 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.

-

tracker_receive_timeout is 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.

-

stop_tracker_timeout is the time to wait for tracker responses when -shutting down the session object. This is given in seconds. Default is -10 seconds.

-

tracker_maximum_response_length is 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.

-

piece_timeout controls the number of seconds from a request is sent until -it times out if no piece response is returned.

-

request_queue_time is 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.

-

max_allowed_in_request_queue is 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 handled) the last request will be -dropped. The higher this is, the faster upload speeds the client can get to a -single peer.

-

max_out_request_queue is 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.

-

whole_pieces_threshold is a limit in seconds. if a whole piece can be -downloaded in at least this number of seconds from a specific peer, the -peer_connection will prefer requesting 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.

-

peer_timeout is the number of seconds the peer connection should -wait (for any activity on the peer connection) before closing it 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.

-

urlseed_timeout is the same as peer_timeout but applies only to -url seeds. This value defaults to 20 seconds.

-

urlseed_pipeline_size 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.

-

file_pool_size is the 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.

-

allow_multiple_connections_per_ip 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.

-

max_failcount is the maximum times we try to connect to a peer before -stop connecting again. If a peer succeeds, the failcounter is reset. If -a peer is retrieved from a peer source (other than DHT) the failcount is -decremented by one, allowing another try.

-

min_reconnect_time is the time to wait between connection attempts. If -the peer fails, the time is multiplied by fail counter.

-

peer_connect_timeout 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.

-

ignore_limits_on_local_network, if set to true, upload, download and -unchoke limits are ignored for peers on the local network.

-

connection_speed is 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.

-

send_redundant_have controls if 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.

-

lazy_bitfields 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 to prevent certain ISPs from stopping people -from seeding.

-

inactivity_timeout, if a peer is uninteresting and uninterested -for longer than this number of seconds, it will be disconnected. -Default is 10 minutes

-

unchoke_interval is 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.

-

optimistic_unchoke_interval is the number of seconds between -each optimistic unchoke. On this timer, the currently optimistically -unchoked peer will change.

-

announce_ip is the ip address passed along to trackers as the &ip= parameter. -If left as the default (an empty string), that parameter is omitted.

-

num_want is the number of peers we want from each tracker request. It defines -what is sent as the &num_want= parameter to the tracker.

-

initial_picker_threshold 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.

-

allowed_fast_set_size is the number of pieces we allow peers to download -from us without being unchoked.

-

suggest_mode controls whether or not libtorrent will send out suggest -messages to create a bias of its peers to request certain pieces. The modes -are:

-
    -
  • no_piece_suggestsions which is the default and will not send out suggest -messages.
  • -
  • suggest_read_cache which will send out suggest messages for the most -recent pieces that are in the read cache.
  • -
-

max_queued_disk_bytes is the number maximum number of bytes, to be -written to disk, that can wait in the disk I/O thread queue. This queue -is only for waiting for the disk I/O thread to receive the job and either -write it to disk or insert it in the write cache. 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.

-

handshake_timeout specifies the number of seconds we allow a peer to -delay responding to a protocol handshake. If no response is received within -this time, the connection is closed.

-

use_dht_as_fallback 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.

-

free_torrent_hashes 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 add_torrent() for instance.

-

upnp_ignore_nonrouters 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.

-

send_buffer_watermark is 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.

-

send_buffer_watermark_factor is multiplied to the peer's upload rate -to determine the low-watermark for the peer. This is clamped to not -exceed the send_buffer_watermark upper limit. This defaults to 1. -For high capacity connections, setting this higher can improve upload -performance and disk throughput.

-

auto_upload_slots 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.

-

When auto_upload_slots_rate_based is 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.

-

choking_algorithm 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.

-

The options for choking algorithms are:

-
    -
  • fixed_slots_choker is the traditional choker with a fixed number of unchoke -slots (as specified by session::set_max_uploads()).
  • -
  • auto_expand_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.
  • -
  • rate_based_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.
  • -
  • bittyrant_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::set_upload_rate_limit()). For more information about this -choker, see the paper.
  • -
-

seed_choking_algorithm controls the seeding unchoke behavior. The available -options are:

-
    -
  • round_robin which 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.
  • -
  • fastest_upload unchokes the peers we can send to the fastest. This might be -a bit more reliable in utilizing all available capacity.
  • -
  • anti_leech 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.
  • -
-

use_parole_mode 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.

-

cache_size is 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.

-

cache_expiry is the number of seconds from the last cached write to a piece -in the write cache, to when it's forcefully flushed to disk. Default is 60 second.

-

use_read_cache, is set to true (default), the disk cache is also used to -cache pieces read from disk. Blocks for writing pieces takes presedence.

-

explicit_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.

-

explicit_cache_interval is 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.

-

disk_io_write_mode and disk_io_read_mode determines how files are -opened when they're in read only mode versus read and write mode. The options -are:

-
-
    -
  • -
    enable_os_cache
    -

    This is the default and files are opened normally, with the OS caching -reads and writes.

    -
    -
    -
  • -
  • -
    disable_os_cache_for_aligned_files
    -

    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
    -

    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.

    -
    -
    -
  • -
-
-

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.

-

outgoing_ports, 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.

-

peer_tos 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.

-

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.

-

auto_manage_prefer_seeds specifies if libtorrent should prefer giving seeds -active slots or downloading torrents. The default is false.

-

if dont_count_slow_torrents is 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.

-

active_limit is a hard limit on the number of active torrents. This applies even to -slow torrents.

-

active_dht_limit is the max number of torrents to announce to the DHT. By default -this is set to 88, which is no more than one DHT announce every 10 seconds.

-

active_tracker_limit is the max number of torrents to announce to their trackers. -By default this is 360, which is no more than one announce every 5 seconds.

-

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).

-

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.

-

auto_manage_interval is the number of seconds between the torrent queue -is updated, and rotated.

-

share_ratio_limit is the upload / download ratio limit for considering a -seeding torrent have met the seed limit criteria. See queuing.

-

seed_time_ratio_limit is the seeding time / downloading time ratio limit -for considering a seeding torrent to have met the seed limit criteria. See queuing.

-

seed_time_limit is 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.

-

peer_turnover_interval controls a feature where libtorrent periodically can disconnect -the least useful peers in the hope of connecting to better ones. This settings 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).

-

close_redundant_connections 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.

-

auto_scrape_interval is 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.

-

auto_scrape_min_interval is 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.

-

max_peerlist_size is 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.

-

max_paused_peerlist_size is 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.

-

min_announce_interval is 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.

-

If prioritize_partial_pieces is 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.

-

auto_manage_startup is 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.

-

If rate_limit_ip_overhead is set to true, the estimated TCP/IP overhead is -drained from the rate limiters, to avoid exceeding the limits with the total traffic

-

announce_to_all_trackers 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.

-

announce_to_all_tiers also 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.

-

prefer_udp_trackers is 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.

-

strict_super_seeding 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.

-

seeding_piece_quota is 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.

-

max_sparse_regions 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.

-

lock_disk_cache 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.

-

max_rejects is 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.

-

recv_socket_buffer_size and send_socket_buffer_size 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.

-

optimize_hashing_for_speed 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.

-

file_checks_delay_per_block is 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.

-

disk_cache_algorithm tells the disk I/O thread which cache flush -algorithm to use. The default algorithm is largest_contiguous. This -flushes the entire piece, in the write cache, that was least recently -written to. This is specified by the session_settings::lru enum -value. session_settings::largest_contiguous will flush the largest -sequences of contiguous blocks from the write cache, regarless of the -piece's last use time.

-

read_cache_line_size is 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.

-

optimistic_disk_retry is 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().

-

disable_hash_check 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.

-

max_suggest_pieces is 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.

-

If drop_skipped_requests is 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.

-

low_prio_disk 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.

-

local_service_announce_interval is 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.

-

dht_announce_interval is the number of seconds between announcing -torrents to the distributed hash table (DHT). This is specified to -be 15 minutes which is its default.

-

udp_tracker_token_expiry is 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.

-

volatile_read_cache, 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.

-

guided_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.

-

default_min_cache_age is 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.

-

num_optimistic_unchoke_slots is 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.

-

no_atime_storage 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.

-

default_est_reciprocation_rate is 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.

-

increase_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.

-

decrease_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.

-

incoming_starts_queued_torrents 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.

-

When report_true_downloaded is true, the &downloaded= argument -sent to trackers will include redundant downloaded bytes. It defaults -to false, which means redundant bytes are not reported to the tracker.

-

strict_end_game_mode 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.

-

default_peer_upload_rate and default_peer_download_rate specifies -the default upload and download rate limits for peers, respectively. These -default to 0, which means unlimited. These settings affect the rate limits -set on new peer connections (not existing ones). The peer rate limits can -be changed individually later using -get_peer_download_limit() get_peer_upload_limit() set_peer_upload_limit() set_peer_download_limit().

-

if broadcast_lsd is 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. It's off by default since it's inefficient.

-

enable_outgoing_utp, enable_incoming_utp, enable_outgoing_tcp, -enable_incoming_tcp 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.

-

ignore_resume_timestamps 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.

-

anonymous_mode 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. Trackers will only be used if they are using a proxy -server. 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). Since no incoming connections -are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off -when this setting is enabled.

-

If you're using I2P, it might make sense to enable anonymous mode as well.

-

tick_interval 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.

-

share_mode_target 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.

-

upload_rate_limit, download_rate_limit, local_upload_rate_limit -and local_download_rate_limit 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.

-

unchoke_slots_limit is the mac number of unchoked peers in the session.

-

The number of unchoke slots may be ignored depending on what -choking_algorithm is set to.

-

half_open_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.

-

connections_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.

-

utp_target_delay is 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.

-

utp_gain_factor is 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.

-

utp_min_timeout is 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.

-

utp_syn_resends is the number of SYN packets that are sent (and timed out) before -giving up and closing the socket.

-

utp_num_resends is the number of times a packet is sent (and lossed or timed out) -before giving up and closing the connection.

-

utp_connect_timeout is 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.

-

utp_delayed_ack is the number of milliseconds to delay ACKs the most. Delaying ACKs -significantly helps reducing the amount of protocol overhead in the reverse direction -from downloads. It defaults to 100 milliseconds. If set to 0, delayed ACKs are disabled -and every incoming payload packet is ACKed. The granularity of this timer is capped by -the tick interval (as specified by tick_interval).

-

utp_dynamic_sock_buf 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).

-

The mixed_mode_algorithm 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 (which they aren't by default).

-

rate_limit_utp determines if uTP connections should be throttled by the global rate -limiter or not. By default they are not, since uTP manages its own rate.

-

listen_queue_size is 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).

-
-
-
-

pe_settings

-

The pe_settings structure is used to control the settings related -to peer protocol encryption:

-
-struct pe_settings
-{
-        pe_settings();
-
-        enum enc_policy
-        {
-                forced,
-                enabled,
-                disabled
-        };
-
-        enum enc_level
-        {
-                plaintext,
-                rc4,
-                both
-        };
-
-        enc_policy out_enc_policy;
-        enc_policy in_enc_policy;
-        enc_level allowed_enc_level;
-        bool prefer_rc4;
-};
-
-

in_enc_policy and out_enc_policy control the settings for incoming -and outgoing connections respectively. The settings for these are:

-
-
    -
  • forced - 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.
  • -
  • enabled - 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.
  • -
  • disabled - only non-encrypted connections are allowed.
  • -
-
-

allowed_enc_level 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. The settings are:

-
-
    -
  • plaintext - only the handshake is encrypted, the bulk of the traffic -remains unchanged.
  • -
  • rc4 - the entire stream is encrypted with RC4
  • -
  • both - both RC4 and plaintext connections are allowed.
  • -
-
-

prefer_rc4 can be set to true if you want to prefer the RC4 encrypted stream.

-
-
-

proxy_settings

-

The proxy_settings structs contains the information needed to -direct certain traffic to a proxy.

-
-
-struct proxy_settings
-{
-        proxy_settings();
-
-        std::string hostname;
-        int port;
-
-        std::string username;
-        std::string password;
-
-        enum proxy_type
-        {
-                none,
-                socks4,
-                socks5,
-                socks5_pw,
-                http,
-                http_pw
-        };
-
-        proxy_type type;
-        bool proxy_hostnames;
-};
-
-
-

hostname is 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.

-

The type tells libtorrent what kind of proxy server it is. The following -options are available:

-
-
    -
  • none - This is the default, no proxy server is used, all other fields -are ignored.
  • -
  • socks4 - The server is assumed to be a SOCKS4 server that -requires a username.
  • -
  • socks5 - The server is assumed to be a SOCKS5 server (RFC 1928) that -does not require any authentication. The username and password are ignored.
  • -
  • socks5_pw - 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.
  • -
  • http - 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.
  • -
  • http_pw - The server is assumed to be an HTTP proxy that requires -user authorization. The username and password will be sent to the proxy.
  • -
-
-

proxy_hostnames 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.

-
-
-

ip_filter

-

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).

-
-
-template <class Addr>
-struct ip_range
-{
-        Addr first;
-        Addr last;
-        int flags;
-};
-
-class ip_filter
-{
-public:
-        enum access_flags { blocked = 1 };
-
-        ip_filter();
-        void add_rule(address first, address last, int flags);
-        int access(address const& addr) const;
-
-        typedef boost::tuple<std::vector<ip_range<address_v4> >
-                , std::vector<ip_range<address_v6> > > filter_tuple_t;
-
-        filter_tuple_t export_filter() const;
-};
-
-
-
-

ip_filter()

-
-
-ip_filter()
-
-
-

Creates a default filter that doesn't filter any address.

-

postcondition: -access(x) == 0 for every x

-
-
-

add_rule()

-
-
-void add_rule(address first, address last, int flags);
-
-
-

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.

-
-
-

access()

-
-
-int access(address const& addr) const;
-
-
-

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.

-
-
-

export_filter()

-
-
-boost::tuple<std::vector<ip_range<address_v4> >
-        , std::vector<ip_range<address_v6> > > export_filter() const;
-
-
-

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.

-
-
-
-

big_number

-

Both the peer_id and sha1_hash types are typedefs of the class -big_number. It represents 20 bytes of data. Its synopsis follows:

-
-class big_number
-{
-public:
-        bool operator==(const big_number& n) const;
-        bool operator!=(const big_number& n) const;
-        bool operator<(const big_number& n) const;
-
-        const unsigned char* begin() const;
-        const unsigned char* end() const;
-
-        unsigned char* begin();
-        unsigned char* end();
-};
-
-

The iterators gives you access to individual bytes.

-
-
-

bitfield

-

The bitfiled type stores any number of bits as a bitfield in an array.

-
-class bitfield
-{
-        bitfield();
-        bitfield(int bits);
-        bitfield(int bits, bool val);
-        bitfield(char const* bytes, int bits);
-        bitfield(bitfield const& rhs);
-
-        void borrow_bytes(char* bytes, int bits);
-        ~bitfield();
-
-        void assign(char const* bytes, int bits);
-
-        bool operator[](int index) const;
-
-        bool get_bit(int index) const;
-
-        void clear_bit(int index);
-        void set_bit(int index);
-
-        std::size_t size() const;
-        bool empty() const;
-
-        char const* bytes() const;
-
-        bitfield& operator=(bitfield const& rhs);
-
-        int count() const;
-
-        typedef const_iterator;
-        const_iterator begin() const;
-        const_iterator end() const;
-
-        void resize(int bits, bool val);
-        void set_all();
-        void clear_all();
-        void resize(int bits);
-};
-
-
-
-

hasher

-

This class creates sha1-hashes. Its declaration looks like this:

-
-class hasher
-{
-public:
-        hasher();
-        hasher(char const* data, unsigned int len);
-
-        void update(char const* data, unsigned int len);
-        sha1_hash final();
-        void reset();
-};
-
-

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.

-
-
-

fingerprint

-

The fingerprint class represents information about a client and its version. It is used -to encode this information into the client's peer id.

-

This is the class declaration:

-
-struct fingerprint
-{
-        fingerprint(const char* id_string, int major, int minor
-                , int revision, int tag);
-
-        std::string to_string() const;
-
-        char name[2];
-        char major_version;
-        char minor_version;
-        char revision_version;
-        char tag_version;
-
-};
-
-

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 charsclient
'AZ'Azureus
'LT'libtorrent (default)
'BX'BittorrentX
'MT'Moonlight Torrent
'TS'Torrent Storm
'SS'Swarm Scope
'XT'Xan Torrent
-

There's currently an informal directory of client id's here.

-

The major, minor, revision and tag parameters are used to identify the -version of your client. All these numbers must be within the range [0, 9].

-

to_string() will generate the actual string put in the peer-id, and return it.

-
-
-

UPnP and NAT-PMP

-

The upnp and natpmp classes contains the state for all UPnP and NAT-PMP mappings, -by default 1 or two mappings are made by libtorrent, one for the listen port and one -for the DHT port (UDP).

-
-class upnp
-{
-public:
-
-        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);
-
-        void discover_device();
-        void close();
-
-        std::string router_model();
-};
-
-class natpmp
-{
-public:
-
-        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);
-
-        void close();
-        void rebind(address const& listen_interface);
-};
-
-

discover_device(), close() and rebind() are for internal uses and should -not be called directly by clients.

-
-

add_mapping()

-
-
-int add_mapping(protocol_type p, int external_port, int local_port);
-
-
-

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.

-
-
-

delete_mapping()

-
-
-void delete_mapping(int mapping_index);
-
-
-

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().

-
-
-

router_model()

-
-
-std::string router_model();
-
-
-

This is only available for UPnP routers. If the model is advertized by -the router, it can be queried through this function.

-
-
-
-

free functions

-
-

identify_client()

-
-
-std::string identify_client(peer_id const& id);
-
-
-

This function is declared in the header <libtorrent/identify_client.hpp>. It 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.

-
-
-

client_fingerprint()

-
-
-boost::optional<fingerprint> client_fingerprint(peer_id const& 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. This function is -declared in the header <libtorrent/identify_client.hpp>.

-
-
-

bdecode() bencode()

-
-
-template<class InIt> entry bdecode(InIt start, InIt end);
-template<class OutIt> void bencode(OutIt out, const entry& e);
-
-
-

These functions will encode data to bencoded or decode bencoded data.

-

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<char> buffer;
-bencode(std::back_inserter(buf), e);
-
-

If you want to decode a torrent file from a buffer in memory, you can do it like this:

-
-std::vector<char> 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 throw libtorrent_exception.

-
-
-

add_magnet_uri()

-
-
-torrent_handle add_magnet_uri(session& ses, std::string const& uri
-        add_torrent_params p);
-torrent_handle add_magnet_uri(session& ses, std::string const& uri
-        add_torrent_params p, error_code& ec);
-
-
-

This function parses the magnet URI (uri) as a bittorrent magnet link, -and adds the torrent to the specified session (ses). It returns the -handle to the newly added torrent, or an invalid handle in case parsing -failed. To control some initial settings of the torrent, sepcify those in -the add_torrent_params, p. See add_torrent().

-

The overload that does not take an error_code throws an exception on -error and is not available when building without exception support.

-

For more information about magnet links, see magnet links.

-
-
-

make_magnet_uri()

-
-
-std::string make_magnet_uri(torrent_handle const& handle);
-
-
-

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.

-
-
-
-

alerts

-

The pop_alert() function on session is the interface for retrieving -alerts, warnings, messages and errors from libtorrent. If no alerts have -been posted by libtorrent pop_alert() will return a default initialized -auto_ptr object. If there is an alert in libtorrent's queue, the alert -from the front of the queue is popped and returned. -You can then use the alert object and query

-

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 a bitmask with the following bits:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
error_notification

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
  • -
-
peer_notificationEnables alerts when peers send invalid requests, get banned or -snubbed.
port_mapping_notificationEnables alerts for port mapping events. For NAT-PMP and UPnP.
storage_notificationEnables alerts for events related to the storage. File errors and -synchronization events for moving the storage, renaming files etc.
tracker_notificationEnables all tracker events. Includes announcing to trackers, -receiving responses, warnings and errors.
debug_notificationLow level alerts for when peers are connected and disconnected.
status_notificationEnables alerts for when a torrent or the session changes state.
progress_notificationAlerts for when blocks are requested and completed. Also when -pieces are completed.
ip_block_notificationAlerts when a peer is blocked by the ip blocker or port blocker.
performance_warningAlerts when some limit is reached that might limit the download -or upload rate.
stats_notificationIf 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.
all_categoriesThe full bitmask, representing all available categories.
-

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

-

When you get an alert, you can use alert_cast<> to attempt to cast the pointer to a -more specific alert type, to be queried for more information about the alert. alert_cast -has the followinf signature:

-
-template <T> T* alert_cast(alert* a);
-template <T> T const* alert_cast(alert const* a);
-
-

You can also use a alert dispatcher mechanism that's available in libtorrent.

-

All alert types are defined in the <libtorrent/alert_types.hpp> header file.

-

The alert class is the base class that specific messages are derived from. This -is its synopsis:

-
-class alert
-{
-public:
-
-        enum category_t
-        {
-                error_notification = implementation defined,
-                peer_notification = implementation defined,
-                port_mapping_notification = implementation defined,
-                storage_notification = implementation defined,
-                tracker_notification = implementation defined,
-                debug_notification = implementation defined,
-                status_notification = implementation defined,
-                progress_notification = implementation defined,
-                ip_block_notification = implementation defined,
-                performance_warning = implementation defined,
-                dht_notification = implementation defined,
-
-                all_categories = implementation defined
-        };
-
-        ptime timestamp() const;
-
-        virtual ~alert();
-
-        virtual int type() const = 0;
-        virtual std::string message() const = 0;
-        virtual char const* what() const = 0;
-        virtual int category() const = 0;
-        virtual std::auto_ptr<alert> clone() const = 0;
-};
-
-

type() 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:

-
-std::auto_ptr<alert> a = ses.pop_alert();
-switch (a->type())
-{
-        case read_piece_alert::alert_type:
-        {
-                read_piece_alert* p = (read_piece_alert*)a.get();
-                // use p
-                break;
-        }
-        case file_renamed_alert::alert_type:
-        {
-                // etc...
-        }
-}
-
-

what() returns a string literal describing the type of the alert. It does -not include any information that might be bundled with the alert.

-

category() returns a bitmask specifying which categories this alert belong to.

-

clone() returns a pointer to a copy of the alert.

-

message() 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.

-

There's another alert base class that most alerts derives from, all the -alerts that are generated for a specific torrent are derived from:

-
-struct torrent_alert: alert
-{
-        // ...
-        torrent_handle handle;
-};
-
-

There's also a base class for all alerts referring to tracker events:

-
-struct tracker_alert: torrent_alert
-{
-        // ...
-        std::string url;
-};
-
-

The specific alerts are:

-
-

read_piece_alert

-

This alert is posted when the asynchronous read operation initiated by -a call to 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.

-
-struct read_piece_alert: torrent_alert
-{
-        // ...
-        boost::shared_ptr<char> buffer;
-        int piece;
-        int size;
-};
-
-
-
-

external_ip_alert

-

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 external_ip_alert: alert
-{
-        // ...
-        address external_address;
-};
-
-
-
-

listen_failed_alert

-

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 listen_failed_alert: alert
-{
-        // ...
-        tcp::endpoint endpoint;
-        error_code error;
-};
-
-
-
-

listen_succeeded_alert

-

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 listen_succeeded_alert: alert
-{
-        // ...
-        tcp::endpoint endpoint;
-};
-
-
-
-

portmap_error_alert

-

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.

-

mapping refers to the mapping index of the port map that failed, i.e. -the index returned from add_mapping().

-

map_type is 0 for NAT-PMP and 1 for UPnP.

-

error tells you what failed.

-
-struct portmap_error_alert: alert
-{
-        // ...
-        int mapping;
-        int type;
-        error_code error;
-};
-
-
-
-

portmap_alert

-

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.

-

mapping refers to the mapping index of the port map that failed, i.e. -the index returned from add_mapping().

-

external_port is the external port allocated for the mapping.

-

type is 0 for NAT-PMP and 1 for UPnP.

-
-struct portmap_alert: alert
-{
-        // ...
-        int mapping;
-        int external_port;
-        int map_type;
-};
-
-
-
-

portmap_log_alert

-

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 portmap_log_alert: alert
-{
-        //...
-        int map_type;
-        std::string msg;
-};
-
-
-
-

file_error_alert

-

If the storage fails to read or write files that it needs access to, this alert is -generated and the torrent is paused.

-

file is the path to the file that was accessed when the error occurred.

-

error is the error code describing the error.

-
-struct file_error_alert: torrent_alert
-{
-        // ...
-        std::string file;
-        error_code error;
-};
-
-
-
-

file_renamed_alert

-

This is posted as a response to a torrent_handle::rename_file call, if the rename -operation succeeds.

-
-struct file_renamed_alert: torrent_alert
-{
-        // ...
-        std::string name;
-        int index;
-};
-
-

The index member refers to the index of the file that was renamed, -name is the new name of the file.

-
-
-

file_rename_failed_alert

-

This is posted as a response to a torrent_handle::rename_file call, if the rename -operation failed.

-
-struct file_rename_failed_alert: torrent_alert
-{
-        // ...
-        int index;
-        error_code error;
-};
-
-

The index member refers to the index of the file that was supposed to be renamed, -error is the error code returned from the filesystem.

-
-
-

tracker_announce_alert

-

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 tracker_announce_alert: tracker_alert
-{
-        // ...
-        int event;
-};
-
-

Event specifies what event was sent to the tracker. It is defined as:

-
    -
  1. None
  2. -
  3. Completed
  4. -
  5. Started
  6. -
  7. Stopped
  8. -
-
-
-

tracker_error_alert

-

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 tracker_error_alert: tracker_alert
-{
-        // ...
-        int times_in_row;
-        int status_code;
-};
-
-
-
-

tracker_reply_alert

-

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 tracker_reply_alert: tracker_alert
-{
-        // ...
-        int num_peers;
-};
-
-

The num_peers tells how many peers were returned from the tracker. This is -not necessarily all new peers, some of them may already be connected.

-
-
-

dht_reply_alert

-

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 dht_reply_alert: tracker_alert
-{
-        // ...
-        int num_peers;
-};
-
-
-
-

tracker_warning_alert

-

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. The msg string in the alert contains the warning message from -the tracker.

-
-struct tracker_warning_alert: tracker_alert
-{
-        // ...
-        std::string msg;
-};
-
-
-
-

scrape_reply_alert

-

This alert is generated when a scrape request succeeds. incomplete -and complete is the data returned in the scrape response. These numbers -may be -1 if the reponse was malformed.

-
-struct scrape_reply_alert: tracker_alert
-{
-        // ...
-        int incomplete;
-        int complete;
-};
-
-
-
-

scrape_failed_alert

-

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. msg contains a message describing the error.

-
-struct scrape_failed_alert: tracker_alert
-{
-        // ...
-        std::string msg;
-};
-
-
-
-

url_seed_alert

-

This alert is generated when a HTTP seed name lookup fails.

-

It contains url to the HTTP seed that failed along with an error message.

-
-struct url_seed_alert: torrent_alert
-{
-        // ...
-        std::string url;
-};
-
-
-
-

hash_failed_alert

-

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 hash_failed_alert: torrent_alert
-{
-        // ...
-        int piece_index;
-};
-
-
-
-

peer_alert

-

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 peer_alert: torrent_alert
-{
-        // ...
-        tcp::endpoint ip;
-        peer_id pid;
-};
-
-
-
-

peer_connect_alert

-

This alert is posted every time an outgoing peer connect attempts succeeds.

-
-struct peer_connect_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

peer_ban_alert

-

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 peer_ban_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

peer_snubbed_alert

-

This alert is generated when a peer is snubbed, when it stops sending data when we request -it.

-
-struct peer_snubbed_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

peer_unsnubbed_alert

-

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 peer_unsnubbed_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

peer_error_alert

-

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.

-

The error_code tells you what error caused this alert.

-
-struct peer_error_alert: peer_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

peer_connected_alert

-

This alert is generated when a peer is connected.

-
-struct peer_connected_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

peer_disconnected_alert

-

This alert is generated when a peer is disconnected for any reason (other than the ones -covered by peer_error_alert).

-

The error_code tells you what error caused peer to disconnect.

-
-struct peer_disconnected_alert: peer_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

invalid_request_alert

-

This is a debug alert that is generated by an incoming invalid piece request. -ĂŹp is the address of the peer and the request is the actual incoming -request from the peer.

-
-struct invalid_request_alert: peer_alert
-{
-        // ...
-        peer_request request;
-};
-
-
-struct peer_request
-{
-        int piece;
-        int start;
-        int length;
-        bool operator==(peer_request const& r) const;
-};
-
-

The peer_request contains the values the client sent in its request message. piece is -the index of the piece it want data from, start is the offset within the piece where the data -should be read, and length is the amount of data it wants.

-
-
-

request_dropped_alert

-

This alert is generated when a peer rejects or ignores a piece request.

-
-struct request_dropped_alert: peer_alert
-{
-        // ...
-        int block_index;
-        int piece_index;
-};
-
-
-
-

block_timeout_alert

-

This alert is generated when a block request times out.

-
-struct block_timeout_alert: peer_alert
-{
-        // ...
-        int block_index;
-        int piece_index;
-};
-
-
-
-

block_finished_alert

-

This alert is generated when a block request receives a response.

-
-struct block_finished_alert: peer_alert
-{
-        // ...
-        int block_index;
-        int piece_index;
-};
-
-
-
-

lsd_peer_alert

-

This alert is generated when we receive a local service discovery message from a peer -for a torrent we're currently participating in.

-
-struct lsd_peer_alert: peer_alert
-{
-        // ...
-};
-
-
-
-

file_completed_alert

-

This is posted whenever an individual file completes its download. i.e. -All pieces overlapping this file have passed their hash check.

-
-struct file_completed_alert: torrent_alert
-{
-        // ...
-        int index;
-};
-
-

The index member refers to the index of the file that completed.

-
-
-

block_downloading_alert

-

This alert is generated when a block request is sent to a peer.

-
-struct block_downloading_alert: peer_alert
-{
-        // ...
-        int block_index;
-        int piece_index;
-};
-
-
-
-

unwanted_block_alert

-

This alert is generated when a block is received that was not requested or -whose request timed out.

-
-struct unwanted_block_alert: peer_alert
-{
-        // ...
-        int block_index;
-        int piece_index;
-};
-
-
-
-

torrent_delete_failed_alert

-

This alert is generated when a request to delete the files of a torrent fails.

-

The error_code tells you why it failed.

-
-struct torrent_delete_failed_alert: torrent_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

torrent_deleted_alert

-

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_deleted_alert: torrent_alert
-{
-        // ...
-        sha1_hash info_hash;
-};
-
-
-
-

torrent_finished_alert

-

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.

-

There are no additional data members in this alert.

-
-
-

performance_alert

-

This alert is generated when a limit is reached that might have a negative impact on -upload or download rate performance.

-
-struct performance_alert: torrent_alert
-{
-        // ...
-
-        enum performance_warning_t
-        {
-                outstanding_disk_buffer_limit_reached,
-                outstanding_request_limit_reached,
-                upload_limit_too_low,
-                download_limit_too_low,
-                send_buffer_watermark_too_low,
-                too_many_optimistic_unchoke_slots
-        };
-
-        performance_warning_t warning_code;
-};
-
-
-
outstanding_disk_buffer_limit_reached
-
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_request_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.
-
upload_limit_too_low
-
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.
-
download_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.
-
send_buffer_watermark_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, send_buffer_watermark))
-
-

If you receive this alert, you migth want to either increase your send_buffer_watermark -or send_buffer_watermark_factor.

-
-
too_many_optimistic_unchoke_slots
-
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.
-
-
-
-

state_changed_alert

-

Generated whenever a torrent changes its state.

-
-struct state_changed_alert: torrent_alert
-{
-        // ...
-
-        torrent_status::state_t state;
-        torrent_status::state_t prev_state;
-};
-
-

state is the new state of the torrent. prev_state is the previous state.

-
-
-

metadata_failed_alert

-

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.

-

There are no additional data members in this alert.

-
-
-

metadata_received_alert

-

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.

-
-
-

fastresume_rejected_alert

-

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 fastresume_rejected_alert: torrent_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

peer_blocked_alert

-

This alert is generated when a peer is blocked by the IP filter. The ip member is the -address that was blocked.

-
-struct peer_blocked_alert: torrent_alert
-{
-        // ...
-        address ip;
-};
-
-
-
-

storage_moved_alert

-

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 storage_moved_alert: torrent_alert
-{
-        // ...
-        std::string path;
-};
-
-
-
-

storage_moved_failed_alert

-

The storage_moved_failed_alert is generated when an attempt to move the storage -(via torrent_handle::move_storage()) fails.

-
-struct storage_moved_failed_alert: torrent_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

torrent_paused_alert

-

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.

-

There are no additional data members in this alert.

-
-
-

torrent_resumed_alert

-

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.

-

There are no additional data members in this alert.

-
-
-

save_resume_data_alert

-

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. -The resume_data member points to the resume data.

-
-struct save_resume_data_alert: torrent_alert
-{
-        // ...
-        boost::shared_ptr<entry> resume_data;
-};
-
-
-
-

save_resume_data_failed_alert

-

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 save_resume_data_failed_alert: torrent_alert
-{
-        // ...
-        error_code error;
-};
-
-
-
-

stats_alert

-

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 stats_alert: torrent_alert
-{
-        // ...
-        enum stats_channel
-        {
-                upload_payload,
-                upload_protocol,
-                upload_ip_protocol,
-                upload_dht_protocol,
-                upload_tracker_protocol,
-                download_payload,
-                download_protocol,
-                download_ip_protocol,
-                download_dht_protocol,
-                download_tracker_protocol,
-                num_channels
-        };
-
-        int transferred[num_channels];
-        int interval;
-};
-
-

transferred this is an array of samples. The enum describes what each -sample is a measurement of. All of these are raw, and not smoothing is performed.

-

interval 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.

-
-
-

cache_flushed_alert

-

This alert is posted when the disk cache has been flushed for a specific torrent -as a result of a call to flush_cache(). This alert belongs to the -storage_notification category, which must be enabled to let this alert through.

-
-struct flush_cached_alert: torrent_alert
-{
-        // ...
-};
-
-
-
-

dht_announce_alert

-

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 dht_announce_alert: alert
-{
-        // ...
-        address ip;
-        int port;
-        sha1_hash info_hash;
-};
-
-
-
-

dht_get_peers_alert

-

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 dht_get_peers_alert: alert
-{
-        // ...
-        sha1_hash info_hash;
-};
-
-
-
-

anonymous_mode_alert

-

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 anonymous_mode_alert: tracker_alert
-{
-        // ...
-        enum kind_t
-        {
-                tracker_not_anonymous = 1
-        };
-        int kind;
-        std::string str;
-};
-
-

kind specifies what error this is, it's one of:

-

tracker_not_anonymous 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.

-
-
-
-

alert dispatcher

-

The handle_alert class is defined in <libtorrent/alert.hpp>.

-

Examples usage:

-
-struct my_handler
-{
-        void operator()(portmap_error_alert const& a) const
-        {
-                std::cout << "Portmapper: " << a.msg << std::endl;
-        }
-
-        void operator()(tracker_warning_alert const& a) const
-        {
-                std::cout << "Tracker warning: " << a.msg << std::endl;
-        }
-
-        void operator()(torrent_finished_alert const& a) const
-        {
-                // write fast resume data
-                // ...
-
-                std::cout << a.handle.get_torrent_info().name() << "completed"
-                        << std::endl;
-        }
-};
-
-
-std::auto_ptr<alert> a;
-a = ses.pop_alert();
-my_handler h;
-while (a.get())
-{
-        handle_alert<portmap_error_alert
-                , tracker_warning_alert
-                , torrent_finished_alert
-        >::handle_alert(h, a);
-        a = ses.pop_alert();
-}
-
-

In this example 3 alert types are used. You can use any number of template -parameters to select between more types. If the number of types are more than -15, you can define TORRENT_MAX_ALERT_TYPES to a greater number before -including <libtorrent/alert.hpp>.

-
-
-

exceptions

-

Many functions in libtorrent have two versions, one that throws exceptions on -errors and one that takes an error_code reference which is filled with the -error code on errors.

-

There is one exception class that is used for errors in libtorrent, it is based -on boost.system's error_code class to carry the error code.

-
-

libtorrent_exception

-
-struct libtorrent_exception: std::exception
-{
-        libtorrent_exception(error_code const& s);
-        virtual const char* what() const throw();
-        virtual ~libtorrent_exception() throw() {}
-        boost::system::error_code error() const;
-};
-
-
-
-
-

error_code

-

libtorrent uses boost.system's error_code class to represent errors. libtorrent has -its own error category (libtorrent::libtorrent_category) whith the following error -codes:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
codesymboldescription
0no_errorNot an error
1file_collisionTwo torrents has files which end up overwriting each other
2failed_hash_checkA piece did not match its piece hash
3torrent_is_no_dictThe .torrent file does not contain a bencoded dictionary at -its top level
4torrent_missing_infoThe .torrent file does not have an info dictionary
5torrent_info_no_dictThe .torrent file's info entry is not a dictionary
6torrent_missing_piece_lengthThe .torrent file does not have a piece length entry
7torrent_missing_nameThe .torrent file does not have a name entry
8torrent_invalid_nameThe .torrent file's name entry is invalid
9torrent_invalid_lengthThe length of a file, or of the whole .torrent file is invalid. -Either negative or not an integer
10torrent_file_parse_failedFailed to parse a file entry in the .torrent
11torrent_missing_piecesThe pieces field is missing or invalid in the .torrent file
12torrent_invalid_hashesThe pieces string has incorrect length
13too_many_pieces_in_torrentThe .torrent file has more pieces than is supported by libtorrent
14invalid_swarm_metadataThe metadata (.torrent file) that was received from the swarm -matched the info-hash, but failed to be parsed
15invalid_bencodingThe file or buffer is not correctly bencoded
16no_files_in_torrentThe .torrent file does not contain any files
17invalid_escaped_stringThe string was not properly url-encoded as expected
18session_is_closingOperation is not permitted since the session is shutting down
19duplicate_torrentThere's already a torrent with that info-hash added to the -session
20invalid_torrent_handleThe supplied torrent_handle is not referring to a valid torrent
21invalid_entry_typeThe type requested from the entry did not match its type
22missing_info_hash_in_uriThe specified URI does not contain a valid info-hash
23file_too_shortOne of the files in the torrent was unexpectadly small. This -might be caused by files being changed by an external process
24unsupported_url_protocolThe URL used an unknown protocol. Currently http and -https (if built with openssl support) are recognized. For -trackers udp is recognized as well.
25url_parse_errorThe URL did not conform to URL syntax and failed to be parsed
26peer_sent_empty_pieceThe peer sent a 'piece' message of length 0
27parse_failedA bencoded structure was currupt and failed to be parsed
28invalid_file_tagThe fast resume file was missing or had an invalid file version -tag
29missing_info_hashThe fast resume file was missing or had an invalid info-hash
30mismatching_info_hashThe info-hash in the resume file did not match the torrent
31invalid_hostnameThe URL contained an invalid hostname
32invalid_portThe URL had an invalid port
33port_blockedThe port is blocked by the port-filter, and prevented the -connection
34expected_close_bracket_in_addressThe IPv6 address was expected to end with ']'
35destructing_torrentThe torrent is being destructed, preventing the operation to -succeed
36timed_outThe connection timed out
37upload_upload_connectionThe peer is upload only, and we are upload only. There's no point -in keeping the connection
38uninteresting_upload_peerThe peer is upload only, and we're not interested in it. There's -no point in keeping the connection
39invalid_info_hashThe peer sent an unknown info-hash
40torrent_pausedThe torrent is paused, preventing the operation from succeeding
41invalid_haveThe peer sent an invalid have message, either wrong size or -referring to a piece that doesn't exist in the torrent
42invalid_bitfield_sizeThe bitfield message had the incorrect size
43too_many_requests_when_chokedThe peer kept requesting pieces after it was choked, possible -abuse attempt.
44invalid_pieceThe peer sent a piece message that does not correspond to a -piece request sent by the client
45no_memorymemory allocation failed
46torrent_abortedThe torrent is aborted, preventing the operation to succeed
47self_connectionThe peer is a connection to ourself, no point in keeping it
48invalid_piece_sizeThe peer sent a piece message with invalid size, either negative -or greater than one block
49timed_out_no_interestThe peer has not been interesting or interested in us for too -long, no point in keeping it around
50timed_out_inactivityThe peer has not said anything in a long time, possibly dead
51timed_out_no_handshakeThe peer did not send a handshake within a reasonable amount of -time, it might not be a bittorrent peer
52timed_out_no_requestThe peer has been unchoked for too long without requesting any -data. It might be lying about its interest in us
53invalid_chokeThe peer sent an invalid choke message
54invalid_unchokeThe peer send an invalid unchoke message
55invalid_interestedThe peer sent an invalid interested message
56invalid_not_interestedThe peer sent an invalid not-interested message
57invalid_requestThe peer sent an invalid piece request message
58invalid_hash_listThe peer sent an invalid hash-list message (this is part of the -merkle-torrent extension)
59invalid_hash_pieceThe peer sent an invalid hash-piece message (this is part of the -merkle-torrent extension)
60invalid_cancelThe peer sent an invalid cancel message
61invalid_dht_portThe peer sent an invalid DHT port-message
62invalid_suggestThe peer sent an invalid suggest piece-message
63invalid_have_allThe peer sent an invalid have all-message
64invalid_have_noneThe peer sent an invalid have none-message
65invalid_rejectThe peer sent an invalid reject message
66invalid_allow_fastThe peer sent an invalid allow fast-message
67invalid_extendedThe peer sent an invalid extesion message ID
68invalid_messageThe peer sent an invalid message ID
69sync_hash_not_foundThe synchronization hash was not found in the encrypted handshake
70invalid_encryption_constantThe encryption constant in the handshake is invalid
71no_plaintext_modeThe peer does not support plaintext, which is the selected mode
72no_rc4_modeThe peer does not support rc4, which is the selected mode
73unsupported_encryption_modeThe peer does not support any of the encryption modes that the -client supports
74unsupported_encryption_mode_selectedThe peer selected an encryption mode that the client did not -advertise and does not support
75invalid_pad_sizeThe pad size used in the encryption handshake is of invalid size
76invalid_encrypt_handshakeThe encryption handshake is invalid
77no_incoming_encryptedThe client is set to not support incoming encrypted connections -and this is an encrypted connection
78no_incoming_regularThe client is set to not support incoming regular bittorrent -connections, and this is a regular connection
79duplicate_peer_idThe client is already connected to this peer-ID
80torrent_removedTorrent was removed
81packet_too_largeThe packet size exceeded the upper sanity check-limit
82reserved 
83http_errorThe web server responded with an error
84missing_locationThe web server response is missing a location header
85invalid_redirectionThe web seed redirected to a path that no longer matches the -.torrent directory structure
86redirectingThe connection was closed becaused it redirected to a different -URL
87invalid_rangeThe HTTP range header is invalid
88no_content_lengthThe HTTP response did not have a content length
89banned_by_ip_filterThe IP is blocked by the IP filter
90too_many_connectionsAt the connection limit
91peer_bannedThe peer is marked as banned
92stopping_torrentThe torrent is stopping, causing the operation to fail
93too_many_corrupt_piecesThe peer has sent too many corrupt pieces and is banned
94torrent_not_readyThe torrent is not ready to receive peers
95peer_not_constructedThe peer is not completely constructed yet
96session_closingThe session is closing, causing the operation to fail
97optimistic_disconnectThe peer was disconnected in order to leave room for a -potentially better peer
98torrent_finishedThe torrent is finished
99no_routerNo UPnP router found
100metadata_too_largeThe metadata message says the metadata exceeds the limit
101invalid_metadata_requestThe peer sent an invalid metadata request message
102invalid_metadata_sizeThe peer advertised an invalid metadata size
103invalid_metadata_offsetThe peer sent a message with an invalid metadata offset
104invalid_metadata_messageThe peer sent an invalid metadata message
105pex_message_too_largeThe peer sent a peer exchange message that was too large
106invalid_pex_messageThe peer sent an invalid peer exchange message
107invalid_lt_tracker_messageThe peer sent an invalid tracker exchange message
108too_frequent_pexThe peer sent an pex messages too often. This is a possible -attempt of and attack
-

NAT-PMP errors:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
codesymboldescription
120unsupported_protocol_versionThe NAT-PMP router responded with an unsupported protocol version
121natpmp_not_authorizedYou are not authorized to map ports on this NAT-PMP router
122network_failureThe NAT-PMP router failed because of a network failure
123no_resourcesThe NAT-PMP router failed because of lack of resources
124unsupported_opcodeThe NAT-PMP router failed because an unsupported opcode was sent
-

fastresume data errors:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
codesymboldescription
130missing_file_sizesThe resume data file is missing the 'file sizes' entry
131no_files_in_resume_dataThe resume data file 'file sizes' entry is empty
132missing_piecesThe resume data file is missing the 'pieces' and 'slots' entry
133mismatching_number_of_filesThe number of files in the resume data does not match the number -of files in the torrent
134mismatching_files_sizeOne of the files on disk has a different size than in the fast -resume file
135mismatching_file_timestampOne of the files on disk has a different timestamp than in the -fast resume file
136not_a_dictionaryThe resume data file is not a dictionary
137invalid_blocks_per_pieceThe 'blocks per piece' entry is invalid in the resume data file
138missing_slotsThe resume file is missing the 'slots' entry, which is required -for torrents with compact allocation
139too_many_slotsThe resume file contains more slots than the torrent
140invalid_slot_listThe 'slot' entry is invalid in the resume data
141invalid_piece_indexOne index in the 'slot' list is invalid
142pieces_need_reorderThe 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
-

HTTP errors:

- ----- - - - - - - - - - - - - - - -
150http_parse_errorThe HTTP header was not correctly formatted
151http_missing_locationThe HTTP response was in the 300-399 range but lacked a location -header
152http_failed_decompressThe HTTP response was encoded with gzip or deflate but -decompressing it failed
-

I2P errors:

- ----- - - - - - - -
160no_i2p_routerThe URL specified an i2p address, but no i2p router is configured
-

tracker errors:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170scrape_not_availableThe tracker URL doesn't support transforming it into a scrape -URL. i.e. it doesn't contain "announce.
171invalid_tracker_responseinvalid tracker response
172invalid_peer_dictinvalid peer dictionary entry. Not a dictionary
173tracker_failuretracker sent a failure message
174invalid_files_entrymissing or invalid 'files' entry
175invalid_hash_entrymissing or invalid 'hash' entry
176invalid_peers_entrymissing or invalid 'peers' and 'peers6' entry
177invalid_tracker_response_lengthudp tracker response packet has invalid size
178invalid_tracker_transaction_idinvalid transaction id in udp tracker response
179invalid_tracker_actioninvalid action field in udp tracker response
-

The names of these error codes are declared in then libtorrent::errors namespace.

-

There is also another error category, libtorrent::upnp_category, defining errors -retrned by UPnP routers. Here's a (possibly incomplete) list of UPnP error codes:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
codesymboldescription
0no_errorNo error
402invalid_argumentOne of the arguments in the request is invalid
501action_failedThe request failed
714value_not_in_arrayThe specified value does not exist in the array
715source_ip_cannot_be_wildcardedThe source IP address cannot be wild-carded, but -must be fully specified
716external_port_cannot_be_wildcardedThe external port cannot be wildcarded, but must -be specified
718port_mapping_conflictThe port mapping entry specified conflicts with a -mapping assigned previously to another client
724internal_port_must_match_externalInternal and external port value must be the same
725only_permanent_leases_supportedThe NAT implementation only supports permanent -lease times on port mappings
726remote_host_must_be_wildcardRemoteHost must be a wildcard and cannot be a -specific IP addres or DNS name
727external_port_must_be_wildcardExternalPort must be a wildcard and cannot be a -specific port
-

The UPnP errors are declared in the libtorrent::upnp_errors namespace.

-
-

translating error codes

-

The error_code::message() function will typically return a localized error string, -for system errors. That is, errors that belong to the generic or system category.

-

Errors that belong to the libtorrent error category are not localized however, they -are only available in english. In order to translate libtorrent errors, compare the -error category of the error_code object against libtorrent::libtorrent_category, -and if matches, you know the error code refers to the list above. You can provide -your own mapping from error code to string, which is localized. In this case, you -cannot rely on error_code::message() to generate your strings.

-

The numeric values of the errors are part of the API and will stay the same, although -new error codes may be appended at the end.

-

Here's a simple example of how to translate error codes:

-
-std::string error_code_to_string(boost::system::error_code const& ec)
-{
-        if (ec.category() != libtorrent::libtorrent_category)
-        {
-                return ec.message();
-        }
-        // the error is a libtorrent error
-
-        int code = ec.value();
-        static const char const* swedish[] =
-        {
-                "inget fel",
-                "en fil i torrenten kolliderar med en fil frÂn en annan torrent",
-                "hash check misslyckades",
-                "torrent filen ‰r inte en dictionary",
-                "'info'-nyckeln saknas eller ‰r korrupt i torrentfilen",
-                "'info'-f‰ltet ‰r inte en dictionary",
-                "'piece length' f‰ltet saknas eller ‰r korrupt i torrentfilen",
-                "torrentfilen saknar namnf‰ltet",
-                "ogiltigt namn i torrentfilen (kan vara en attack)",
-                // ... more strings here
-        };
-
-        // use the default error string in case we don't have it
-        // in our translated list
-        if (code < 0 || code >= sizeof(swedish)/sizeof(swedish[0]))
-                return ec.message();
-
-        return swedish[code];
-}
-
-
-
-
-

storage_interface

-

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).

-

The interface looks like this:

-
-struct storage_interface
-{
-        virtual bool initialize(bool allocate_files) = 0;
-        virtual bool has_any_file() = 0;
-        virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0;
-        virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0;
-        virtual int sparse_end(int start) const;
-        virtual bool move_storage(fs::path save_path) = 0;
-        virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0;
-        virtual bool write_resume_data(entry& rd) const = 0;
-        virtual bool move_slot(int src_slot, int dst_slot) = 0;
-        virtual bool swap_slots(int slot1, int slot2) = 0;
-        virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0;
-        virtual bool rename_file(int file, std::string const& new_name) = 0;
-        virtual bool release_files() = 0;
-        virtual bool delete_files() = 0;
-        virtual void finalize_file(int index) {}
-        virtual ~storage_interface() {}
-
-        // non virtual functions
-
-        disk_buffer_pool* disk_pool();
-        void set_error(boost::filesystem::path const& file, error_code const& ec) const;
-        error_code const& error() const;
-        std::string const& error_file() const;
-        void clear_error();
-};
-
-
-

initialize()

-
-
-bool initialize(bool allocate_files) = 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.

-
-
-

has_any_file()

-
-
-virtual bool has_any_file() = 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.

-
-
-

readv() writev()

-
-
-int readv(file::iovec_t const* buf, int slot, int offset, int num_bufs) = 0;
-int write(const char* buf, int slot, int offset, int size) = 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.

-
-
-

sparse_end()

-
-
-int sparse_end(int start) const;
-
-
-

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.

-
-
-

move_storage()

-
-
-bool move_storage(fs::path save_path) = 0;
-
-
-

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().

-

Returning false indicates an error occurred.

-
-
-

verify_resume_data()

-
-
-bool verify_resume_data(lazy_entry const& rd, error_code& error) = 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.

-
-
-

write_resume_data()

-
-
-bool write_resume_data(entry& rd) const = 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.

-
-
-

move_slot()

-
-
-bool move_slot(int src_slot, int dst_slot) = 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.

-
-
-

swap_slots()

-
-
-bool swap_slots(int slot1, int slot2) = 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.

-
-
-

swap_slots3()

-
-
-bool swap_slots3(int slot1, int slot2, int slot3) = 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.

-
-
-

rename_file()

-
-
-bool rename_file(int file, std::string const& new_name) = 0;
-
-
-

Rename file with index file to the thame new_name. If there is an error, -true should be returned.

-
-
-

release_files()

-
-
-bool release_files() = 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.

-
-
-

delete_files()

-
-
-bool delete_files() = 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();
-};
-
-
-
-

finalize_file()

-
-
-virtual void finalize_file(int index);
-
-
-

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.

-
-
- -
-

queuing

-

libtorrent supports queuing. Which means it makes sure that a limited number of -torrents are being downloaded at any given time, and once a torrent is completely -downloaded, the next in line is started.

-

Torrents that are auto managed are subject to the queuing and the active torrents -limits. To make a torrent auto managed, set auto_managed to true when adding the -torrent (see add_torrent()).

-

The limits of the number of downloading and seeding torrents are controlled via -active_downloads, active_seeds and active_limit in session_settings. -These limits takes non auto managed torrents into account as well. If there are -more non-auto managed torrents being downloaded than the active_downloads -setting, any auto managed torrents will be queued until torrents are removed so -that the number drops below the limit.

-

The default values are 8 active downloads and 5 active seeds.

-

At a regular interval, torrents are checked if there needs to be any re-ordering of -which torrents are active and which are queued. This interval can be controlled via -auto_manage_interval in session_settings. It defaults to every 30 seconds.

-

For queuing to work, resume data needs to be saved and restored for all torrents. -See save_resume_data().

-
-

downloading

-

Torrents that are currently being downloaded or incomplete (with bytes still to download) -are queued. The torrents in the front of the queue are started to be actively downloaded -and the rest are ordered with regards to their queue position. Any newly added torrent -is placed at the end of the queue. Once a torrent is removed or turns into a seed, its -queue position is -1 and all torrents that used to be after it in the queue, decreases their -position in order to fill the gap.

-

The queue positions are always in a sequence without any gaps.

-

Lower queue position means closer to the front of the queue, and will be started sooner than -torrents with higher queue positions.

-

To query a torrent for its position in the queue, or change its position, see: -queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom().

-
-
-

seeding

-

Auto managed seeding torrents are rotated, so that all of them are allocated a fair -amount of seeding. Torrents with fewer completed seed cycles are prioritized for -seeding. A seed cycle is completed when a torrent meets either the share ratio limit -(uploaded bytes / downloaded bytes), the share time ratio (time seeding / time -downloaing) or seed time limit (time seeded).

-

The relevant settings to control these limits are share_ratio_limit, -seed_time_ratio_limit and seed_time_limit in session_settings.

-
-
-
-

fast resume

-

The fast resume mechanism is a way to remember which pieces are downloaded -and where they are put between sessions. You can generate fast resume data by -calling save_resume_data() on torrent_handle. You can -then save this data to disk and use it when resuming the torrent. libtorrent -will not check the piece hashes then, and rely on the information given in the -fast-resume data. The fast-resume data also contains information about which -blocks, in the unfinished pieces, were downloaded, so it will not have to -start from scratch on the partially downloaded pieces.

-

To use the fast-resume data you simply give it to add_torrent(), and it -will skip the time consuming checks. It may have to do the checking anyway, if -the fast-resume data is corrupt or doesn't fit the storage for that torrent, -then it will not trust the fast-resume data and just do the checking.

-
-

file format

-

The file format is a bencoded dictionary containing the following fields:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
file-formatstring: "libtorrent resume file"
file-versioninteger: 1
info-hashstring, the info hash of the torrent this data is saved for.
blocks per pieceinteger, the number of blocks per piece. Must be: piece_size -/ (16 * 1024). Clamped to be within the range [1, 256]. It -is the number of blocks per (normal sized) piece. Usually -each block is 16 * 1024 bytes in size. But if piece size is -greater than 4 megabytes, the block size will increase.
piecesA string with piece flags, one character per piece. -Bit 1 means we have that piece. -Bit 2 means we have verified that this piece is correct. -This only applies when the torrent is in seed_mode.
slots

list of integers. The list maps slots to piece indices. It -tells which piece is on which slot. If piece index is -2 it -means it is free, that there's no piece there. If it is -1, -means the slot isn't allocated on disk yet. The pieces have -to meet the following requirement:

-

If there's a slot at the position of the piece index, -the piece must be located in that slot.

-
total_uploadedinteger. The number of bytes that have been uploaded in -total for this torrent.
total_downloadedinteger. The number of bytes that have been downloaded in -total for this torrent.
active_timeinteger. The number of seconds this torrent has been active. -i.e. not paused.
seeding_timeinteger. The number of seconds this torrent has been active -and seeding.
num_seedsinteger. An estimate of the number of seeds on this torrent -when the resume data was saved. This is scrape data or based -on the peer list if scrape data is unavailable.
num_downloadersinteger. An estimate of the number of downloaders on this -torrent when the resume data was last saved. This is used as -an initial estimate until we acquire up-to-date scrape info.
upload_rate_limitinteger. In case this torrent has a per-torrent upload rate -limit, this is that limit. In bytes per second.
download_rate_limitinteger. The download rate limit for this torrent in case -one is set, in bytes per second.
max_connectionsinteger. The max number of peer connections this torrent -may have, if a limit is set.
max_uploadsinteger. The max number of unchoked peers this torrent may -have, if a limit is set.
seed_modeinteger. 1 if the torrent is in seed mode, 0 otherwise.
file_prioritylist of integers. One entry per file in the torrent. Each -entry is the priority of the file with the same index.
piece_prioritystring of bytes. Each byte is interpreted as an integer and -is the priority of that piece.
auto_managedinteger. 1 if the torrent is auto managed, otherwise 0.
sequential_downloadinteger. 1 if the torrent is in sequential download mode, -0 otherwise.
pausedinteger. 1 if the torrent is paused, 0 otherwise.
trackerslist of lists of strings. The top level list lists all -tracker tiers. Each second level list is one tier of -trackers.
mapped_fileslist of strings. If any file in the torrent has been -renamed, this entry contains a list of all the filenames. -In the same order as in the torrent file.
url-listlist of strings. List of url-seed URLs used by this torrent. -The urls are expected to be properly encoded and not contain -any illegal url characters.
httpseedslist of strings. List of httpseed URLs used by this torrent. -The urls are expected to be properly encoded and not contain -any illegal url characters.
merkle treestring. In case this torrent is a merkle torrent, this is a -string containing the entire merkle tree, all nodes, -including the root and all leaves. The tree is not -necessarily complete, but complete enough to be able to send -any piece that we have, indicated by the have bitmask.
peers

list of dictionaries. Each dictionary has the following -layout:

- ---- - - - - - - - - -
ipstring, the ip address of the peer. This is -not a binary representation of the ip -address, but the string representation. It -may be an IPv6 string or an IPv4 string.
portinteger, the listen port of the peer
-

These are the local peers we were connected to when this -fast-resume data was saved.

-
unfinished

list of dictionaries. Each dictionary represents an -piece, and has the following layout:

- ---- - - - - - - - - - - - -
pieceinteger, the index of the piece this entry -refers to.
bitmaskstring, a binary bitmask representing the -blocks that have been downloaded in this -piece.
adler32The adler32 checksum of the data in the -blocks specified by bitmask.
-
file sizeslist where each entry corresponds to a file in the file list -in the metadata. Each entry has a list of two values, the -first value is the size of the file in bytes, the second -is the time stamp when the last time someone wrote to it. -This information is used to compare with the files on disk. -All the files must match exactly this information in order -to consider the resume data as current. Otherwise a full -re-check is issued.
allocationThe allocation mode for the storage. Can be either full -or compact. If this is full, the file sizes and -timestamps are disregarded. Pieces are assumed not to have -moved around even if the files have been modified after the -last resume data checkpoint.
-
-
-
-

threads

-

libtorrent starts 2 or 3 threads.

-
-
    -
  • The first thread is the main thread that will sit -idle in a select() call most of the time. This thread runs the main loop -that will send and receive data on all connections.
  • -
  • The second thread is the disk I/O thread. All disk read and write operations -are passed to this thread and messages are passed back to the main thread when -the operation completes. The disk thread also verifies the piece hashes.
  • -
  • The third and forth threads are spawned by asio on systems that don't support -non-blocking host name resolution to simulate non-blocking getaddrinfo().
  • -
-
-
-
-

storage allocation

-

There are three modes in which storage (files on disk) are allocated in libtorrent.

-
    -
  1. The traditional full allocation mode, where the entire files are filled up with -zeros before anything is downloaded. libtorrent will look for sparse files support -in the filesystem that is used for storage, and use sparse files or file system -zero fill support if present. This means that on NTFS, full allocation mode will -only allocate storage for the downloaded pieces.
  2. -
  3. The compact allocation mode, where only files are allocated for actual -pieces that have been downloaded.
  4. -
  5. The sparse allocation, sparse files are used, and pieces are downloaded directly -to where they belong. This is the recommended (and default) mode.
  6. -
-

The allocation mode is selected when a torrent is started. It is passed as an -argument to session::add_torrent() (see add_torrent()).

-

The decision to use full allocation or compact allocation typically depends on whether -any files have priority 0 and if the filesystem supports sparse files.

-
-

sparse allocation

-

On filesystems that supports sparse files, this allocation mode will only use -as much space as has been downloaded.

-
-
    -
  • It does not require an allocation pass on startup.
  • -
  • It supports skipping files (setting prioirty to 0 to not download).
  • -
  • Fast resume data will remain valid even when file time stamps are out of date.
  • -
-
-
-
-

full allocation

-

When a torrent is started in full allocation mode, the disk-io thread (see threads) -will make sure that the entire storage is allocated, and fill any gaps with zeros. -This will be skipped if the filesystem supports sparse files or automatic zero filling. -It will of course still check for existing pieces and fast resume data. The main -drawbacks of this mode are:

-
-
    -
  • It may take longer to start the torrent, since it will need to fill the files -with zeros on some systems. This delay is linearly dependent on the size of -the download.
  • -
  • The download may occupy unnecessary disk space between download sessions. In case -sparse files are not supported.
  • -
  • Disk caches usually perform extremely poorly with random access to large files -and may slow down a download considerably.
  • -
-
-

The benefits of this mode are:

-
-
    -
  • Downloaded pieces are written directly to their final place in the files and the -total number of disk operations will be fewer and may also play nicer to -filesystems' file allocation, and reduce fragmentation.
  • -
  • No risk of a download failing because of a full disk during download. Unless -sparse files are being used.
  • -
  • The fast resume data will be more likely to be usable, regardless of crashes or -out of date data, since pieces won't move around.
  • -
  • Can be used with prioritizing files to 0.
  • -
-
-
-
-

compact allocation

-

The compact allocation will only allocate as much storage as it needs to keep the -pieces downloaded so far. This means that pieces will be moved around to be placed -at their final position in the files while downloading (to make sure the completed -download has all its pieces in the correct place). So, the main drawbacks are:

-
-
    -
  • More disk operations while downloading since pieces are moved around.
  • -
  • Potentially more fragmentation in the filesystem.
  • -
  • Cannot be used while having files with priority 0.
  • -
-
-

The benefits though, are:

-
-
    -
  • No startup delay, since the files doesn't need allocating.
  • -
  • The download will not use unnecessary disk space.
  • -
  • Disk caches perform much better than in full allocation and raises the download -speed limit imposed by the disk.
  • -
  • Works well on filesystems that doesn't support sparse files.
  • -
-
-

The algorithm that is used when allocating pieces and slots isn't very complicated. -For the interested, a description follows.

-

storing a piece:

-
    -
  1. let A be a newly downloaded piece, with index n.
  2. -
  3. let s be the number of slots allocated in the file we're -downloading to. (the number of pieces it has room for).
  4. -
  5. if n >= s then allocate a new slot and put the piece there.
  6. -
  7. if n < s then allocate a new slot, move the data at -slot n to the new slot and put A in slot n.
  8. -
-

allocating a new slot:

-
    -
  1. if there's an unassigned slot (a slot that doesn't -contain any piece), return that slot index.
  2. -
  3. append the new slot at the end of the file (or find an unused slot).
  4. -
  5. let i be the index of newly allocated slot
  6. -
  7. if we have downloaded piece index i already (to slot j) then
      -
    1. move the data at slot j to slot i.
    2. -
    3. return slot index j as the newly allocated free slot.
    4. -
    -
  8. -
  9. return i as the newly allocated slot.
  10. -
-
-
-
-

extensions

-

These extensions all operates within the extension protocol. The -name of the extension is the name used in the extension-list packets, -and the payload is the data in the extended message (not counting the -length-prefix, message-id nor extension-id).

-

Note that since this protocol relies on one of the reserved bits in the -handshake, it may be incompatible with future versions of the mainline -bittorrent client.

-

These are the extensions that are currently implemented.

-
-

metadata from peers

-

Extension name: "LT_metadata"

-

The point with this extension is that you don't have to distribute the -metadata (.torrent-file) separately. The metadata can be distributed -through the bittorrent swarm. The only thing you need to download such -a torrent is the tracker url and the info-hash of the torrent.

-

It works by assuming that the initial seeder has the metadata and that -the metadata will propagate through the network as more peers join.

-

There are three kinds of messages in the metadata extension. These packets -are put as payload to the extension message. The three packets are:

-
-
    -
  • request metadata
  • -
  • metadata
  • -
  • don't have metadata
  • -
-
-

request metadata:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
uint8_tmsg_typeDetermines the kind of message this is -0 means 'request metadata'
uint8_tstartThe start of the metadata block that -is requested. It is given in 256:ths -of the total size of the metadata, -since the requesting client don't know -the size of the metadata.
uint8_tsizeThe size of the metadata block that is -requested. This is also given in -256:ths of the total size of the -metadata. The size is given as size-1. -That means that if this field is set -0, the request wants one 256:th of the -metadata.
-

metadata:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
uint8_tmsg_type1 means 'metadata'
int32_ttotal_sizeThe total size of the metadata, given -in number of bytes.
int32_toffsetThe offset of where the metadata block -in this message belongs in the final -metadata. This is given in bytes.
uint8_t[]metadataThe actual metadata block. The size of -this part is given implicit by the -length prefix in the bittorrent -protocol packet.
-

Don't have metadata:

- ----- - - - - - - - - - - - - -
sizenamedescription
uint8_tmsg_type2 means 'I don't have metadata'. -This message is sent as a reply to a -metadata request if the the client -doesn't have any metadata.
-
-
-

HTTP seeding

-

There are two kinds of HTTP seeding. One with that assumes a smart -(and polite) client and one that assumes a smart server. These -are specified in BEP 19 and BEP 17 respectively.

-

libtorrent supports both. In the libtorrent source code and API, -BEP 19 urls are typically referred to as url seeds and BEP 17 -urls are typically referred to as HTTP seeds.

-

The libtorrent implementation of BEP 19 assumes that, if the URL ends with a slash -('/'), the filename should be appended to it in order to request pieces from -that file. The way this works is that if the torrent is a single-file torrent, -only that filename is appended. If the torrent is a multi-file torrent, the -torrent's name '/' the file name is appended. This is the same directory -structure that libtorrent will download torrents into.

-
-
-
-

filename checks

-

Boost.Filesystem will by default check all its paths to make sure they conform -to filename requirements on many platforms. If you don't want this check, you can -set it to either only check for native filesystem requirements or turn it off -altogether. You can use:

-
-boost::filesystem::path::default_name_check(boost::filesystem::native);
-
-

for example. For more information, see the Boost.Filesystem docs.

-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/manual.rst b/libtorrent_utp/docs/manual.rst deleted file mode 100644 index b35afa729..000000000 --- a/libtorrent_utp/docs/manual.rst +++ /dev/null @@ -1,7898 +0,0 @@ -============================ -libtorrent API Documentation -============================ - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -overview -======== - -The interface of libtorrent consists of a few classes. The main class is -the ``session``, it contains the main loop that serves all torrents. - -The basic usage is as follows: - -* construct a session -* load session state from settings file (see `load_state() save_state()`_) -* start extensions (see `add_extension()`_). -* start DHT, LSD, UPnP, NAT-PMP etc (see `start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running()`_ - `start_lsd() stop_lsd()`_, `start_upnp() stop_upnp()`_ and `start_natpmp() stop_natpmp()`_) -* parse .torrent-files and add them to the session (see `bdecode() bencode()`_ and `add_torrent()`_) -* main loop (see session_) - - * query the torrent_handles for progress (see torrent_handle_) - * query the session for information - * add and remove torrents from the session at run-time - -* save resume data for all torrent_handles (optional, see - `save_resume_data()`_) -* save session state (see `load_state() save_state()`_) -* destruct session object - -Each class and function is described in this manual. - -For a description on how to create torrent files, see make_torrent_. - -.. _make_torrent: make_torrent.html - -things to keep in mind -====================== - -A common problem developers are facing is torrents stopping without explanation. -Here is a description on which conditions libtorrent will stop your torrents, -how to find out about it and what to do about it. - -Make sure to keep track of the paused state, the error state and the upload -mode of your torrents. By default, torrents are auto-managed, which means -libtorrent will pause them, unpause them, scrape them and take them out -of upload-mode automatically. - -Whenever a torrent encounters a fatal error, it will be stopped, and the -``torrent_status::error`` will describe the error that caused it. If a torrent -is auto managed, it is scraped periodically and paused or resumed based on -the number of downloaders per seed. This will effectively seed torrents that -are in the greatest need of seeds. - -If a torrent hits a disk write error, it will be put into upload mode. This -means it will not download anything, but only upload. The assumption is that -the write error is caused by a full disk or write permission errors. If the -torrent is auto-managed, it will periodically be taken out of the upload -mode, trying to write things to the disk again. This means torrent will recover -from certain disk errors if the problem is resolved. If the torrent is not -auto managed, you have to call `set_upload_mode()`_ to turn -downloading back on again. - - -network primitives -================== - -There are a few typedefs in the ``libtorrent`` namespace which pulls -in network types from the ``asio`` namespace. These are:: - - typedef asio::ip::address address; - typedef asio::ip::address_v4 address_v4; - typedef asio::ip::address_v6 address_v6; - using asio::ip::tcp; - using asio::ip::udp; - -These are declared in the ```` header. - -The ``using`` statements will give easy access to:: - - tcp::endpoint - udp::endpoint - -Which are the endpoint types used in libtorrent. An endpoint is an address -with an associated port. - -For documentation on these types, please refer to the `asio documentation`_. - -.. _`asio documentation`: http://asio.sourceforge.net/asio-0.3.8/doc/asio/reference.html - -session -======= - -The ``session`` class has the following synopsis:: - - class session: public boost::noncopyable - { - - session(fingerprint const& print - = libtorrent::fingerprint( - "LT", 0, 1, 0, 0) - , int flags = start_default_features - | add_default_plugins - , int alert_mask = alert::error_notification); - - session( - fingerprint const& print - , std::pair listen_port_range - , char const* listen_interface = 0 - , int flags = start_default_features - | add_default_plugins - , int alert_mask = alert::error_notification); - - enum save_state_flags_t - { - save_settings = 0x001, - save_dht_settings = 0x002, - save_dht_state = 0x004, - save_proxy = 0x008, - save_i2p_proxy = 0x010, - save_encryption_settings = 0x020, - save_as_map = 0x040, - }; - - void load_state(lazy_entry const& e); - void save_state(entry& e, boost::uint32_t flags) const; - - torrent_handle add_torrent( - add_torrent_params const& params); - torrent_handle add_torrent( - add_torrent_params const& params - , error_code& ec); - - void pause(); - void resume(); - - session_proxy abort(); - - enum options_t - { - none = 0, - delete_files = 1 - }; - - enum session_flags_t - { - add_default_plugins = 1, - start_default_features = 2 - }; - - void remove_torrent(torrent_handle const& h - , int options = none); - torrent_handle find_torrent(sha_hash const& ih); - std::vector get_torrents() const; - - void set_settings(session_settings const& settings); - void set_pe_settings(pe_settings const& settings); - - void set_upload_rate_limit(int bytes_per_second); - int upload_rate_limit() const; - void set_download_rate_limit(int bytes_per_second); - int download_rate_limit() const; - - void set_local_upload_rate_limit(int bytes_per_second); - int local_upload_rate_limit() const; - void set_local_download_rate_limit(int bytes_per_second); - int local_download_rate_limit() const; - - void set_max_uploads(int limit); - void set_max_connections(int limit); - int max_connections() const; - void set_max_half_open_connections(int limit); - int max_half_open_connections() const; - - void set_proxy(proxy_settings const& s); - proxy_settings proxy() const; - - int num_uploads() const; - int num_connections() const; - - void load_asnum_db(char const* file); - void load_asnum_db(wchar_t const* file); - void load_country_db(char const* file); - void load_country_db(wchar_t const* file); - int as_for_ip(address const& adr); - - void set_ip_filter(ip_filter const& f); - ip_filter get_ip_filter() const; - - session_status status() const; - cache_status get_cache_status() const; - - bool is_listening() const; - unsigned short listen_port() const; - - enum { listen_reuse_address = 1 }; - bool listen_on( - std::pair const& port_range - , char const* interface = 0 - , int flags = 0); - - std::auto_ptr pop_alert(); - alert const* wait_for_alert(time_duration max_wait); - void set_alert_mask(int m); - size_t set_alert_queue_size_limit( - size_t queue_size_limit_); - - void add_extension(boost::function< - boost::shared_ptr(torrent*)> ext); - - void start_dht(); - void stop_dht(); - void set_dht_settings( - dht_settings const& settings); - entry dht_state() const; - void add_dht_node(std::pair const& node); - void add_dht_router(std::pair const& node); - bool is_dht_running() const; - - void start_lsd(); - void stop_lsd(); - - upnp* start_upnp(); - void stop_upnp(); - - natpmp* start_natpmp(); - void stop_natpmp(); - }; - -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. - -session() ---------- - - :: - - session(fingerprint const& print - = libtorrent::fingerprint("LT", 0, 1, 0, 0) - , int flags = start_default_features - | add_default_plugins - , int alert_mask = alert::error_notification); - - session(fingerprint const& print - , std::pair listen_port_range - , char const* listen_interface = 0 - , int flags = start_default_features - | add_default_plugins - , int alert_mask = alert::error_notification); - -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() ----------- - -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()``. - -load_state() save_state() -------------------------- - - :: - - void load_state(lazy_entry const& e); - void save_state(entry& e, boost::uint32_t flags) const; - -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). These are the possible flags. A flag that's set, means those settings are saved:: - - enum save_state_flags_t - { - save_settings = 0x001, - save_dht_settings = 0x002, - save_dht_state = 0x004, - save_proxy = 0x008, - save_i2p_proxy = 0x010, - save_encryption_settings = 0x020, - save_as_map = 0x040, - }; - - -pause() resume() is_paused() ----------------------------- - - :: - - void pause(); - void resume(); - bool is_paused() const; - -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. - -abort() -------- - - :: - - session_proxy abort(); - -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() - }; - - -add_torrent() -------------- - - :: - - typedef boost::function const&) storage_constructor_type; - - struct add_torrent_params - { - add_torrent_params(storage_constructor_type s); - - int version; - boost::intrusive_ptr ti; - char const* tracker_url; - sha1_hash info_hash; - char const* name; - fs::path save_path; - std::vector* resume_data; - storage_mode_t storage_mode; - bool paused; - bool auto_managed; - bool duplicate_is_error; - storage_constructor_type storage; - void* userdata; - bool seed_mode; - bool override_resume_data; - bool upload_mode; - std::vector const* file_priorities; - bool share_mode; - std::string trackerid; - }; - - torrent_handle add_torrent(add_torrent_params const& params); - torrent_handle add_torrent(add_torrent_params const& params - , error_code& ec); - -You add torrents through the ``add_torrent()`` function where you give an -object with all the parameters. - -The overload that does not take an ``error_code`` throws an exception on -error and is not available when building without exception support. - -The only mandatory parameter is ``save_path`` which is the directory where you -want the files to be saved. You also need to specify either the ``ti`` (the -torrent file) or ``info_hash`` (the info hash of the torrent). If you 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 0 in case no -name should be assigned to the torrent. In case it's not 0, the name is used for -the torrent as long as it doesn't have metadata. See ``torrent_handle::name``. - -If the torrent doesn't have a tracker, but relies on the DHT to find peers, the -``tracker_url`` can be 0, otherwise you might specify a tracker url that tracks this -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. - -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()``. - -The ``storage_mode`` parameter refers to the layout of the storage for this torrent. -There are 3 different modes: - -storage_mode_sparse - 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_allocate - 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_compact - The storage will grow as more pieces are downloaded, and pieces - are rearranged to finally be in their correct places once the entire torrent has been - downloaded. - -For more information, see `storage allocation`_. - -``paused`` is a boolean that 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. - -If you pass in resume data, the paused state of the torrent when the resume data -was saved will override the paused state you pass in here. You can override this -by setting ``override_resume_data``. - -If ``auto_managed`` is true, this 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``. - -``storage`` 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`_. - -The ``userdata`` parameter is optional and will be passed on to the extension -constructor functions, if any (see `add_extension()`_). - -If ``seed_mode`` is set to true, 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 ``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. - -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 ``override_resume_data`` is set to true, the ``paused`` and ``auto_managed`` -state of the torrent are not loaded from the resume data, but the states requested -by this ``add_torrent_params`` will override it. - -If ``upload_mode`` is set to true, 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. - -``share_mode`` 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 -merlely 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. - -``file_priorities`` can be set to control the initial file priorities when adding -a torrent. The semantics are the same as for ``torrent_handle::prioritize_files()``. - -``version`` is filled in by the constructor and should be left untouched. It -is used for forward binary compatibility. - -``trackerid`` is 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. - -remove_torrent() ----------------- - - :: - - void remove_torrent(torrent_handle const& h, int options = none); - -``remove_torrent()`` will close all peer connections associated with the torrent and tell -the tracker that we've stopped participating in the swarm. The optional second argument -``options`` can be used to delete all the files downloaded by this torrent. To do this, 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. - -find_torrent() get_torrents() ------------------------------ - - :: - - torrent_handle find_torrent(sha_hash const& ih); - std::vector get_torrents() const; - -``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. - -load_asnum_db() load_country_db() as_for_ip() ---------------------------------------------- - - :: - - void load_asnum_db(char const* file); - void load_asnum_db(wchar_t const* file); - void load_country_db(char const* file); - void load_country_db(wchar_t const* file); - int as_for_ip(address const& adr); - -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. - -The ``wchar_t`` overloads are for wide character paths. - -.. _`MaxMind ASN database`: http://www.maxmind.com/app/asnum -.. _`MaxMind GeoIP database`: http://www.maxmind.com/app/geolitecountry - -set_ip_filter() ---------------- - - :: - - void set_ip_filter(ip_filter const& filter); - -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() ---------------- - - :: - - ip_filter get_ip_filter() const; - -Returns the ip_filter currently in the session. See ip_filter_. - - -status() --------- - - :: - - session_status status() const; - -``status()`` returns session wide-statistics and status. The ``session_status`` -struct has the following members:: - - struct dht_lookup - { - char const* type; - int outstanding_requests; - int timeouts; - int responses; - int branch_factor; - }; - - struct utp_status - { - int num_idle; - int num_syn_sent; - int num_connected; - int num_fin_sent; - int num_close_wait; - }; - - struct session_status - { - bool has_incoming_connections; - - int upload_rate; - int download_rate; - size_type total_download; - size_type total_upload; - - int payload_upload_rate; - int payload_download_rate; - size_type total_payload_download; - size_type total_payload_upload; - - int ip_overhead_upload_rate; - int ip_overhead_download_rate; - size_type total_ip_overhead_download; - size_type total_ip_overhead_upload; - - int dht_upload_rate; - int dht_download_rate; - size_type total_dht_download; - size_type total_dht_upload; - - int tracker_upload_rate; - int tracker_download_rate; - size_type total_tracker_download; - size_type total_tracker_upload; - - size_type total_redundant_bytes; - size_type total_failed_bytes; - - int num_peers; - int num_unchoked; - int allowed_upload_slots; - - int optimistic_unchoke_counter; - int unchoke_counter; - - int dht_nodes; - int dht_node_cache; - int dht_torrents; - size_type dht_global_nodes; - std::vector active_requests; - int dht_total_allocations; - - utp_status utp_stats; - }; - -``has_incoming_connections`` is 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. - -``upload_rate``, ``download_rate`` are the total download and upload rates accumulated -from all torrents. This includes bittorrent protocol, DHT and an estimated TCP/IP -protocol overhead. - -``total_download`` and ``total_upload`` are the total number of bytes downloaded and -uploaded to and from all torrents. This also includes all the protocol overhead. - -``payload_download_rate`` and ``payload_upload_rate`` is the rate of the payload -down- and upload only. - -``total_payload_download`` and ``total_payload_upload`` is 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. - -``ip_overhead_upload_rate``, ``ip_overhead_download_rate``, ``total_ip_overhead_download`` -and ``total_ip_overhead_upload`` is the estimated TCP/IP overhead in each direction. - -``dht_upload_rate``, ``dht_download_rate``, ``total_dht_download`` and ``total_dht_upload`` -is the DHT bandwidth usage. - -``total_redundant_bytes`` is 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. - -``total_failed_bytes`` is the number of bytes that was downloaded which later failed -the hash-check. - -``num_peers`` is 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. - -``num_unchoked`` is the current number of unchoked peers. -``allowed_upload_slots`` is the current allowed number of unchoked peers. - -``optimistic_unchoke_counter`` and ``unchoke_counter`` 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. - -``dht_nodes``, ``dht_node_cache`` and ``dht_torrents`` are 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. - -``dht_torrents`` are the number of torrents tracked by the DHT at the moment. - -``dht_global_nodes`` is an estimation of the total number of nodes in the DHT -network. - -``active_requests`` is a vector of the currently running DHT lookups. - -``dht_total_allocations`` is the number of nodes allocated dynamically for a -particular DHT lookup. This represents roughly the amount of memory used -by the DHT. - -``utp_stats`` contains statistics on the uTP sockets. - -get_cache_status() ------------------- - - :: - - cache_status get_cache_status() const; - -Returns status of the disk cache for this session. - - :: - - struct cache_status - { - size_type blocks_written; - size_type writes; - size_type blocks_read; - size_type blocks_read_hit; - size_type reads; - int cache_size; - int read_cache_size; - int total_used_buffers; - int average_queue_time; - int average_read_time; - int job_queue_length; - }; - -``blocks_written`` is the total number of 16 KiB blocks written to disk -since this session was started. - -``writes`` is 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. - -``blocks_read`` is the number of blocks that were requested from the -bittorrent engine (from peers), that were served from disk or cache. - -``blocks_read_hit`` is the number of blocks that were served from cache. - -The ratio ``blocks_read_hit`` / ``blocks_read`` is the cache hit ratio -for the read cache. - -``cache_size`` is the number of 16 KiB blocks currently in the disk cache. -This includes both read and write cache. - -``read_cache_size`` is the number of 16KiB blocks in the read cache. - -``total_used_buffers`` is 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. - -``average_queue_time`` is the number of microseconds an average disk I/O job -has to wait in the job queue before it get processed. - -``average_read_time`` is the number of microseconds a read job takes to -wait in the queue and complete, in microseconds. This only includes -cache misses. - -``job_queue_length`` is the number of jobs in the job queue. - -get_cache_info() ----------------- - - :: - - void get_cache_info(sha1_hash const& ih - , std::vector& ret) const; - -``get_cache_info()`` 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``). - - :: - - struct cached_piece_info - { - int piece; - std::vector blocks; - ptime last_use; - enum kind_t { read_cache = 0, write_cache = 1 }; - kind_t kind; - }; - -``piece`` is the piece index for this cache entry. - -``blocks`` has 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. - -``last_use`` is 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. - -``kind`` specifies if this piece is part of the read cache or the write cache. - -is_listening() listen_port() listen_on() ----------------------------------------- - - :: - - bool is_listening() const; - unsigned short listen_port() const; - bool listen_on( - std::pair const& port_range - , char const* interface = 0 - , int flags = 0); - -``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 true -if it managed to open the socket, and false if it failed. 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. - -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). - -set_alert_mask() ----------------- - - :: - - void set_alert_mask(int m); - -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 alerts_ for mor information on the alert categories. - -pop_alert() wait_for_alert() set_alert_queue_size_limit() ---------------------------------------------------------- - - :: - - std::auto_ptr pop_alert(); - alert const* wait_for_alert(time_duration max_wait); - size_t set_alert_queue_size_limit(size_t queue_size_limit_); - -``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_. - -``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. - -In the python binding, ``wait_for_alert`` takes the number of milliseconds to wait as an integer. - -``set_alert_queue_size_limit()`` you can specify how many alerts can be awaiting for dispatching. -If this limit is reached, new incoming alerts can not be received until alerts are popped -by calling ``pop_alert``. Default value is 1000. - -``save_resume_data_alert`` and ``save_resume_data_failed_alert`` are always posted, regardelss -of the alert mask. - -add_extension() ---------------- - - :: - - void add_extension(boost::function< - boost::shared_ptr(torrent*, void*)> ext); - -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 - -set_settings() set_pe_settings() --------------------------------- - - :: - - void set_settings(session_settings const& settings); - void set_pe_settings(pe_settings const& settings); - -Sets the session settings and the packet encryption settings respectively. -See session_settings_ and pe_settings_ for more information on available -options. - - -set_proxy() proxy() -------------------- - - :: - - void set_proxy(proxy_settings const& s); - proxy_setting proxy() 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`_. - - -set_i2p_proxy() i2p_proxy() ---------------------------- - - :: - - void set_i2p_proxy(proxy_settings const&); - proxy_settings const& i2p_proxy(); - -``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 - - -start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running() ----------------------------------------------------------------------- - - :: - - void start_dht(entry const& startup_state); - void stop_dht(); - void set_dht_settings(dht_settings const& settings); - entry dht_state() const; - bool is_dht_running() const; - -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. The -struct has the following members:: - - struct dht_settings - { - int max_peers_reply; - int search_branching; - int max_fail_count; - }; - -``max_peers_reply`` is the maximum number of peers the node will send in -response to a ``get_peers`` message from another node. - -``search_branching`` is 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. - -``max_fail_count`` is 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. - -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. - -``is_dht_running()`` returns true if the DHT support has been started and false -otherwise. - - -add_dht_node() add_dht_router() -------------------------------- - - :: - - void add_dht_node(std::pair const& node); - void add_dht_router(std::pair const& node); - -``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``. - - -start_lsd() stop_lsd() ----------------------- - - :: - - void start_lsd(); - void stop_lsd(); - -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. - -start_upnp() stop_upnp() ------------------------- - - :: - - upnp* start_upnp(); - void stop_upnp(); - -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. - -start_natpmp() stop_natpmp() ----------------------------- - - :: - - natpmp* start_natpmp(); - void stop_natpmp(); - -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. - - -entry -===== - -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. This is its synopsis:: - - class entry - { - public: - - typedef std::map dictionary_type; - typedef std::string string_type; - typedef std::list list_type; - typedef size_type integer_type; - - enum data_type - { - int_t, - string_t, - list_t, - dictionary_t, - undefined_t - }; - - data_type type() const; - - entry(dictionary_type const&); - entry(string_type const&); - entry(list_type const&); - entry(integer_type const&); - - entry(); - entry(data_type t); - entry(entry const& e); - ~entry(); - - void operator=(entry const& e); - void operator=(dictionary_type const&); - void operator=(string_type const&); - void operator=(list_type const&); - void operator=(integer_type const&); - - integer_type& integer(); - integer_type const& integer() const; - string_type& string(); - string_type const& string() const; - list_type& list(); - list_type const& list() const; - dictionary_type& dict(); - dictionary_type const& dict() const; - - // these functions requires that the entry - // is a dictionary, otherwise they will throw - entry& operator[](char const* key); - entry& operator[](std::string const& key); - entry const& operator[](char const* key) const; - entry const& operator[](std::string const& key) const; - entry* find_key(char const* key); - entry const* find_key(char const* key) const; - - void print(std::ostream& os, int indent = 0) const; - }; - -*TODO: finish documentation of entry.* - -integer() string() list() dict() type() ---------------------------------------- - - :: - - integer_type& integer(); - integer_type const& integer() const; - string_type& string(); - string_type const& string() const; - list_type& list(); - list_type const& list() const; - dictionary_type& dict(); - dictionary_type const& dict() 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. - -The ``print()`` function is there for debug purposes only. - -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:: - - 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:: - - 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. - - -operator[] ----------- - - :: - - entry& operator[](char const* key); - entry& operator[](std::string const& key); - entry const& operator[](char const* key) const; - entry const& operator[](std::string const& key) const; - -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``. - - -find_key() ----------- - - :: - - entry* find_key(char const* key); - entry const* find_key(char const* key) const; - -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. - - -torrent_info -============ - -In previous versions of libtorrent, this class was also used for creating -torrent files. This functionality has been moved to ``create_torrent``, see -make_torrent_. - -The ``torrent_info`` has the following synopsis:: - - class torrent_info - { - public: - - // these constructors throws exceptions on error - torrent_info(sha1_hash const& info_hash, int flags = 0); - torrent_info(lazy_entry const& torrent_file, int flags = 0); - torrent_info(char const* buffer, int size, int flags = 0); - torrent_info(boost::filesystem::path const& filename, int flags = 0); - torrent_info(boost::filesystem::wpath const& filename, int flags = 0); - - // these constructors sets the error code on error - torrent_info(sha1_hash const& info_hash, error_code& ec, 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(fs::path const& filename, error_code& ec, int flags = 0); - torrent_info(fs::wpath const& filename, error_code& ec, int flags = 0); - - void add_tracker(std::string const& url, int tier = 0); - std::vector const& trackers() const; - - file_storage const& files() const; - file_storage const& orig_files() const; - - void remap_files(file_storage const& f); - - void rename_file(int index, std::string const& new_filename); - void rename_file(int index, std::wstring const& new_filename); - - typedef file_storage::iterator file_iterator; - typedef file_storage::reverse_iterator reverse_file_iterator; - - file_iterator begin_files() const; - file_iterator end_files() const; - reverse_file_iterator rbegin_files() const; - reverse_file_iterator rend_files() const; - - int num_files() const; - file_entry const& file_at(int index) const; - - std::vector map_block(int piece, size_type offset - , int size) const; - peer_request map_file(int file_index, size_type file_offset - , int size) const; - - bool priv() const; - - void add_url_seed(std::string const& url); - void add_http_seed(std::string const& url); - std::vector const& web_seeds() const; - - size_type total_size() const; - int piece_length() const; - int num_pieces() const; - sha1_hash const& info_hash() const; - std::string const& name() const; - std::string const& comment() const; - std::string const& creator() const; - - std::vector > const& nodes() const; - void add_node(std::pair const& node); - - boost::optional creation_date() const; - - int piece_size(unsigned int index) const; - sha1_hash const& hash_for_piece(unsigned int index) const; - char const* hash_for_piece_ptr(unsigned int index) const; - - boost::shared_array metadata() const; - int metadata_size() const; - }; - -torrent_info() --------------- - - :: - - torrent_info(sha1_hash const& info_hash, int flags = 0); - torrent_info(lazy_entry const& torrent_file, int flags = 0); - torrent_info(char const* buffer, int size, int flags = 0); - torrent_info(boost::filesystem::path const& filename, int flags = 0); - torrent_info(boost::filesystem::wpath const& filename, int flags = 0); - - torrent_info(sha1_hash const& info_hash, error_code& ec, 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(fs::path const& filename, error_code& ec, int flags = 0); - torrent_info(fs::wpath const& filename, error_code& ec, int flags = 0); - -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. - -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. - - -add_tracker() -------------- - - :: - - void add_tracker(std::string const& url, int tier = 0); - -``add_tracker()`` adds a tracker to the announce-list. The ``tier`` determines the order in -which the trackers are to be tried. For more information see `trackers()`_. - -files() orig_files() --------------------- - - :: - - file_storage const& files() const; - file_storage const& orig_files() const; - -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. - -remap_files() -------------- - - :: - - void remap_files(file_storage const& f); - -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. - -rename_file() -------------- - - :: - - void rename_file(int index, std::string const& new_filename); - void rename_file(int index, std::wstring const& new_filename); - -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() orig_files()`_), change the name, and -then use `remap_files()`_. - - -begin_files() end_files() rbegin_files() rend_files() ------------------------------------------------------ - - :: - - file_iterator begin_files() const; - file_iterator end_files() const; - reverse_file_iterator rbegin_files() const; - reverse_file_iterator rend_files() const; - -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 ``file_entry``. - -:: - - struct file_entry - { - std::string filename(); - size_type offset; - size_type size; - size_type file_base; - time_t mtime; - int symlink_index; - int filehash_index; - bool pad_file:1; - bool hidden_attribute:1; - bool executable_attribute:1; - bool symlink_attribute:1; - }; - -The ``filename`` function returns the filename of this file. It does not include the -path, just the leaf name. To get the full path name, use ``file_storage::file_path()``. -The filenames are unicode strings encoded in UTF-8. - -``size`` is 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. - -``file_base`` is 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. - -``mtime`` is the modification time of this file specified in posix time. - -``symlink_index`` is an index into an array of paths in ``file_storage``, or --1 if this file is not a symlink. This field is only used if the ``symlink_attribute`` -is set. To resolve the symlink, call ``file_storage::symlink(e.symlink_index)``. - -``filehash_index`` is an index into an array of sha-1 hashes in ``file_storage``, or --1 if this file doesn't have a hash specified. The hash is the hash of the actual -content of the file, and can be used to potentially find alternative sources for it. -To resolve the hash, use ``file_storage::hash(e)``. - -``pad_file`` is 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. - -``hidden_attribute`` is true if the file was marked as hidden (on windows). - -``executable_attribute`` is true if the file was marked as executable (posix) - -``symlink_attribute`` is 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. - -num_files() file_at() ---------------------- - - :: - - int num_files() const; - file_entry const& file_at(int index) const; - -If you need index-access to files you can use the ``num_files()`` and ``file_at()`` -to access files using indices. - - -map_block() ------------ - - :: - - std::vector map_block(int piece, size_type offset - , int size) const; - -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. - -The file slice struct looks like this:: - - struct file_slice - { - int file_index; - size_type offset; - size_type size; - }; - - -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. - - -map_file() ----------- - - :: - - peer_request map_file(int file_index, size_type file_offset - , int size) const; - -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. -The ``peer_request`` structure looks like this:: - - struct peer_request - { - int piece; - int start; - int length; - bool operator==(peer_request const& r) const; - }; - -``piece`` is the index of the piece in which the range starts. -``start`` is the offset within that piece where the range starts. -``length`` is the size of the range, in bytes. - -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()``. - - -add_url_seed() add_http_seed() ------------------------------- - - :: - - 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; - -``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. - -The ``web_seed_entry`` has the following members:: - - struct web_seed_entry - { - 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()); - - bool operator==(web_seed_entry const& e) const; - bool operator<(web_seed_entry const& e) const; - - std::string url; - type_t type; - std::string auth; - headers_t extra_headers; - - // ... - }; - - -trackers() ----------- - - :: - - std::vector const& trackers() const; - -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. - -:: - - struct announce_entry - { - announce_entry(std::string const& url); - std::string url; - - int next_announce_in() const; - int min_announce_in() const; - - error_code last_error; - - std::string message; - - boost::uint8_t tier; - boost::uint8_t fail_limit; - boost::uint8_t fails; - - enum tracker_source - { - source_torrent = 1, - source_client = 2, - source_magnet_link = 4, - source_tex = 8 - }; - boost::uint8_t source; - - bool verified:1; - bool updating:1; - bool start_sent:1; - bool complete_sent:1; - }; - -``next_announce_in()`` 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. - -If the last time this tracker was contacted, the tracker returned a warning -or error message, ``message`` contains that message. - -``fail_limit`` is the max number of failures to announce to this tracker in -a row, before this tracker is not used anymore. - -``fails`` is the number of times in a row we have failed to announce to this -tracker. - -``source`` is a bitmask specifying which sources we got this tracker from. - -``verified`` is set to true the first time we receive a valid response -from this tracker. - -``updating`` is true while we're waiting for a response from the tracker. - -``start_sent`` is 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. - -``complete_sent`` is set to true when we send a event=completed. - - -total_size() piece_length() piece_size() num_pieces() ------------------------------------------------------ - - :: - - size_type total_size() const; - int piece_length() const; - int piece_size(unsigned int index) const; - int num_pieces() const; - - -``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. - - -hash_for_piece() hash_for_piece_ptr() info_hash() -------------------------------------------------- - - :: - - size_type piece_size(unsigned int index) const; - sha1_hash const& hash_for_piece(unsigned int index) const; - char const* hash_for_piece_ptr(unsigned int index) const; - -``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. For more information on the ``sha1_hash``, see the big_number_ class. -``hash_for_piece_ptr()`` returns a pointer to the 20 byte sha1 digest for the piece. -Note that the string is not null-terminated. - - -name() comment() creation_date() creator() ------------------------------------------- - - :: - - std::string const& name() const; - std::string const& comment() const; - std::string const& creator() const; - boost::optional creation_date() const; - -``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 - -priv() ------- - - :: - - bool priv() const; - -``priv()`` returns true if this torrent is private. i.e., it should not be -distributed on the trackerless network (the kademlia DHT). - - -nodes() -------- - - :: - - std::vector > const& nodes() const; - -If this torrent contains any DHT nodes, they are put in this vector in their original -form (host name and port number). - -add_node() ----------- - - :: - - void add_node(std::pair const& node); - -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. - - -metadata() metadata_size() --------------------------- - - :: - - boost::shared_array metadata() const; - int metadata_size() const; - -``metadata()`` returns a the raw info section of the torrent file. The size -of the metadata is returned by ``metadata_size()``. - - -torrent_handle -============== - -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. - -Its declaration looks like this:: - - struct torrent_handle - { - torrent_handle(); - - enum status_flags_t - { - query_distributed_copies = 1, - query_accurate_download_counters = 2, - query_last_seen_complete = 4 - }; - - torrent_status status(boost::uint32_t flags = 0xffffffff); - void file_progress(std::vector& fp, int flags = 0); - void get_download_queue(std::vector& queue) const; - void get_peer_info(std::vector& v) const; - torrent_info const& get_torrent_info() const; - bool is_valid() const; - - std::string name() const; - - void save_resume_data(int flags = 0) const; - bool need_save_resume_data() const; - void force_reannounce() const; - void force_dht_announce() const; - void force_reannounce(boost::posix_time::time_duration) const; - void scrape_tracker() const; - void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const; - - void set_tracker_login(std::string const& username - , std::string const& password) const; - - std::vector trackers() const; - void replace_trackers(std::vector const&); - void add_tracker(announce_entry const& url); - - void add_url_seed(std::string const& url); - void remove_url_seed(std::string const& url); - std::set url_seeds() const; - - void add_http_seed(std::string const& url); - void remove_http_seed(std::string const& url); - std::set http_seeds() const; - - void set_ratio(float ratio) const; - int max_uploads() const; - void set_max_uploads(int max_uploads) const; - void set_max_connections(int max_connections) const; - int max_connections() const; - void set_upload_limit(int limit) const; - int upload_limit() const; - void set_download_limit(int limit) const; - int download_limit() const; - void set_sequential_download(bool sd) const; - bool is_sequential_download() const; - - int get_peer_upload_limit(tcp::endpoint ip); - int get_peer_download_limit(tcp::endpoint ip); - void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const; - void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const; - - int queue_position() const; - void queue_position_up() const; - void queue_position_down() const; - void queue_position_top() const; - void queue_position_bottom() const; - - void set_priority(int prio) const; - - void use_interface(char const* net_interface) const; - - enum pause_flags_t { graceful_pause = 1 }; - void pause(int flags = 0) const; - void resume() const; - bool is_seed() const; - void force_recheck() const; - void clear_error() const; - void set_upload_mode(bool m) const; - void set_share_mode(bool m) const; - - void flush_cache() const; - - void resolve_countries(bool r); - bool resolve_countries() const; - - enum deadline_flags { alert_when_available = 1 }; - void set_piece_deadline(int index, int deadline, int flags = 0) const; - - void piece_availability(std::vector& avail) const; - 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; - - 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; - - void auto_managed(bool m) const; - - bool set_metadata(char const* buf, int size) const; - - boost::filesystem::path save_path() const; - void move_storage(boost::filesystem::path const& save_path) const; - void move_storage(boost::filesystem::wpath const& save_path) const; - void rename_file(int index, boost::filesystem::path) const; - void rename_file(int index, boost::filesystem::wpath) const; - storage_interface* get_storage_impl() const; - - bool super_seeding() const; - void super_seeding(bool on) const; - - enum flags_t { overwrite_existing = 1 }; - void add_piece(int piece, char const* data, int flags = 0) const; - void read_piece(int piece) const; - - sha1_hash info_hash() const; - - bool operator==(torrent_handle const&) const; - bool operator!=(torrent_handle const&) const; - bool operator<(torrent_handle const&) const; - }; - -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. - -set_piece_deadline() --------------------- - - :: - - enum deadline_flags { alert_when_available = 1 }; - void set_piece_deadline(int index, int deadline, int flags = 0) const; - -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. - - -piece_availability() --------------------- - - :: - - void piece_availability(std::vector& avail) 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. - - -piece_priority() prioritize_pieces() piece_priorities() -------------------------------------------------------- - - :: - - 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; - -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. - - -file_priority() prioritize_files() file_priorities() ----------------------------------------------------- - - :: - - 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; - -``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 sepcial 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. - -file_progress() ---------------- - - :: - - void file_progress(std::vector& fp, int flags = 0); - -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. - - -save_path() ------------ - - :: - - boost::filesystem::path save_path() const; - -``save_path()`` returns the path that was given to `add_torrent()`_ when this torrent -was started. - -move_storage() --------------- - - :: - - void move_storage(boost::filesystem::path const& save_path) const; - void move_storage(boost::filesystem::wpath const& save_path) 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. - -rename_file() -------------- - - :: - - void rename_file(int index, boost::filesystem::path) const; - void rename_file(int index, boost::filesystem::wpath) 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. - -get_storage_impl() ------------------- - - :: - - storage_interface* get_storage_impl() const; - -Returns the storage implementation for this torrent. This depends on the -storage contructor function that was passed to ``session::add_torrent``. - -super_seeding() ---------------- - - :: - - bool super_seeding() const; - void super_seeding(bool on) const; - -Enables or disabled super seeding/initial seeding for this torrent. The torrent -needs to be a seed for this to take effect. The overload that returns a bool -tells you of super seeding is enabled or not. - -add_piece() ------------ - - :: - - enum flags_t { overwrite_existing = 1 }; - void add_piece(int piece, char const* data, int flags = 0) const; - -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 ``has_failed_alert``. - -read_piece() ------------- - - :: - - void read_piece(int piece) 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_. In order to receive this alert, you must enable -``alert::storage_notification`` in your alert mask (see `set_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. - -force_reannounce() force_dht_announce() ---------------------------------------- - - :: - - void force_reannounce() const; - void force_reannounce(boost::posix_time::time_duration) const; - void force_dht_announce() const; - -``force_reannounce()`` will force this torrent to do another tracker request, to receive new -peers. The second overload of ``force_reannounce`` that takes a ``time_duration`` as -argument will schedule a reannounce in that amount of time from now. - -``force_dht_announce`` will announce the torrent to the DHT immediately. - -scrape_tracker() ----------------- - - :: - - void scrape_tracker() const; - -``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_. - -connect_peer() --------------- - - :: - - void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) 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. - - -name() ------- - - :: - - std::string name() const; - -Returns the name of the torrent. i.e. the name from the metadata associated with it. 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``. - - -set_ratio() ------------ - - :: - - void set_ratio(float ratio) const; - -``set_ratio()`` sets the desired download / upload ratio. If set to 0, it is considered being -infinite. i.e. the client will always upload as much as it can, no matter how much it gets back -in return. With this setting it will work much like the standard clients. - -Besides 0, the ratio can be set to any number greater than or equal to 1. It means how much to -attempt to upload in return for each download. e.g. if set to 2, the client will try to upload -2 bytes for every byte received. The default setting for this is 0, which will make it work -as a standard client. - - -set_upload_limit() set_download_limit() upload_limit() download_limit() ------------------------------------------------------------------------ - - :: - - void set_upload_limit(int limit) const; - void set_download_limit(int limit) const; - int upload_limit() const; - int download_limit() 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::set_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. - - -set_sequential_download() -------------------------- - - :: - - void set_sequential_download(bool sd); - -``set_sequential_download()`` enables or disables *sequential download*. When enabled, the piece -picker will pick pieces in sequence instead of rarest first. - -Enabling sequential download will affect the piece distribution negatively in the swarm. It should be -used sparingly. - -get_peer_download_limit() get_peer_upload_limit() set_peer_upload_limit() set_peer_download_limit() ---------------------------------------------------------------------------------------------------- - - :: - - int get_peer_upload_limit(tcp::endpoint ip); - int get_peer_download_limit(tcp::endpoint ip); - void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const; - void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const; - -Works like ``get_upload_limit``, ``get_download_limit``, ``set_upload_limit`` and -``set_download_limit`` respectively, but controls individual peer instead of the -whole torrent. - -pause() resume() ----------------- - - :: - - enum pause_flags_t { graceful_pause = 1 }; - void pause(int flags) const; - void resume() const; - -``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_. - -flush_cache() -------------- - - :: - - void flush_cache() 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. - -force_recheck() ---------------- - - :: - - void force_recheck() 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. - -clear_error() -------------- - - :: - - void clear_error() 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. - -set_upload_mode() ------------------ - -:: - - void set_upload_mode(bool m) 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``. - -set_share_mode() ----------------- - - :: - - void set_share_mode(bool m) 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. - -resolve_countries() -------------------- - - :: - - void resolve_countries(bool r); - bool resolve_countries() 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. - -is_seed() ---------- - - :: - - bool is_seed() const; - -Returns true if the torrent is in seed mode (i.e. if it has finished downloading). - -auto_managed() --------------- - - :: - - void auto_managed(bool m) const; - -``auto_managed()`` changes whether the torrent is auto managed or not. For more info, -see queuing_. - -set_metadata() --------------- - - :: - - bool set_metadata(char const* buf, int size) const; - -``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. - - -set_tracker_login() -------------------- - - :: - - void set_tracker_login(std::string const& username - , std::string const& password) const; - -``set_tracker_login()`` 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. - - -trackers() replace_trackers() add_tracker() -------------------------------------------- - - :: - - std::vector trackers() const; - void replace_trackers(std::vector const&) const; - void add_tracker(announc_entry const& url); - -``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() force_dht_announce()`_. See `trackers()`_ for the definition of ``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. - - -add_url_seed() remove_url_seed() url_seeds() --------------------------------------------- - - :: - - void add_url_seed(std::string const& url); - void remove_url_seed(std::string const& url); - std::set url_seeds() 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. - -add_http_seed() remove_http_seed() http_seeds() ------------------------------------------------ - - :: - - void add_http_seed(std::string const& url); - void remove_http_seed(std::string const& url); - std::set http_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. - -queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom() -------------------------------------------------------------------------------------------------------- - - :: - - int queue_position() const; - void queue_position_up() const; - void queue_position_down() const; - void queue_position_top() const; - void queue_position_bottom() 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. - -set_priority() --------------- - - :: - - void set_priority(int prio) 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. - -Torrents with higher priority are also more likely to have its peers unchoked, to -distribute more upload capacity to them. - -use_interface() ---------------- - - :: - - void use_interface(char const* net_interface) const; - -``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. - - -info_hash() ------------ - - :: - - sha1_hash info_hash() const; - -``info_hash()`` returns the info-hash for the torrent. - - -set_max_uploads() max_uploads() -------------------------------- - - :: - - void set_max_uploads(int max_uploads) const; - int max_uploads() 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. - -``max_uploads()`` returns the current settings. - - -set_max_connections() max_connections() ---------------------------------------- - - :: - - void set_max_connections(int max_connections) const; - int max_connections() 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. - -``max_connections()`` returns the current settings. - - -save_resume_data() ------------------- - - :: - - void save_resume_data(int flags = 0) const; - -``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 may be set to ``torrent_handle::flush_cache``. Doing so will flush the disk -cache 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. - -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:: - - int num_resume_data = 0; - 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; - - h.save_resume_data(); - ++num_resume_data; - } - - while (num_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); - --num_resume_data; - continue; - } - - save_resume_data_alert const* rd = alert_cast(a); - if (rd == 0) - { - process_alert(a); - continue; - } - - torrent_handle h = rd->handle; - boost::filesystem::ofstream out(h.save_path() - / (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary); - out.unsetf(std::ios_base::skipws); - bencode(std::ostream_iterator(out), *rd->resume_data); - --num_resume_data; - } - - -need_save_resume_data() ------------------------ - - :: - - bool need_save_resume_data() 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. - -status() --------- - - :: - - torrent_status status(boost::uint32_t flags = 0xffffffff) const; - -``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: - -* ``query_distributed_copies`` - calculates ``distributed_copies``, ``distributed_full_copies`` and ``distributed_fraction``. - -* ``query_accurate_download_counters`` - includes partial downloaded blocks in ``total_done`` and ``total_wanted_done``. - -* ``query_last_seen_complete`` - includes ``last_seen_complete``. - - -get_download_queue() --------------------- - - :: - - void get_download_queue(std::vector& queue) 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. The entry in the vector (``partial_piece_info``) looks like this:: - - struct partial_piece_info - { - int piece_index; - int blocks_in_piece; - enum state_t { none, slow, medium, fast }; - state_t piece_state; - block_info* blocks; - }; - -``piece_index`` is 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. - -``piece_state`` is 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. - -:: - - struct block_info - { - enum block_state_t - { none, requested, writing, finished }; - - void set_peer(tcp::endpoint const& ep); - tcp::endpoint peer() const; - - unsigned bytes_progress:15; - unsigned block_size:15; - unsigned state:2; - unsigned num_peers:14; - }; - - -The ``blocks`` field points to an array of ``blocks_in_piece`` elements. This pointer is -only valid until the next call to ``get_download_queue()`` for any torrent in the same session. -They all share the storaga for the block arrays in their session object. - -The ``block_info`` array contains data for each individual block in the piece. Each block has -a state (``state``) which is any of: - -* ``none`` - This block has not been downloaded or requested form any peer. -* ``requested`` - The block has been requested, but not completely downloaded yet. -* ``writing`` - The block has been downloaded and is currently queued for being written to disk. -* ``finished`` - The block has been written to disk. - -The ``peer`` field is the ip address of the peer this block was downloaded from. -``num_peers`` is 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. -``bytes_progress`` is the number of bytes that have been received for this block, and -``block_size`` is the total number of bytes in this block. - -get_peer_info() ---------------- - - :: - - void get_peer_info(std::vector&) const; - -``get_peer_info()`` 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_. - - -get_torrent_info() ------------------- - - :: - - torrent_info const& get_torrent_info() const; - -Returns a const reference to the torrent_info_ object associated with this torrent. -This reference is valid as long as the torrent_handle_ is valid, no longer. If the -torrent_handle_ is invalid or if it doesn't have any metadata, libtorrent_exception_ -exception will be thrown. The torrent may be in a state without metadata only if -it was started without a .torrent file, i.e. by using the libtorrent extension of -just supplying a tracker and info-hash. - - -is_valid() ----------- - - :: - - bool is_valid() 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. - - -torrent_status -============== - -It contains the following fields:: - - struct torrent_status - { - enum state_t - { - queued_for_checking, - checking_files, - downloading_metadata, - downloading, - finished, - seeding, - allocating, - checking_resume_data - }; - - state_t state; - bool paused; - bool auto_managed; - bool sequential_download; - bool seeding; - bool finished; - float progress; - int progress_ppm; - std::string error; - - boost::posix_time::time_duration next_announce; - boost::posix_time::time_duration announce_interval; - - std::string current_tracker; - - size_type total_download; - size_type total_upload; - - size_type total_payload_download; - size_type total_payload_upload; - - size_type total_failed_bytes; - size_type total_redundant_bytes; - - int download_rate; - int upload_rate; - - int download_payload_rate; - int upload_payload_rate; - - int num_peers; - - int num_complete; - int num_incomplete; - - int list_seeds; - int list_peers; - - int connect_candidates; - - bitfield pieces; - int num_pieces; - - size_type total_done; - size_type total_wanted_done; - size_type total_wanted; - - int num_seeds; - - int distributed_full_copies; - int distributed_fraction; - - float distributed_copies; - - int block_size; - - int num_uploads; - int num_connections; - int uploads_limit; - int connections_limit; - - storage_mode_t storage_mode; - - int up_bandwidth_queue; - int down_bandwidth_queue; - - size_type all_time_upload; - size_type all_time_download; - - int active_time; - int finished_time; - int seeding_time; - - int seed_rank; - - int last_scrape; - - bool has_incoming; - - int sparse_regions; - - bool seed_mode; - bool upload_mode; - bool share_mode; - - int priority; - - time_t added_time; - time_t completed_time; - time_t last_seen_complete; - - int time_since_upload; - int time_since_download; - - int queue_position; - bool need_save_resume; - }; - -``progress`` is a value in the range [0, 1], that represents the progress of the -torrent's current task. It may be checking files or downloading. - -``progress_ppm`` 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``. - -The torrent's current task is in the ``state`` member, it will be one of the following: - -+--------------------------+----------------------------------------------------------+ -|``checking_resume_data`` |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. | -+--------------------------+----------------------------------------------------------+ -|``queued_for_checking`` |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. | -+--------------------------+----------------------------------------------------------+ -|``checking_files`` |The torrent has not started its download yet, and is | -| |currently checking existing files. | -+--------------------------+----------------------------------------------------------+ -|``downloading_metadata`` |The torrent is trying to download metadata from peers. | -| |This assumes the metadata_transfer extension is in use. | -+--------------------------+----------------------------------------------------------+ -|``downloading`` |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. | -+--------------------------+----------------------------------------------------------+ -|``finished`` |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. | -+--------------------------+----------------------------------------------------------+ -|``seeding`` |In this state the torrent has finished downloading and | -| |is a pure seeder. | -+--------------------------+----------------------------------------------------------+ -|``allocating`` |If the torrent was started in full allocation mode, this | -| |indicates that the (disk) storage for the torrent is | -| |allocated. | -+--------------------------+----------------------------------------------------------+ - - -When downloading, the progress is ``total_wanted_done`` / ``total_wanted``. This takes -into account files whose priority have been set to 0. They are not considered. - -``paused`` is 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()``. - -``auto_managed`` is 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_ - -``sequential_download`` is true when the torrent is in sequential download mode. In -this mode pieces are downloaded in order rather than rarest first. - -``is_seeding`` is true if all pieces have been downloaded. - -``is_finished`` is 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. - -``has_metadata`` is 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). Note that if the torrent doesn't have metadata, the member -`get_torrent_info()`_ will throw. - -``error`` 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. - -``next_announce`` is the time until the torrent will announce itself to the tracker. And -``announce_interval`` is the time the tracker want us to wait until we announce ourself -again the next time. - -``current_tracker`` is the URL of the last working tracker. If no tracker request has -been successful yet, it's set to an empty string. - -``total_download`` and ``total_upload`` is 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``. - -``total_payload_download`` and ``total_payload_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. - -``total_failed_bytes`` is 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. - -``total_redundant_bytes`` is 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. - -``pieces`` is the 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. - -``num_pieces`` is 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. - -``download_rate`` and ``upload_rate`` are 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. The -``download_payload_rate`` and ``upload_payload_rate`` respectively is 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. - -``num_peers`` is 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()`_. - -``num_complete`` and ``num_incomplete`` are set to -1 if the tracker did not -send any scrape data in its announce reply. This data is optional and may -not be available from all trackers. If these are not -1, they are the total -number of peers that are seeding (complete) and the total number of peers -that are still downloading (incomplete) this torrent. - -``list_seeds`` and ``list_peers`` are the number of seeds in our peer list -and the total number of peers (including seeds) respectively. 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. - -``connect_candidates`` is 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. - -``total_done`` is 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``). - -``total_wanted_done`` is 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). - -``total_wanted`` is the total number of bytes we want to download. This is also -excluding pieces whose priorities have been set to 0. - -``num_seeds`` is the number of peers that are seeding that this client is -currently connected to. - -``distributed_full_copies`` is 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. - -``distributed_fraction`` 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. - -``distributed_copies`` is a floating point representation of the -``distributed_full_copies`` as the integer part and ``distributed_fraction`` -/ 1000 as the fraction part. If floating point operations are disabled -this value is always -1. - -``block_size`` is 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. - -``num_uploads`` is the number of unchoked peers in this torrent. - -``num_connections`` is 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``. - -``uploads_limit`` is the set limit of upload slots (unchoked peers) for this torrent. - -``connections_limit`` is the set limit of number of connections for this torrent. - -``storage_mode`` is one of ``storage_mode_allocate``, ``storage_mode_sparse`` or -``storage_mode_compact``. Identifies which storage mode this torrent is being saved -with. See `Storage allocation`_. - -``up_bandwidth_queue`` and ``down_bandwidth_queue`` are 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. - -``all_time_upload`` and ``all_time_download`` are accumulated upload and download -payload byte counters. They are saved in and restored from resume data to keep totals -across sessions. - -``active_time``, ``finished_time`` and ``seeding_time`` are second counters. -They 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. - -``seed_rank`` is 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_. - -``last_scrape`` is the number of seconds since this torrent acquired scrape data. -If it has never done that, this value is -1. - -``has_incoming`` is true if there has ever been an incoming connection attempt -to this torrent.' - -``sparse_regions`` 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. - -``seed_mode`` is 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. - -``upload_mode`` is 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_. - -``share_mode`` is true if the torrent is currently in share-mode, i.e. -not downloading the torrent, but just helping the swarm out. - -``added_time`` is the posix-time when this torrent was added. i.e. what -``time(NULL)`` returned at the time. - -``completed_time`` is the posix-time when this torrent was finished. If -the torrent is not yet finished, this is 0. - -``last_seen_complete`` is the time when we, or one of our peers, last -saw a complete copy of this torrent. - -``time_since_upload`` and ``time_since_download`` are the number of -seconds since any peer last uploaded from this torrent and the last -time a downloaded piece passed the hash check, respectively. - -``queue_position`` is the position this torrent has in the download -queue. If the torrent is a seed or finished, this is -1. - -``need_save_resume`` is true if this torrent has unsaved changes -to its download state and statistics since the last resume data -was saved. - -peer_info -========= - -It contains the following fields:: - - struct peer_info - { - enum - { - interesting = 0x1, - choked = 0x2, - remote_interested = 0x4, - remote_choked = 0x8, - supports_extensions = 0x10, - local_connection = 0x20, - handshake = 0x40, - connecting = 0x80, - queued = 0x100, - on_parole = 0x200, - seed = 0x400, - optimistic_unchoke = 0x800, - snubbed = 0x1000, - upload_only = 0x2000, - holepunched = 0x4000, - rc4_encrypted = 0x100000, - plaintext_encrypted = 0x200000 - }; - - unsigned int flags; - - enum peer_source_flags - { - tracker = 0x1, - dht = 0x2, - pex = 0x4, - lsd = 0x8 - }; - - int source; - - enum bw_state { bw_idle, bw_limit, bw_network, bw_disk }; - - char read_state; - char write_state; - - asio::ip::tcp::endpoint ip; - int up_speed; - int down_speed; - int payload_up_speed; - int payload_down_speed; - size_type total_download; - size_type total_upload; - peer_id pid; - bitfield pieces; - int upload_limit; - int download_limit; - - time_duration last_request; - time_duration last_active; - int request_timeout; - - int send_buffer_size; - int used_send_buffer; - - int receive_buffer_size; - int used_receive_buffer; - - int num_hashfails; - - char country[2]; - - std::string inet_as_name; - int inet_as; - - size_type load_balancing; - - int requests_in_buffer; - int download_queue_length; - int upload_queue_length; - - int failcount; - - int downloading_piece_index; - int downloading_block_index; - int downloading_progress; - int downloading_total; - - std::string client; - - enum - { - standard_bittorrent = 0, - web_seed = 1 - }; - int connection_type; - - int remote_dl_rate; - - int pending_disk_bytes; - - int send_quota; - int receive_quota; - - int rtt; - - int num_pieces; - - int download_rate_peak; - int upload_rate_peak; - - float progress; - int progress_ppm; - - tcp::endpoint local_endpoint; - }; - -The ``flags`` attribute tells you in which state the peer is. It is set to -any combination of the enums above. The following table describes each flag: - -+-------------------------+-------------------------------------------------------+ -| ``interesting`` | **we** are interested in pieces from this peer. | -+-------------------------+-------------------------------------------------------+ -| ``choked`` | **we** have choked this peer. | -+-------------------------+-------------------------------------------------------+ -| ``remote_interested`` | the peer is interested in **us** | -+-------------------------+-------------------------------------------------------+ -| ``remote_choked`` | the peer has choked **us**. | -+-------------------------+-------------------------------------------------------+ -| ``support_extensions`` | means that this peer supports the | -| | `extension protocol`__. | -+-------------------------+-------------------------------------------------------+ -| ``local_connection`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``handshake`` | The connection is opened, and waiting for the | -| | handshake. Until the handshake is done, the peer | -| | cannot be identified. | -+-------------------------+-------------------------------------------------------+ -| ``connecting`` | The connection is in a half-open state (i.e. it is | -| | being connected). | -+-------------------------+-------------------------------------------------------+ -| ``queued`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``on_parole`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``seed`` | This peer is a seed (it has all the pieces). | -+-------------------------+-------------------------------------------------------+ -| ``optimistic_unchoke`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``snubbed`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``upload_only`` | 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. | -+-------------------------+-------------------------------------------------------+ -| ``holepunched`` | 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. | -+-------------------------+-------------------------------------------------------+ - -__ extension_protocol.html - -``source`` is a combination of flags describing from which sources this peer -was received. The flags are: - -+------------------------+--------------------------------------------------------+ -| ``tracker`` | The peer was received from the tracker. | -+------------------------+--------------------------------------------------------+ -| ``dht`` | The peer was received from the kademlia DHT. | -+------------------------+--------------------------------------------------------+ -| ``pex`` | The peer was received from the peer exchange | -| | extension. | -+------------------------+--------------------------------------------------------+ -| ``lsd`` | The peer was received from the local service | -| | discovery (The peer is on the local network). | -+------------------------+--------------------------------------------------------+ -| ``resume_data`` | The peer was added from the fast resume data. | -+------------------------+--------------------------------------------------------+ - -``read_state`` and ``write_state`` indicates what state this peer is in with regards -to sending and receiving data. The states are declared in the ``bw_state`` enum and -defines as follows: - -+------------------------+--------------------------------------------------------+ -| ``bw_idle`` | The peer is not waiting for any external events to | -| | send or receive data. | -| | | -+------------------------+--------------------------------------------------------+ -| ``bw_limit`` | The peer is waiting for the rate limiter. | -| | | -+------------------------+--------------------------------------------------------+ -| ``bw_network`` | 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_disk`` | The peer is waiting for the disk I/O thread to catch | -| | up writing buffers to disk before downloading more. | -| | | -+------------------------+--------------------------------------------------------+ - -The ``ip`` field is the IP-address to this peer. The type is an asio endpoint. For -more info, see the asio_ documentation. - -.. _asio: http://asio.sf.net - -``up_speed`` and ``down_speed`` contains the current upload and download speed -we have to and from this peer (including any protocol messages). The transfer rates -of payload data only are found in ``payload_up_speed`` and ``payload_down_speed``. -These figures are updated approximately once every second. - -``total_download`` and ``total_upload`` are 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. - -``pid`` is 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()_ - -``pieces`` is 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). - -``seed`` is true if this peer is a seed. - -``upload_limit`` is the number of bytes per second we are allowed to send to this -peer every second. It may be -1 if there's no local limit on the peer. The global -limit and the torrent limit is always enforced anyway. - -``download_limit`` is the number of bytes per second this peer is allowed to -receive. -1 means it's unlimited. - -``last_request`` and ``last_active`` is the time since we last sent a request -to this peer and since any transfer occurred with this peer, respectively. - -``request_timeout`` is 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. - -``send_buffer_size`` and ``used_send_buffer`` is the number of bytes allocated -and used for the peer's send buffer, respectively. - -``receive_buffer_size`` and ``used_receive_buffer`` are the number of bytes -allocated and used as receive buffer, respectively. - -``num_hashfails`` is the number of pieces this peer has participated in -sending us that turned out to fail the hash check. - -``country`` is 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 - -``inet_as_name`` is 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. - -``inet_as`` is the AS number the peer is located in. - -``load_balancing`` is a measurement of the balancing of free download (that we get) -and free upload that we give. Every peer gets a certain amount of free upload, but -this member says how much *extra* free upload this peer has got. If it is a negative -number it means that this was a peer from which we have got this amount of free -download. - -``requests_in_buffer`` is the number of requests messages that are currently in the -send buffer waiting to be sent. - -``download_queue_length`` is the number of piece-requests we have sent to this peer -that hasn't been answered with a piece yet. - -``upload_queue_length`` is the number of piece-requests we have received from this peer -that we haven't answered with a piece yet. - -``failcount`` is 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. - -You can know which piece, and which part of that piece, that is currently being -downloaded from a specific peer by looking at the next 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. - -``client`` is 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. - -``connection_type`` can currently be one of: - -+---------------------------------------+-------------------------------------------------------+ -| type | meaning | -+=======================================+=======================================================+ -| ``peer_info::standard_bittorrent`` | Regular bittorrent connection over TCP | -+---------------------------------------+-------------------------------------------------------+ -| ``peer_info::bittorrent_utp`` | Bittorrent connection over uTP | -+---------------------------------------+-------------------------------------------------------+ -| ``peer_info::web_sesed`` | HTTP connection using the `BEP 19`_ protocol | -+---------------------------------------+-------------------------------------------------------+ -| ``peer_info::http_seed`` | HTTP connection using the `BEP 17`_ protocol | -+---------------------------------------+-------------------------------------------------------+ - -``remote_dl_rate`` is an estimate of the rate this peer is downloading at, in -bytes per second. - -``pending_disk_bytes`` is 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``. - -``send_quota`` and ``receive_quota`` are 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. - -``rtt`` is 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. - -``num_pieces`` is the number of pieces this peer has. - -``download_rate_peak`` and ``upload_rate_peak`` are 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. - -``progress`` is the progress of the peer in the range [0, 1]. This is always 0 when -floating point operations are diabled, instead use ``progress_ppm``. - -``progress_ppm`` indicates the download progress of the peer in the range [0, 1000000] -(parts per million). - -``local_endpoint`` is 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. - - -session customization -===================== - -You have some control over session configuration through the ``session_settings`` object. You -create it and fill it with your settings and then use ``session::set_settings()`` -to apply them. - -You have control over proxy and authorization settings and also the user-agent -that will be sent to the tracker. The user-agent will also be used to identify the -client with other peers. - -presets -------- - -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. - -:: - - session_settings min_memory_usage(); - session_settings high_performance_seed(); - -``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. - - -session_settings ----------------- - -:: - - struct session_settings - { - session_settings(); - int version; - std::string user_agent; - int tracker_completion_timeout; - int tracker_receive_timeout; - int stop_tracker_timeout; - int tracker_maximum_response_length; - - int piece_timeout; - float request_queue_time; - int max_allowed_in_request_queue; - int max_out_request_queue; - int whole_pieces_threshold; - int peer_timeout; - int urlseed_timeout; - int urlseed_pipeline_size; - int file_pool_size; - bool allow_multiple_connections_per_ip; - int max_failcount; - int min_reconnect_time; - int peer_connect_timeout; - bool ignore_limits_on_local_network; - int connection_speed; - bool send_redundant_have; - bool lazy_bitfields; - int inactivity_timeout; - int unchoke_interval; - int optimistic_unchoke_interval; - std::string announce_ip; - int num_want; - int initial_picker_threshold; - int allowed_fast_set_size; - - enum { no_piece_suggestions = 0, suggest_read_cache = 1 }; - int suggest_mode; - int max_queued_disk_bytes; - int handshake_timeout; - bool use_dht_as_fallback; - bool free_torrent_hashes; - bool upnp_ignore_nonrouters; - int send_buffer_watermark; - int send_buffer_watermark_factor; - - #ifndef TORRENT_NO_DEPRECATE - bool auto_upload_slots; - bool auto_upload_slots_rate_based; - #endif - - enum choking_algorithm_t - { - fixed_slots_choker, - auto_expand_choker, - rate_based_choker, - bittyrant_choker - }; - - int choking_algorithm; - - enum seed_choking_algorithm_t - { - round_robin, - fastest_upload, - anti_leech - }; - - int seed_choking_algorithm; - - bool use_parole_mode; - int cache_size; - int cache_buffer_chunk_size; - int cache_expiry; - bool use_read_cache; - bool explicit_read_cache; - int explicit_cache_interval; - - enum io_buffer_mode_t - { - enable_os_cache = 0, - disable_os_cache_for_aligned_files = 1, - disable_os_cache = 2 - }; - int disk_io_write_mode; - int disk_io_read_mode; - - std::pair outgoing_ports; - char peer_tos; - - int active_downloads; - int active_seeds; - int active_dht_limit; - int active_tracker_limit; - int active_limit; - bool auto_manage_prefer_seeds; - bool dont_count_slow_torrents; - int auto_manage_interval; - float share_ratio_limit; - float seed_time_ratio_limit; - int seed_time_limit; - int peer_turnover_interval; - float peer_turnover; - float peer_turnover_cutoff; - bool close_redundant_connections; - - int auto_scrape_interval; - int auto_scrape_min_interval; - - int max_peerlist_size; - - int min_announce_interval; - - bool prioritize_partial_pieces; - int auto_manage_startup; - - bool rate_limit_ip_overhead; - - bool announce_to_all_trackers; - bool announce_to_all_tiers; - - bool prefer_udp_trackers; - bool strict_super_seeding; - - int seeding_piece_quota; - - int max_sparse_regions; - - bool lock_disk_cache; - - int max_rejects; - - int recv_socket_buffer_size; - int send_socket_buffer_size; - - bool optimize_hashing_for_speed; - - int file_checks_delay_per_block; - - enum disk_cache_algo_t - { lru, largest_contiguous }; - - disk_cache_algo_t disk_cache_algorithm; - - int read_cache_line_size; - int write_cache_line_size; - - int optimistic_disk_retry; - bool disable_hash_check; - - int max_suggest_pieces; - - bool drop_skipped_requests; - - bool low_prio_disk; - int local_service_announce_interval; - int dht_announce_interval; - int dht_max_torrents; - - int udp_tracker_token_expiry; - bool volatile_read_cache; - bool guided_read_cache; - bool default_min_cache_age; - - int num_optimistic_unchoke_slots; - bool no_atime_storage; - int default_est_reciprocation_rate; - int increase_est_reciprocation_rate; - int decrease_est_reciprocation_rate; - bool incoming_starts_queued_torrents; - bool report_true_downloaded; - bool strict_end_game_mode; - - int default_peer_upload_rate; - int default_peer_download_rate; - bool broadcast_lsd; - - bool enable_outgoing_utp; - bool enable_incoming_utp; - bool enable_outgoing_tcp; - bool enable_incoming_tcp; - int max_pex_peers; - bool ignore_resume_timestamps; - bool anonymous_mode; - int tick_interval; - int share_mode_target; - - int upload_rate_limit; - int download_rate_limit; - int local_upload_rate_limit; - int local_download_rate_limit; - int unchoke_slots_limit; - int half_open_limit; - int connections_limit; - - int utp_target_delay; - int utp_gain_factor; - int utp_min_timeout; - int utp_syn_resends; - int utp_num_resends; - int utp_connect_timeout; - int utp_delayed_ack; - bool utp_dynamic_sock_buf; - - enum bandwidth_mixed_algo_t - { - prefer_tcp = 0, - peer_proportional = 1 - - }; - int mixed_mode_algorithm; - bool rate_limit_utp; - - int listen_queue_size; - }; - -``version`` is automatically set to the libtorrent version you're using -in order to be forward binary compatible. This field should not be changed. - -``user_agent`` this is 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. - -``tracker_completion_timeout`` is 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. - -``tracker_receive_timeout`` is 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. - -``stop_tracker_timeout`` is the time to wait for tracker responses when -shutting down the session object. This is given in seconds. Default is -10 seconds. - -``tracker_maximum_response_length`` is 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. - -``piece_timeout`` controls the number of seconds from a request is sent until -it times out if no piece response is returned. - -``request_queue_time`` is 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. - -``max_allowed_in_request_queue`` is 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 handled) the last request will be -dropped. The higher this is, the faster upload speeds the client can get to a -single peer. - -``max_out_request_queue`` is 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. - -``whole_pieces_threshold`` is a limit in seconds. if a whole piece can be -downloaded in at least this number of seconds from a specific peer, the -peer_connection will prefer requesting 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. - -``peer_timeout`` is the number of seconds the peer connection should -wait (for any activity on the peer connection) before closing it 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. - -``urlseed_timeout`` is the same as ``peer_timeout`` but applies only to -url seeds. This value defaults to 20 seconds. - -``urlseed_pipeline_size`` 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. - -``file_pool_size`` is the 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. - -``allow_multiple_connections_per_ip`` 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. - -``max_failcount`` is the maximum times we try to connect to a peer before -stop connecting again. If a peer succeeds, the failcounter is reset. If -a peer is retrieved from a peer source (other than DHT) the failcount is -decremented by one, allowing another try. - -``min_reconnect_time`` is the time to wait between connection attempts. If -the peer fails, the time is multiplied by fail counter. - -``peer_connect_timeout`` 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. - -``ignore_limits_on_local_network``, if set to true, upload, download and -unchoke limits are ignored for peers on the local network. - -``connection_speed`` is 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. - -``send_redundant_have`` controls if 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. - -``lazy_bitfields`` 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 to prevent certain ISPs from stopping people -from seeding. - -``inactivity_timeout``, if a peer is uninteresting and uninterested -for longer than this number of seconds, it will be disconnected. -Default is 10 minutes - -``unchoke_interval`` is 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. - -``optimistic_unchoke_interval`` is the number of seconds between -each *optimistic* unchoke. On this timer, the currently optimistically -unchoked peer will change. - -``announce_ip`` is the ip address passed along to trackers as the ``&ip=`` parameter. -If left as the default (an empty string), that parameter is omitted. - -``num_want`` is the number of peers we want from each tracker request. It defines -what is sent as the ``&num_want=`` parameter to the tracker. - -``initial_picker_threshold`` 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. - -``allowed_fast_set_size`` is the number of pieces we allow peers to download -from us without being unchoked. - -``suggest_mode`` controls whether or not libtorrent will send out suggest -messages to create a bias of its peers to request certain pieces. The modes -are: - -* ``no_piece_suggestsions`` which is the default and will not send out suggest - messages. -* ``suggest_read_cache`` which will send out suggest messages for the most - recent pieces that are in the read cache. - -``max_queued_disk_bytes`` is the number maximum number of bytes, to be -written to disk, that can wait in the disk I/O thread queue. This queue -is only for waiting for the disk I/O thread to receive the job and either -write it to disk or insert it in the write cache. 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. - -``handshake_timeout`` specifies the number of seconds we allow a peer to -delay responding to a protocol handshake. If no response is received within -this time, the connection is closed. - -``use_dht_as_fallback`` 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. - -``free_torrent_hashes`` 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 `add_torrent()`_ for instance. - -``upnp_ignore_nonrouters`` 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. - -``send_buffer_watermark`` is 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. - -``send_buffer_watermark_factor`` is multiplied to the peer's upload rate -to determine the low-watermark for the peer. This is clamped to not -exceed the ``send_buffer_watermark`` upper limit. This defaults to 1. -For high capacity connections, setting this higher can improve upload -performance and disk throughput. - -``auto_upload_slots`` 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``. - -When ``auto_upload_slots_rate_based`` is 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. - -``choking_algorithm`` 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``. - -The options for choking algorithms are: - -* ``fixed_slots_choker`` is the traditional choker with a fixed number of unchoke - slots (as specified by ``session::set_max_uploads()``). - -* ``auto_expand_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. - -* ``rate_based_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. - -* ``bittyrant_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::set_upload_rate_limit()``). For more information about this - choker, see the paper_. - -.. _paper: http://bittyrant.cs.washington.edu/#papers - -``seed_choking_algorithm`` controls the seeding unchoke behavior. The available -options are: - -* ``round_robin`` which 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. - -* ``fastest_upload`` unchokes the peers we can send to the fastest. This might be - a bit more reliable in utilizing all available capacity. - -* ``anti_leech`` 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. - -``use_parole_mode`` 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. - -``cache_size`` is 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. - -``cache_expiry`` is the number of seconds from the last cached write to a piece -in the write cache, to when it's forcefully flushed to disk. Default is 60 second. - -``use_read_cache``, is set to true (default), the disk cache is also used to -cache pieces read from disk. Blocks for writing pieces takes presedence. - -``explicit_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. - -``explicit_cache_interval`` is 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. - -``disk_io_write_mode`` and ``disk_io_read_mode`` determines how files are -opened when they're in read only mode versus read and write mode. The options -are: - - * enable_os_cache - This is the default and files are opened normally, with the OS caching - reads and writes. - * disable_os_cache_for_aligned_files - 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 - 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``. - -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. - -``outgoing_ports``, 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. - -``peer_tos`` 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/ - -``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. - -``auto_manage_prefer_seeds`` specifies if libtorrent should prefer giving seeds -active slots or downloading torrents. The default is ``false``. - -if ``dont_count_slow_torrents`` is 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. - -``active_limit`` is a hard limit on the number of active torrents. This applies even to -slow torrents. - -``active_dht_limit`` is the max number of torrents to announce to the DHT. By default -this is set to 88, which is no more than one DHT announce every 10 seconds. - -``active_tracker_limit`` is the max number of torrents to announce to their trackers. -By default this is 360, which is no more than one announce every 5 seconds. - -``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). - -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. - -``auto_manage_interval`` is the number of seconds between the torrent queue -is updated, and rotated. - -``share_ratio_limit`` is the upload / download ratio limit for considering a -seeding torrent have met the seed limit criteria. See queuing_. - -``seed_time_ratio_limit`` is the seeding time / downloading time ratio limit -for considering a seeding torrent to have met the seed limit criteria. See queuing_. - -``seed_time_limit`` is 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_. - -``peer_turnover_interval`` controls a feature where libtorrent periodically can disconnect -the least useful peers in the hope of connecting to better ones. This settings 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). - -``close_redundant_connections`` 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``. - -``auto_scrape_interval`` is 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. - -``auto_scrape_min_interval`` is 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. - -``max_peerlist_size`` is 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. - -``max_paused_peerlist_size`` is 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. - -``min_announce_interval`` is 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. - -If ``prioritize_partial_pieces`` is 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. - -``auto_manage_startup`` is 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. - -If ``rate_limit_ip_overhead`` is set to true, the estimated TCP/IP overhead is -drained from the rate limiters, to avoid exceeding the limits with the total traffic - -``announce_to_all_trackers`` 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. - -``announce_to_all_tiers`` also 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. - -``prefer_udp_trackers`` is 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. - -``strict_super_seeding`` 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. - -``seeding_piece_quota`` is 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. - -``max_sparse_regions`` 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. - -``lock_disk_cache`` 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. - -``max_rejects`` is 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. - - -``recv_socket_buffer_size`` and ``send_socket_buffer_size`` 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. - -``optimize_hashing_for_speed`` 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. - -``file_checks_delay_per_block`` is 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. - -``disk_cache_algorithm`` tells the disk I/O thread which cache flush -algorithm to use. The default algorithm is largest_contiguous. This -flushes the entire piece, in the write cache, that was least recently -written to. This is specified by the ``session_settings::lru`` enum -value. ``session_settings::largest_contiguous`` will flush the largest -sequences of contiguous blocks from the write cache, regarless of the -piece's last use time. - -``read_cache_line_size`` is 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. - -``optimistic_disk_retry`` is 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()`_. - -``disable_hash_check`` 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. - -``max_suggest_pieces`` is 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. - -If ``drop_skipped_requests`` is 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. - -``low_prio_disk`` 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. - -``local_service_announce_interval`` is 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. - -``dht_announce_interval`` is the number of seconds between announcing -torrents to the distributed hash table (DHT). This is specified to -be 15 minutes which is its default. - -``dht_max_torrents`` is the max number of torrents we will track -in the DHT. - -``udp_tracker_token_expiry`` is 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. - -``volatile_read_cache``, 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. - -``guided_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. - -``default_min_cache_age`` is 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. - -``num_optimistic_unchoke_slots`` is 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. - -``no_atime_storage`` 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. - -``default_est_reciprocation_rate`` is 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. - -``increase_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. - -``decrease_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. - -``incoming_starts_queued_torrents`` 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. - -When ``report_true_downloaded`` is true, the ``&downloaded=`` argument -sent to trackers will include redundant downloaded bytes. It defaults -to ``false``, which means redundant bytes are not reported to the tracker. - -``strict_end_game_mode`` 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. - -``default_peer_upload_rate`` and ``default_peer_download_rate`` specifies -the default upload and download rate limits for peers, respectively. These -default to 0, which means unlimited. These settings affect the rate limits -set on new peer connections (not existing ones). The peer rate limits can -be changed individually later using -`get_peer_download_limit() get_peer_upload_limit() set_peer_upload_limit() set_peer_download_limit()`_. - -if ``broadcast_lsd`` is 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. It's off by default since it's inefficient. - -``enable_outgoing_utp``, ``enable_incoming_utp``, ``enable_outgoing_tcp``, -``enable_incoming_tcp`` 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. - -``ignore_resume_timestamps`` 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. - -``anonymous_mode`` 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. Trackers will only be used if they are using a proxy -server. 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). Since no incoming connections -are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all turned off -when this setting is enabled. - -If you're using I2P, it might make sense to enable anonymous mode as well. - -``tick_interval`` 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. - -``share_mode_target`` 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. - -``upload_rate_limit``, ``download_rate_limit``, ``local_upload_rate_limit`` -and ``local_download_rate_limit`` 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. - -``unchoke_slots_limit`` is the mac number of unchoked peers in the session. - -The number of unchoke slots may be ignored depending on what -``choking_algorithm`` is set to. - -``half_open_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. - -``connections_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. - -``utp_target_delay`` is 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. - -``utp_gain_factor`` is 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. - -``utp_min_timeout`` is 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. - -``utp_syn_resends`` is the number of SYN packets that are sent (and timed out) before -giving up and closing the socket. - -``utp_num_resends`` is the number of times a packet is sent (and lossed or timed out) -before giving up and closing the connection. - -``utp_connect_timeout`` is 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. - -``utp_delayed_ack`` is the number of milliseconds to delay ACKs the most. Delaying ACKs -significantly helps reducing the amount of protocol overhead in the reverse direction -from downloads. It defaults to 100 milliseconds. If set to 0, delayed ACKs are disabled -and every incoming payload packet is ACKed. The granularity of this timer is capped by -the tick interval (as specified by ``tick_interval``). - -``utp_dynamic_sock_buf`` 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). - -The ``mixed_mode_algorithm`` 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 (which they aren't by default). - -``rate_limit_utp`` determines if uTP connections should be throttled by the global rate -limiter or not. By default they are not, since uTP manages its own rate. - -``listen_queue_size`` is 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). - -pe_settings -=========== - -The ``pe_settings`` structure is used to control the settings related -to peer protocol encryption:: - - struct pe_settings - { - pe_settings(); - - enum enc_policy - { - forced, - enabled, - disabled - }; - - enum enc_level - { - plaintext, - rc4, - both - }; - - enc_policy out_enc_policy; - enc_policy in_enc_policy; - enc_level allowed_enc_level; - bool prefer_rc4; - }; - - -``in_enc_policy`` and ``out_enc_policy`` control the settings for incoming -and outgoing connections respectively. The settings for these are: - - * ``forced`` - 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. - - * ``enabled`` - 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. - - * ``disabled`` - only non-encrypted connections are allowed. - -``allowed_enc_level`` 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. The settings are: - - * ``plaintext`` - only the handshake is encrypted, the bulk of the traffic - remains unchanged. - - * ``rc4`` - the entire stream is encrypted with RC4 - - * ``both`` - both RC4 and plaintext connections are allowed. - -``prefer_rc4`` can be set to true if you want to prefer the RC4 encrypted stream. - - -proxy_settings -============== - -The ``proxy_settings`` structs contains the information needed to -direct certain traffic to a proxy. - - :: - - struct proxy_settings - { - proxy_settings(); - - std::string hostname; - int port; - - std::string username; - std::string password; - - enum proxy_type - { - none, - socks4, - socks5, - socks5_pw, - http, - http_pw - }; - - proxy_type type; - bool proxy_hostnames; - }; - -``hostname`` is 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. - -The ``type`` tells libtorrent what kind of proxy server it is. The following -options are available: - - * ``none`` - This is the default, no proxy server is used, all other fields - are ignored. - - * ``socks4`` - The server is assumed to be a `SOCKS4 server`_ that - requires a username. - - * ``socks5`` - The server is assumed to be a SOCKS5 server (`RFC 1928`_) that - does not require any authentication. The username and password are ignored. - - * ``socks5_pw`` - 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. - - * ``http`` - 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. - - * ``http_pw`` - The server is assumed to be an HTTP proxy that requires - user authorization. The username and password will be sent to the proxy. - -.. _`SOCKS4 server`: http://www.ufasoft.com/doc/socks4_protocol.htm -.. _`RFC 1928`: http://www.faqs.org/rfcs/rfc1928.html -.. _`RFC 1929`: http://www.faqs.org/rfcs/rfc1929.html -.. _CONNECT: draft-luotonen-web-proxy-tunneling-01.txt - -``proxy_hostnames`` 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. - -ip_filter -========= - -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). - - :: - - template - struct ip_range - { - Addr first; - Addr last; - int flags; - }; - - class ip_filter - { - public: - enum access_flags { blocked = 1 }; - - ip_filter(); - void add_rule(address first, address last, int flags); - int access(address const& addr) const; - - typedef boost::tuple > - , std::vector > > filter_tuple_t; - - filter_tuple_t export_filter() const; - }; - - -ip_filter() ------------ - - :: - - ip_filter() - -Creates a default filter that doesn't filter any address. - -postcondition: -``access(x) == 0`` for every ``x`` - - -add_rule() ----------- - - :: - - void add_rule(address first, address last, int flags); - -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. - - -access() --------- - - :: - - int access(address const& addr) const; - -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. - - -export_filter() ---------------- - - :: - - boost::tuple > - , std::vector > > export_filter() const; - -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. - - -big_number -========== - -Both the ``peer_id`` and ``sha1_hash`` types are typedefs of the class -``big_number``. It represents 20 bytes of data. Its synopsis follows:: - - class big_number - { - public: - bool operator==(const big_number& n) const; - bool operator!=(const big_number& n) const; - bool operator<(const big_number& n) const; - - const unsigned char* begin() const; - const unsigned char* end() const; - - unsigned char* begin(); - unsigned char* end(); - }; - -The iterators gives you access to individual bytes. - - -bitfield -======== - -The bitfiled type stores any number of bits as a bitfield in an array. - -:: - - class bitfield - { - bitfield(); - bitfield(int bits); - bitfield(int bits, bool val); - bitfield(char const* bytes, int bits); - bitfield(bitfield const& rhs); - - void borrow_bytes(char* bytes, int bits); - ~bitfield(); - - void assign(char const* bytes, int bits); - - bool operator[](int index) const; - - bool get_bit(int index) const; - - void clear_bit(int index); - void set_bit(int index); - - std::size_t size() const; - bool empty() const; - - char const* bytes() const; - - bitfield& operator=(bitfield const& rhs); - - int count() const; - - typedef const_iterator; - const_iterator begin() const; - const_iterator end() const; - - void resize(int bits, bool val); - void set_all(); - void clear_all(); - void resize(int bits); - }; - - - -hasher -====== - -This class creates sha1-hashes. Its declaration looks like this:: - - class hasher - { - public: - hasher(); - hasher(char const* data, unsigned int len); - - void update(char const* data, unsigned int len); - sha1_hash final(); - void reset(); - }; - - -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``. - - -fingerprint -=========== - -The fingerprint class represents information about a client and its version. It is used -to encode this information into the client's peer id. - -This is the class declaration:: - - struct fingerprint - { - fingerprint(const char* id_string, int major, int minor - , int revision, int tag); - - std::string to_string() const; - - char name[2]; - char major_version; - char minor_version; - char revision_version; - char tag_version; - - }; - -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 currently an informal directory of client id's here__. - -__ http://wiki.theory.org/BitTorrentSpecification#peer_id - - -The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the -version of your client. All these numbers must be within the range [0, 9]. - -``to_string()`` will generate the actual string put in the peer-id, and return it. - - -UPnP and NAT-PMP -================ - -The ``upnp`` and ``natpmp`` classes contains the state for all UPnP and NAT-PMP mappings, -by default 1 or two mappings are made by libtorrent, one for the listen port and one -for the DHT port (UDP). - -:: - - class upnp - { - public: - - 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); - - void discover_device(); - void close(); - - std::string router_model(); - }; - - class natpmp - { - public: - - 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); - - void close(); - void rebind(address const& listen_interface); - }; - -``discover_device()``, ``close()`` and ``rebind()`` are for internal uses and should -not be called directly by clients. - -add_mapping() -------------- - - :: - - int add_mapping(protocol_type p, int external_port, int local_port); - -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. - -delete_mapping() ----------------- - - :: - - void delete_mapping(int mapping_index); - -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()`_. - -router_model() --------------- - - :: - - std::string router_model(); - -This is only available for UPnP routers. If the model is advertized by -the router, it can be queried through this function. - - -free functions -============== - -identify_client() ------------------ - - :: - - std::string identify_client(peer_id const& id); - -This function is declared in the header ````. It 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. - - -client_fingerprint() --------------------- - - :: - - boost::optional client_fingerprint(peer_id const& 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. This function is -declared in the header ````. - - -lazy_bdecode() --------------- - - :: - - 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); - -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. - -bdecode() bencode() --------------------- - - :: - - template entry bdecode(InIt start, InIt end); - template void bencode(OutIt out, const entry& e); - -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 throw libtorrent_exception_. - -add_magnet_uri() ----------------- - - :: - - torrent_handle add_magnet_uri(session& ses, std::string const& uri - add_torrent_params p); - torrent_handle add_magnet_uri(session& ses, std::string const& uri - add_torrent_params p, error_code& ec); - -This function parses the magnet URI (``uri``) as a bittorrent magnet link, -and adds the torrent to the specified session (``ses``). It returns the -handle to the newly added torrent, or an invalid handle in case parsing -failed. To control some initial settings of the torrent, sepcify those in -the ``add_torrent_params``, ``p``. See `add_torrent()`_. - -The overload that does not take an ``error_code`` throws an exception on -error and is not available when building without exception support. - -For more information about magnet links, see `magnet links`_. - -make_magnet_uri() ------------------ - - :: - - std::string make_magnet_uri(torrent_handle const& handle); - -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`_. - - -alerts -====== - -The ``pop_alert()`` function on session is the interface for retrieving -alerts, warnings, messages and errors from libtorrent. If no alerts have -been posted by libtorrent ``pop_alert()`` will return a default initialized -``auto_ptr`` object. If there is an alert in libtorrent's queue, the alert -from the front of the queue is popped and returned. -You can then use the alert object and query - -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 a bitmask with the following bits: - -+--------------------------------+---------------------------------------------------------------------+ -| ``error_notification`` | 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 | -+--------------------------------+---------------------------------------------------------------------+ -| ``peer_notification`` | Enables alerts when peers send invalid requests, get banned or | -| | snubbed. | -+--------------------------------+---------------------------------------------------------------------+ -| ``port_mapping_notification`` | Enables alerts for port mapping events. For NAT-PMP and UPnP. | -+--------------------------------+---------------------------------------------------------------------+ -| ``storage_notification`` | Enables alerts for events related to the storage. File errors and | -| | synchronization events for moving the storage, renaming files etc. | -+--------------------------------+---------------------------------------------------------------------+ -| ``tracker_notification`` | Enables all tracker events. Includes announcing to trackers, | -| | receiving responses, warnings and errors. | -+--------------------------------+---------------------------------------------------------------------+ -| ``debug_notification`` | Low level alerts for when peers are connected and disconnected. | -+--------------------------------+---------------------------------------------------------------------+ -| ``status_notification`` | Enables alerts for when a torrent or the session changes state. | -+--------------------------------+---------------------------------------------------------------------+ -| ``progress_notification`` | Alerts for when blocks are requested and completed. Also when | -| | pieces are completed. | -+--------------------------------+---------------------------------------------------------------------+ -| ``ip_block_notification`` | Alerts when a peer is blocked by the ip blocker or port blocker. | -+--------------------------------+---------------------------------------------------------------------+ -| ``performance_warning`` | Alerts when some limit is reached that might limit the download | -| | or upload rate. | -+--------------------------------+---------------------------------------------------------------------+ -| ``stats_notification`` | 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. | -+--------------------------------+---------------------------------------------------------------------+ -| ``all_categories`` | The full bitmask, representing all available categories. | -+--------------------------------+---------------------------------------------------------------------+ - -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 - -When you get an alert, you can use ``alert_cast<>`` to attempt to cast the pointer to a -more specific alert type, to be queried for more information about the alert. ``alert_cast`` -has the followinf signature:: - - template T* alert_cast(alert* a); - template T const* alert_cast(alert const* a); - -You can also use a `alert dispatcher`_ mechanism that's available in libtorrent. - -All alert types are defined in the ```` header file. - -The ``alert`` class is the base class that specific messages are derived from. This -is its synopsis: - -.. parsed-literal:: - - class alert - { - public: - - enum category_t - { - error_notification = *implementation defined*, - peer_notification = *implementation defined*, - port_mapping_notification = *implementation defined*, - storage_notification = *implementation defined*, - tracker_notification = *implementation defined*, - debug_notification = *implementation defined*, - status_notification = *implementation defined*, - progress_notification = *implementation defined*, - ip_block_notification = *implementation defined*, - performance_warning = *implementation defined*, - dht_notification = *implementation defined*, - - all_categories = *implementation defined* - }; - - ptime timestamp() const; - - virtual ~alert(); - - virtual int type() const = 0; - virtual std::string message() const = 0; - virtual char const* what() const = 0; - virtual int category() const = 0; - virtual std::auto_ptr clone() const = 0; - }; - -``type()`` 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:: - - 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(); - // use p - break; - } - case file_renamed_alert::alert_type: - { - // etc... - } - } - -``what()`` returns a string literal describing the type of the alert. It does -not include any information that might be bundled with the alert. - -``category()`` returns a bitmask specifying which categories this alert belong to. - -``clone()`` returns a pointer to a copy of the alert. - -``message()`` 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. - -There's another alert base class that most alerts derives from, all the -alerts that are generated for a specific torrent are derived from:: - - struct torrent_alert: alert - { - // ... - torrent_handle handle; - }; - -There's also a base class for all alerts referring to tracker events:: - - struct tracker_alert: torrent_alert - { - // ... - std::string url; - }; - -The specific alerts are: - -read_piece_alert ----------------- - -This alert is posted when the asynchronous read operation initiated by -a call to `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. - -:: - - struct read_piece_alert: torrent_alert - { - // ... - boost::shared_ptr buffer; - int piece; - int size; - }; - -external_ip_alert ------------------ - -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 external_ip_alert: alert - { - // ... - address external_address; - }; - - -listen_failed_alert -------------------- - -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 listen_failed_alert: alert - { - // ... - tcp::endpoint endpoint; - error_code error; - }; - -listen_succeeded_alert ----------------------- - -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 listen_succeeded_alert: alert - { - // ... - tcp::endpoint endpoint; - }; - - -portmap_error_alert -------------------- - -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. - -``mapping`` refers to the mapping index of the port map that failed, i.e. -the index returned from `add_mapping()`_. - -``map_type`` is 0 for NAT-PMP and 1 for UPnP. - -``error`` tells you what failed. -:: - - struct portmap_error_alert: alert - { - // ... - int mapping; - int type; - error_code error; - }; - -portmap_alert -------------- - -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. - -``mapping`` refers to the mapping index of the port map that failed, i.e. -the index returned from `add_mapping()`_. - -``external_port`` is the external port allocated for the mapping. - -``type`` is 0 for NAT-PMP and 1 for UPnP. - -:: - - struct portmap_alert: alert - { - // ... - int mapping; - int external_port; - int map_type; - }; - -portmap_log_alert ------------------ - -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 portmap_log_alert: alert - { - //... - int map_type; - std::string msg; - }; - -file_error_alert ----------------- - -If the storage fails to read or write files that it needs access to, this alert is -generated and the torrent is paused. - -``file`` is the path to the file that was accessed when the error occurred. - -``error`` is the error code describing the error. - -:: - - struct file_error_alert: torrent_alert - { - // ... - std::string file; - error_code error; - }; - -file_renamed_alert ------------------- - -This is posted as a response to a ``torrent_handle::rename_file`` call, if the rename -operation succeeds. - -:: - - struct file_renamed_alert: torrent_alert - { - // ... - std::string name; - int index; - }; - -The ``index`` member refers to the index of the file that was renamed, -``name`` is the new name of the file. - - -file_rename_failed_alert ------------------------- - -This is posted as a response to a ``torrent_handle::rename_file`` call, if the rename -operation failed. - -:: - - struct file_rename_failed_alert: torrent_alert - { - // ... - int index; - error_code error; - }; - -The ``index`` member refers to the index of the file that was supposed to be renamed, -``error`` is the error code returned from the filesystem. - -tracker_announce_alert ----------------------- - -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 tracker_announce_alert: tracker_alert - { - // ... - int event; - }; - -Event specifies what event was sent to the tracker. It is defined as: - -0. None -1. Completed -2. Started -3. Stopped - - -tracker_error_alert -------------------- - -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 tracker_error_alert: tracker_alert - { - // ... - int times_in_row; - int status_code; - }; - - -tracker_reply_alert -------------------- - -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 tracker_reply_alert: tracker_alert - { - // ... - int num_peers; - }; - -The ``num_peers`` tells how many peers were returned from the tracker. This is -not necessarily all new peers, some of them may already be connected. - -dht_reply_alert ---------------- - -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 dht_reply_alert: tracker_alert - { - // ... - int num_peers; - }; - - -tracker_warning_alert ---------------------- - -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. The ``msg`` string in the alert contains the warning message from -the tracker. - -:: - - struct tracker_warning_alert: tracker_alert - { - // ... - std::string msg; - }; - -scrape_reply_alert ------------------- - -This alert is generated when a scrape request succeeds. ``incomplete`` -and ``complete`` is the data returned in the scrape response. These numbers -may be -1 if the reponse was malformed. - -:: - - struct scrape_reply_alert: tracker_alert - { - // ... - int incomplete; - int complete; - }; - - -scrape_failed_alert -------------------- - -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. ``msg`` contains a message describing the error. - -:: - - struct scrape_failed_alert: tracker_alert - { - // ... - std::string msg; - }; - -url_seed_alert --------------- - -This alert is generated when a HTTP seed name lookup fails. - -It contains ``url`` to the HTTP seed that failed along with an error message. - -:: - - struct url_seed_alert: torrent_alert - { - // ... - std::string url; - }; - - -hash_failed_alert ------------------ - -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 hash_failed_alert: torrent_alert - { - // ... - int piece_index; - }; - - -peer_alert ----------- - -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 peer_alert: torrent_alert - { - // ... - tcp::endpoint ip; - peer_id pid; - }; - -peer_connect_alert ------------------- - -This alert is posted every time an outgoing peer connect attempts succeeds. - -:: - - struct peer_connect_alert: peer_alert - { - // ... - }; - -peer_ban_alert --------------- - -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 peer_ban_alert: peer_alert - { - // ... - }; - - -peer_snubbed_alert ------------------- - -This alert is generated when a peer is snubbed, when it stops sending data when we request -it. - -:: - - struct peer_snubbed_alert: peer_alert - { - // ... - }; - - -peer_unsnubbed_alert --------------------- - -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 peer_unsnubbed_alert: peer_alert - { - // ... - }; - - -peer_error_alert ----------------- - -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. - -The ``error_code`` tells you what error caused this alert. - -:: - - struct peer_error_alert: peer_alert - { - // ... - error_code error; - }; - - -peer_connected_alert --------------------- - -This alert is generated when a peer is connected. - -:: - - struct peer_connected_alert: peer_alert - { - // ... - }; - - -peer_disconnected_alert ------------------------ - -This alert is generated when a peer is disconnected for any reason (other than the ones -covered by ``peer_error_alert``). - -The ``error_code`` tells you what error caused peer to disconnect. - -:: - - struct peer_disconnected_alert: peer_alert - { - // ... - error_code error; - }; - - -invalid_request_alert ---------------------- - -This is a debug alert that is generated by an incoming invalid piece request. -``ěp`` is the address of the peer and the ``request`` is the actual incoming -request from the peer. - -:: - - struct invalid_request_alert: peer_alert - { - // ... - peer_request request; - }; - - - struct peer_request - { - int piece; - int start; - int length; - bool operator==(peer_request const& r) const; - }; - - -The ``peer_request`` contains the values the client sent in its ``request`` message. ``piece`` is -the index of the piece it want data from, ``start`` is the offset within the piece where the data -should be read, and ``length`` is the amount of data it wants. - -request_dropped_alert ---------------------- - -This alert is generated when a peer rejects or ignores a piece request. - -:: - - struct request_dropped_alert: peer_alert - { - // ... - int block_index; - int piece_index; - }; - - -block_timeout_alert -------------------- - -This alert is generated when a block request times out. - -:: - - struct block_timeout_alert: peer_alert - { - // ... - int block_index; - int piece_index; - }; - - -block_finished_alert --------------------- - -This alert is generated when a block request receives a response. - -:: - - struct block_finished_alert: peer_alert - { - // ... - int block_index; - int piece_index; - }; - - -lsd_peer_alert --------------- - -This alert is generated when we receive a local service discovery message from a peer -for a torrent we're currently participating in. - -:: - - struct lsd_peer_alert: peer_alert - { - // ... - }; - - -file_completed_alert --------------------- - -This is posted whenever an individual file completes its download. i.e. -All pieces overlapping this file have passed their hash check. - -:: - - struct file_completed_alert: torrent_alert - { - // ... - int index; - }; - -The ``index`` member refers to the index of the file that completed. - - -block_downloading_alert ------------------------ - -This alert is generated when a block request is sent to a peer. - -:: - - struct block_downloading_alert: peer_alert - { - // ... - int block_index; - int piece_index; - }; - - -unwanted_block_alert --------------------- - -This alert is generated when a block is received that was not requested or -whose request timed out. - -:: - - struct unwanted_block_alert: peer_alert - { - // ... - int block_index; - int piece_index; - }; - - -torrent_delete_failed_alert ---------------------------- - -This alert is generated when a request to delete the files of a torrent fails. - -The ``error_code`` tells you why it failed. - -:: - - struct torrent_delete_failed_alert: torrent_alert - { - // ... - error_code error; - }; - -torrent_deleted_alert ---------------------- - -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_deleted_alert: torrent_alert - { - // ... - sha1_hash info_hash; - }; - -torrent_finished_alert ----------------------- - -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. - -There are no additional data members in this alert. - - -performance_alert ------------------ - -This alert is generated when a limit is reached that might have a negative impact on -upload or download rate performance. - -:: - - struct performance_alert: torrent_alert - { - // ... - - enum performance_warning_t - { - outstanding_disk_buffer_limit_reached, - outstanding_request_limit_reached, - upload_limit_too_low, - download_limit_too_low, - send_buffer_watermark_too_low, - too_many_optimistic_unchoke_slots - }; - - performance_warning_t warning_code; - }; - -outstanding_disk_buffer_limit_reached - 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_request_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. - -upload_limit_too_low - 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. - -download_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. - -send_buffer_watermark_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, send_buffer_watermark)) - - If you receive this alert, you migth want to either increase your ``send_buffer_watermark`` - or ``send_buffer_watermark_factor``. - -too_many_optimistic_unchoke_slots - 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. - - -state_changed_alert -------------------- - -Generated whenever a torrent changes its state. - -:: - - struct state_changed_alert: torrent_alert - { - // ... - - torrent_status::state_t state; - torrent_status::state_t prev_state; - }; - -``state`` is the new state of the torrent. ``prev_state`` is the previous state. - - -metadata_failed_alert ---------------------- - -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. - -There are no additional data members in this alert. - - -metadata_received_alert ------------------------ - -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. - - -fastresume_rejected_alert -------------------------- - -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 fastresume_rejected_alert: torrent_alert - { - // ... - error_code error; - }; - - -peer_blocked_alert ------------------- - -This alert is generated when a peer is blocked by the IP filter. The ``ip`` member is the -address that was blocked. - -:: - - struct peer_blocked_alert: torrent_alert - { - // ... - address ip; - }; - - -storage_moved_alert -------------------- - -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 storage_moved_alert: torrent_alert - { - // ... - std::string path; - }; - - -storage_moved_failed_alert --------------------------- - -The ``storage_moved_failed_alert`` is generated when an attempt to move the storage -(via torrent_handle::move_storage()) fails. - -:: - - struct storage_moved_failed_alert: torrent_alert - { - // ... - error_code error; - }; - - -torrent_paused_alert --------------------- - -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. - -There are no additional data members in this alert. - -torrent_resumed_alert ---------------------- - -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. - -There are no additional data members in this alert. - -save_resume_data_alert ----------------------- - -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. -The ``resume_data`` member points to the resume data. - -:: - - struct save_resume_data_alert: torrent_alert - { - // ... - boost::shared_ptr resume_data; - }; - -save_resume_data_failed_alert ------------------------------ - -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 save_resume_data_failed_alert: torrent_alert - { - // ... - error_code error; - }; - -stats_alert ------------ - -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 stats_alert: torrent_alert - { - // ... - enum stats_channel - { - upload_payload, - upload_protocol, - upload_ip_protocol, - upload_dht_protocol, - upload_tracker_protocol, - download_payload, - download_protocol, - download_ip_protocol, - download_dht_protocol, - download_tracker_protocol, - num_channels - }; - - int transferred[num_channels]; - int interval; - }; - -``transferred`` this is an array of samples. The enum describes what each -sample is a measurement of. All of these are raw, and not smoothing is performed. - -``interval`` 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. - - -cache_flushed_alert -------------------- - -This alert is posted when the disk cache has been flushed for a specific torrent -as a result of a call to `flush_cache()`_. This alert belongs to the -``storage_notification`` category, which must be enabled to let this alert through. - -:: - - struct flush_cached_alert: torrent_alert - { - // ... - }; - -dht_announce_alert ------------------- - -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 dht_announce_alert: alert - { - // ... - address ip; - int port; - sha1_hash info_hash; - }; - -dht_get_peers_alert -------------------- - -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 dht_get_peers_alert: alert - { - // ... - sha1_hash info_hash; - }; - -anonymous_mode_alert --------------------- - -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 anonymous_mode_alert: tracker_alert - { - // ... - enum kind_t - { - tracker_not_anonymous = 1 - }; - int kind; - std::string str; - }; - -``kind`` specifies what error this is, it's one of: - -``tracker_not_anonymous`` 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. - -alert dispatcher -================ - -The ``handle_alert`` class is defined in ````. - -Examples usage:: - - struct my_handler - { - void operator()(portmap_error_alert const& a) const - { - std::cout << "Portmapper: " << a.msg << std::endl; - } - - void operator()(tracker_warning_alert const& a) const - { - std::cout << "Tracker warning: " << a.msg << std::endl; - } - - void operator()(torrent_finished_alert const& a) const - { - // write fast resume data - // ... - - std::cout << a.handle.get_torrent_info().name() << "completed" - << std::endl; - } - }; - -:: - - std::auto_ptr a; - a = ses.pop_alert(); - my_handler h; - while (a.get()) - { - handle_alert::handle_alert(h, a); - a = ses.pop_alert(); - } - -In this example 3 alert types are used. You can use any number of template -parameters to select between more types. If the number of types are more than -15, you can define ``TORRENT_MAX_ALERT_TYPES`` to a greater number before -including ````. - - -exceptions -========== - -Many functions in libtorrent have two versions, one that throws exceptions on -errors and one that takes an ``error_code`` reference which is filled with the -error code on errors. - -There is one exception class that is used for errors in libtorrent, it is based -on boost.system's ``error_code`` class to carry the error code. - -libtorrent_exception --------------------- - -:: - - struct libtorrent_exception: std::exception - { - libtorrent_exception(error_code const& s); - virtual const char* what() const throw(); - virtual ~libtorrent_exception() throw() {} - boost::system::error_code error() const; - }; - - -error_code -========== - -libtorrent uses boost.system's ``error_code`` class to represent errors. libtorrent has -its own error category (``libtorrent::libtorrent_category``) whith the following error -codes: - -====== ========================================= ================================================================= -code symbol description -====== ========================================= ================================================================= -0 no_error Not an error ------- ----------------------------------------- ----------------------------------------------------------------- -1 file_collision Two torrents has files which end up overwriting each other ------- ----------------------------------------- ----------------------------------------------------------------- -2 failed_hash_check A piece did not match its piece hash ------- ----------------------------------------- ----------------------------------------------------------------- -3 torrent_is_no_dict The .torrent file does not contain a bencoded dictionary at - its top level ------- ----------------------------------------- ----------------------------------------------------------------- -4 torrent_missing_info The .torrent file does not have an ``info`` dictionary ------- ----------------------------------------- ----------------------------------------------------------------- -5 torrent_info_no_dict The .torrent file's ``info`` entry is not a dictionary ------- ----------------------------------------- ----------------------------------------------------------------- -6 torrent_missing_piece_length The .torrent file does not have a ``piece length`` entry ------- ----------------------------------------- ----------------------------------------------------------------- -7 torrent_missing_name The .torrent file does not have a ``name`` entry ------- ----------------------------------------- ----------------------------------------------------------------- -8 torrent_invalid_name The .torrent file's name entry is invalid ------- ----------------------------------------- ----------------------------------------------------------------- -9 torrent_invalid_length The length of a file, or of the whole .torrent file is invalid. - Either negative or not an integer ------- ----------------------------------------- ----------------------------------------------------------------- -10 torrent_file_parse_failed Failed to parse a file entry in the .torrent ------- ----------------------------------------- ----------------------------------------------------------------- -11 torrent_missing_pieces The ``pieces`` field is missing or invalid in the .torrent file ------- ----------------------------------------- ----------------------------------------------------------------- -12 torrent_invalid_hashes The ``pieces`` string has incorrect length ------- ----------------------------------------- ----------------------------------------------------------------- -13 too_many_pieces_in_torrent The .torrent file has more pieces than is supported by libtorrent ------- ----------------------------------------- ----------------------------------------------------------------- -14 invalid_swarm_metadata The metadata (.torrent file) that was received from the swarm - matched the info-hash, but failed to be parsed ------- ----------------------------------------- ----------------------------------------------------------------- -15 invalid_bencoding The file or buffer is not correctly bencoded ------- ----------------------------------------- ----------------------------------------------------------------- -16 no_files_in_torrent The .torrent file does not contain any files ------- ----------------------------------------- ----------------------------------------------------------------- -17 invalid_escaped_string The string was not properly url-encoded as expected ------- ----------------------------------------- ----------------------------------------------------------------- -18 session_is_closing Operation is not permitted since the session is shutting down ------- ----------------------------------------- ----------------------------------------------------------------- -19 duplicate_torrent There's already a torrent with that info-hash added to the - session ------- ----------------------------------------- ----------------------------------------------------------------- -20 invalid_torrent_handle The supplied torrent_handle is not referring to a valid torrent ------- ----------------------------------------- ----------------------------------------------------------------- -21 invalid_entry_type The type requested from the entry did not match its type ------- ----------------------------------------- ----------------------------------------------------------------- -22 missing_info_hash_in_uri The specified URI does not contain a valid info-hash ------- ----------------------------------------- ----------------------------------------------------------------- -23 file_too_short One of the files in the torrent was unexpectadly small. This - might be caused by files being changed by an external process ------- ----------------------------------------- ----------------------------------------------------------------- -24 unsupported_url_protocol The URL used an unknown protocol. Currently ``http`` and - ``https`` (if built with openssl support) are recognized. For - trackers ``udp`` is recognized as well. ------- ----------------------------------------- ----------------------------------------------------------------- -25 url_parse_error The URL did not conform to URL syntax and failed to be parsed ------- ----------------------------------------- ----------------------------------------------------------------- -26 peer_sent_empty_piece The peer sent a 'piece' message of length 0 ------- ----------------------------------------- ----------------------------------------------------------------- -27 parse_failed A bencoded structure was currupt and failed to be parsed ------- ----------------------------------------- ----------------------------------------------------------------- -28 invalid_file_tag The fast resume file was missing or had an invalid file version - tag ------- ----------------------------------------- ----------------------------------------------------------------- -29 missing_info_hash The fast resume file was missing or had an invalid info-hash ------- ----------------------------------------- ----------------------------------------------------------------- -30 mismatching_info_hash The info-hash in the resume file did not match the torrent ------- ----------------------------------------- ----------------------------------------------------------------- -31 invalid_hostname The URL contained an invalid hostname ------- ----------------------------------------- ----------------------------------------------------------------- -32 invalid_port The URL had an invalid port ------- ----------------------------------------- ----------------------------------------------------------------- -33 port_blocked The port is blocked by the port-filter, and prevented the - connection ------- ----------------------------------------- ----------------------------------------------------------------- -34 expected_close_bracket_in_address The IPv6 address was expected to end with ']' ------- ----------------------------------------- ----------------------------------------------------------------- -35 destructing_torrent The torrent is being destructed, preventing the operation to - succeed ------- ----------------------------------------- ----------------------------------------------------------------- -36 timed_out The connection timed out ------- ----------------------------------------- ----------------------------------------------------------------- -37 upload_upload_connection The peer is upload only, and we are upload only. There's no point - in keeping the connection ------- ----------------------------------------- ----------------------------------------------------------------- -38 uninteresting_upload_peer The peer is upload only, and we're not interested in it. There's - no point in keeping the connection ------- ----------------------------------------- ----------------------------------------------------------------- -39 invalid_info_hash The peer sent an unknown info-hash ------- ----------------------------------------- ----------------------------------------------------------------- -40 torrent_paused The torrent is paused, preventing the operation from succeeding ------- ----------------------------------------- ----------------------------------------------------------------- -41 invalid_have The peer sent an invalid have message, either wrong size or - referring to a piece that doesn't exist in the torrent ------- ----------------------------------------- ----------------------------------------------------------------- -42 invalid_bitfield_size The bitfield message had the incorrect size ------- ----------------------------------------- ----------------------------------------------------------------- -43 too_many_requests_when_choked The peer kept requesting pieces after it was choked, possible - abuse attempt. ------- ----------------------------------------- ----------------------------------------------------------------- -44 invalid_piece The peer sent a piece message that does not correspond to a - piece request sent by the client ------- ----------------------------------------- ----------------------------------------------------------------- -45 no_memory memory allocation failed ------- ----------------------------------------- ----------------------------------------------------------------- -46 torrent_aborted The torrent is aborted, preventing the operation to succeed ------- ----------------------------------------- ----------------------------------------------------------------- -47 self_connection The peer is a connection to ourself, no point in keeping it ------- ----------------------------------------- ----------------------------------------------------------------- -48 invalid_piece_size The peer sent a piece message with invalid size, either negative - or greater than one block ------- ----------------------------------------- ----------------------------------------------------------------- -49 timed_out_no_interest The peer has not been interesting or interested in us for too - long, no point in keeping it around ------- ----------------------------------------- ----------------------------------------------------------------- -50 timed_out_inactivity The peer has not said anything in a long time, possibly dead ------- ----------------------------------------- ----------------------------------------------------------------- -51 timed_out_no_handshake The peer did not send a handshake within a reasonable amount of - time, it might not be a bittorrent peer ------- ----------------------------------------- ----------------------------------------------------------------- -52 timed_out_no_request The peer has been unchoked for too long without requesting any - data. It might be lying about its interest in us ------- ----------------------------------------- ----------------------------------------------------------------- -53 invalid_choke The peer sent an invalid choke message ------- ----------------------------------------- ----------------------------------------------------------------- -54 invalid_unchoke The peer send an invalid unchoke message ------- ----------------------------------------- ----------------------------------------------------------------- -55 invalid_interested The peer sent an invalid interested message ------- ----------------------------------------- ----------------------------------------------------------------- -56 invalid_not_interested The peer sent an invalid not-interested message ------- ----------------------------------------- ----------------------------------------------------------------- -57 invalid_request The peer sent an invalid piece request message ------- ----------------------------------------- ----------------------------------------------------------------- -58 invalid_hash_list The peer sent an invalid hash-list message (this is part of the - merkle-torrent extension) ------- ----------------------------------------- ----------------------------------------------------------------- -59 invalid_hash_piece The peer sent an invalid hash-piece message (this is part of the - merkle-torrent extension) ------- ----------------------------------------- ----------------------------------------------------------------- -60 invalid_cancel The peer sent an invalid cancel message ------- ----------------------------------------- ----------------------------------------------------------------- -61 invalid_dht_port The peer sent an invalid DHT port-message ------- ----------------------------------------- ----------------------------------------------------------------- -62 invalid_suggest The peer sent an invalid suggest piece-message ------- ----------------------------------------- ----------------------------------------------------------------- -63 invalid_have_all The peer sent an invalid have all-message ------- ----------------------------------------- ----------------------------------------------------------------- -64 invalid_have_none The peer sent an invalid have none-message ------- ----------------------------------------- ----------------------------------------------------------------- -65 invalid_reject The peer sent an invalid reject message ------- ----------------------------------------- ----------------------------------------------------------------- -66 invalid_allow_fast The peer sent an invalid allow fast-message ------- ----------------------------------------- ----------------------------------------------------------------- -67 invalid_extended The peer sent an invalid extesion message ID ------- ----------------------------------------- ----------------------------------------------------------------- -68 invalid_message The peer sent an invalid message ID ------- ----------------------------------------- ----------------------------------------------------------------- -69 sync_hash_not_found The synchronization hash was not found in the encrypted handshake ------- ----------------------------------------- ----------------------------------------------------------------- -70 invalid_encryption_constant The encryption constant in the handshake is invalid ------- ----------------------------------------- ----------------------------------------------------------------- -71 no_plaintext_mode The peer does not support plaintext, which is the selected mode ------- ----------------------------------------- ----------------------------------------------------------------- -72 no_rc4_mode The peer does not support rc4, which is the selected mode ------- ----------------------------------------- ----------------------------------------------------------------- -73 unsupported_encryption_mode The peer does not support any of the encryption modes that the - client supports ------- ----------------------------------------- ----------------------------------------------------------------- -74 unsupported_encryption_mode_selected The peer selected an encryption mode that the client did not - advertise and does not support ------- ----------------------------------------- ----------------------------------------------------------------- -75 invalid_pad_size The pad size used in the encryption handshake is of invalid size ------- ----------------------------------------- ----------------------------------------------------------------- -76 invalid_encrypt_handshake The encryption handshake is invalid ------- ----------------------------------------- ----------------------------------------------------------------- -77 no_incoming_encrypted The client is set to not support incoming encrypted connections - and this is an encrypted connection ------- ----------------------------------------- ----------------------------------------------------------------- -78 no_incoming_regular The client is set to not support incoming regular bittorrent - connections, and this is a regular connection ------- ----------------------------------------- ----------------------------------------------------------------- -79 duplicate_peer_id The client is already connected to this peer-ID ------- ----------------------------------------- ----------------------------------------------------------------- -80 torrent_removed Torrent was removed ------- ----------------------------------------- ----------------------------------------------------------------- -81 packet_too_large The packet size exceeded the upper sanity check-limit ------- ----------------------------------------- ----------------------------------------------------------------- -82 reserved ------- ----------------------------------------- ----------------------------------------------------------------- -83 http_error The web server responded with an error ------- ----------------------------------------- ----------------------------------------------------------------- -84 missing_location The web server response is missing a location header ------- ----------------------------------------- ----------------------------------------------------------------- -85 invalid_redirection The web seed redirected to a path that no longer matches the - .torrent directory structure ------- ----------------------------------------- ----------------------------------------------------------------- -86 redirecting The connection was closed becaused it redirected to a different - URL ------- ----------------------------------------- ----------------------------------------------------------------- -87 invalid_range The HTTP range header is invalid ------- ----------------------------------------- ----------------------------------------------------------------- -88 no_content_length The HTTP response did not have a content length ------- ----------------------------------------- ----------------------------------------------------------------- -89 banned_by_ip_filter The IP is blocked by the IP filter ------- ----------------------------------------- ----------------------------------------------------------------- -90 too_many_connections At the connection limit ------- ----------------------------------------- ----------------------------------------------------------------- -91 peer_banned The peer is marked as banned ------- ----------------------------------------- ----------------------------------------------------------------- -92 stopping_torrent The torrent is stopping, causing the operation to fail ------- ----------------------------------------- ----------------------------------------------------------------- -93 too_many_corrupt_pieces The peer has sent too many corrupt pieces and is banned ------- ----------------------------------------- ----------------------------------------------------------------- -94 torrent_not_ready The torrent is not ready to receive peers ------- ----------------------------------------- ----------------------------------------------------------------- -95 peer_not_constructed The peer is not completely constructed yet ------- ----------------------------------------- ----------------------------------------------------------------- -96 session_closing The session is closing, causing the operation to fail ------- ----------------------------------------- ----------------------------------------------------------------- -97 optimistic_disconnect The peer was disconnected in order to leave room for a - potentially better peer ------- ----------------------------------------- ----------------------------------------------------------------- -98 torrent_finished The torrent is finished ------- ----------------------------------------- ----------------------------------------------------------------- -99 no_router No UPnP router found ------- ----------------------------------------- ----------------------------------------------------------------- -100 metadata_too_large The metadata message says the metadata exceeds the limit ------- ----------------------------------------- ----------------------------------------------------------------- -101 invalid_metadata_request The peer sent an invalid metadata request message ------- ----------------------------------------- ----------------------------------------------------------------- -102 invalid_metadata_size The peer advertised an invalid metadata size ------- ----------------------------------------- ----------------------------------------------------------------- -103 invalid_metadata_offset The peer sent a message with an invalid metadata offset ------- ----------------------------------------- ----------------------------------------------------------------- -104 invalid_metadata_message The peer sent an invalid metadata message ------- ----------------------------------------- ----------------------------------------------------------------- -105 pex_message_too_large The peer sent a peer exchange message that was too large ------- ----------------------------------------- ----------------------------------------------------------------- -106 invalid_pex_message The peer sent an invalid peer exchange message ------- ----------------------------------------- ----------------------------------------------------------------- -107 invalid_lt_tracker_message The peer sent an invalid tracker exchange message ------- ----------------------------------------- ----------------------------------------------------------------- -108 too_frequent_pex The peer sent an pex messages too often. This is a possible - attempt of and attack -====== ========================================= ================================================================= - -NAT-PMP errors: - -====== ========================================= ================================================================= -code symbol description -====== ========================================= ================================================================= -120 unsupported_protocol_version The NAT-PMP router responded with an unsupported protocol version ------- ----------------------------------------- ----------------------------------------------------------------- -121 natpmp_not_authorized You are not authorized to map ports on this NAT-PMP router ------- ----------------------------------------- ----------------------------------------------------------------- -122 network_failure The NAT-PMP router failed because of a network failure ------- ----------------------------------------- ----------------------------------------------------------------- -123 no_resources The NAT-PMP router failed because of lack of resources ------- ----------------------------------------- ----------------------------------------------------------------- -124 unsupported_opcode The NAT-PMP router failed because an unsupported opcode was sent -====== ========================================= ================================================================= - -fastresume data errors: - -====== ========================================= ================================================================= -code symbol description -====== ========================================= ================================================================= -130 missing_file_sizes The resume data file is missing the 'file sizes' entry ------- ----------------------------------------- ----------------------------------------------------------------- -131 no_files_in_resume_data The resume data file 'file sizes' entry is empty ------- ----------------------------------------- ----------------------------------------------------------------- -132 missing_pieces The resume data file is missing the 'pieces' and 'slots' entry ------- ----------------------------------------- ----------------------------------------------------------------- -133 mismatching_number_of_files The number of files in the resume data does not match the number - of files in the torrent ------- ----------------------------------------- ----------------------------------------------------------------- -134 mismatching_files_size One of the files on disk has a different size than in the fast - resume file ------- ----------------------------------------- ----------------------------------------------------------------- -135 mismatching_file_timestamp One of the files on disk has a different timestamp than in the - fast resume file ------- ----------------------------------------- ----------------------------------------------------------------- -136 not_a_dictionary The resume data file is not a dictionary ------- ----------------------------------------- ----------------------------------------------------------------- -137 invalid_blocks_per_piece The 'blocks per piece' entry is invalid in the resume data file ------- ----------------------------------------- ----------------------------------------------------------------- -138 missing_slots The resume file is missing the 'slots' entry, which is required - for torrents with compact allocation ------- ----------------------------------------- ----------------------------------------------------------------- -139 too_many_slots The resume file contains more slots than the torrent ------- ----------------------------------------- ----------------------------------------------------------------- -140 invalid_slot_list The 'slot' entry is invalid in the resume data ------- ----------------------------------------- ----------------------------------------------------------------- -141 invalid_piece_index One index in the 'slot' list is invalid ------- ----------------------------------------- ----------------------------------------------------------------- -142 pieces_need_reorder 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 -====== ========================================= ================================================================= - -HTTP errors: - -====== ========================================= ================================================================= -150 http_parse_error The HTTP header was not correctly formatted ------- ----------------------------------------- ----------------------------------------------------------------- -151 http_missing_location The HTTP response was in the 300-399 range but lacked a location - header ------- ----------------------------------------- ----------------------------------------------------------------- -152 http_failed_decompress The HTTP response was encoded with gzip or deflate but - decompressing it failed -====== ========================================= ================================================================= - -I2P errors: - -====== ========================================= ================================================================= -160 no_i2p_router The URL specified an i2p address, but no i2p router is configured -====== ========================================= ================================================================= - -tracker errors: - -====== ========================================= ================================================================= -170 scrape_not_available The tracker URL doesn't support transforming it into a scrape - URL. i.e. it doesn't contain "announce. ------- ----------------------------------------- ----------------------------------------------------------------- -171 invalid_tracker_response invalid tracker response ------- ----------------------------------------- ----------------------------------------------------------------- -172 invalid_peer_dict invalid peer dictionary entry. Not a dictionary ------- ----------------------------------------- ----------------------------------------------------------------- -173 tracker_failure tracker sent a failure message ------- ----------------------------------------- ----------------------------------------------------------------- -174 invalid_files_entry missing or invalid 'files' entry ------- ----------------------------------------- ----------------------------------------------------------------- -175 invalid_hash_entry missing or invalid 'hash' entry ------- ----------------------------------------- ----------------------------------------------------------------- -176 invalid_peers_entry missing or invalid 'peers' and 'peers6' entry ------- ----------------------------------------- ----------------------------------------------------------------- -177 invalid_tracker_response_length udp tracker response packet has invalid size ------- ----------------------------------------- ----------------------------------------------------------------- -178 invalid_tracker_transaction_id invalid transaction id in udp tracker response ------- ----------------------------------------- ----------------------------------------------------------------- -179 invalid_tracker_action invalid action field in udp tracker response ------- ----------------------------------------- ----------------------------------------------------------------- -190 expected_string expected string in bencoded string ------- ----------------------------------------- ----------------------------------------------------------------- -191 expected_colon expected colon in bencoded string ------- ----------------------------------------- ----------------------------------------------------------------- -192 unexpected_eof unexpected end of file in bencoded string ------- ----------------------------------------- ----------------------------------------------------------------- -193 expected_value expected value (list, dict, int or string) in bencoded string ------- ----------------------------------------- ----------------------------------------------------------------- -194 depth_exceeded bencoded recursion depth limit exceeded ------- ----------------------------------------- ----------------------------------------------------------------- -195 item_limit_exceeded bencoded item count limit exceeded -====== ========================================= ================================================================= - -The names of these error codes are declared in then ``libtorrent::errors`` namespace. - -There is also another error category, ``libtorrent::upnp_category``, defining errors -retrned by UPnP routers. Here's a (possibly incomplete) list of UPnP error codes: - -====== ========================================= ==================================================== -code symbol description -====== ========================================= ==================================================== -0 no_error No error ------- ----------------------------------------- ---------------------------------------------------- -402 invalid_argument One of the arguments in the request is invalid ------- ----------------------------------------- ---------------------------------------------------- -501 action_failed The request failed ------- ----------------------------------------- ---------------------------------------------------- -714 value_not_in_array The specified value does not exist in the array ------- ----------------------------------------- ---------------------------------------------------- -715 source_ip_cannot_be_wildcarded The source IP address cannot be wild-carded, but - must be fully specified ------- ----------------------------------------- ---------------------------------------------------- -716 external_port_cannot_be_wildcarded The external port cannot be wildcarded, but must - be specified ------- ----------------------------------------- ---------------------------------------------------- -718 port_mapping_conflict The port mapping entry specified conflicts with a - mapping assigned previously to another client ------- ----------------------------------------- ---------------------------------------------------- -724 internal_port_must_match_external Internal and external port value must be the same ------- ----------------------------------------- ---------------------------------------------------- -725 only_permanent_leases_supported The NAT implementation only supports permanent - lease times on port mappings ------- ----------------------------------------- ---------------------------------------------------- -726 remote_host_must_be_wildcard RemoteHost must be a wildcard and cannot be a - specific IP addres or DNS name ------- ----------------------------------------- ---------------------------------------------------- -727 external_port_must_be_wildcard ExternalPort must be a wildcard and cannot be a - specific port -====== ========================================= ==================================================== - -The UPnP errors are declared in the ``libtorrent::upnp_errors`` namespace. - -translating error codes ------------------------ - -The error_code::message() function will typically return a localized error string, -for system errors. That is, errors that belong to the generic or system category. - -Errors that belong to the libtorrent error category are not localized however, they -are only available in english. In order to translate libtorrent errors, compare the -error category of the ``error_code`` object against ``libtorrent::libtorrent_category``, -and if matches, you know the error code refers to the list above. You can provide -your own mapping from error code to string, which is localized. In this case, you -cannot rely on ``error_code::message()`` to generate your strings. - -The numeric values of the errors are part of the API and will stay the same, although -new error codes may be appended at the end. - -Here's a simple example of how to translate error codes:: - - std::string error_code_to_string(boost::system::error_code const& ec) - { - if (ec.category() != libtorrent::libtorrent_category) - { - return ec.message(); - } - // the error is a libtorrent error - - int code = ec.value(); - static const char const* swedish[] = - { - "inget fel", - "en fil i torrenten kolliderar med en fil frĺn en annan torrent", - "hash check misslyckades", - "torrent filen är inte en dictionary", - "'info'-nyckeln saknas eller är korrupt i torrentfilen", - "'info'-fältet är inte en dictionary", - "'piece length' fältet saknas eller är korrupt i torrentfilen", - "torrentfilen saknar namnfältet", - "ogiltigt namn i torrentfilen (kan vara en attack)", - // ... more strings here - }; - - // use the default error string in case we don't have it - // in our translated list - if (code < 0 || code >= sizeof(swedish)/sizeof(swedish[0])) - return ec.message(); - - return swedish[code]; - } - -storage_interface -================= - -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). - -The interface looks like this:: - - struct storage_interface - { - virtual bool initialize(bool allocate_files) = 0; - virtual bool has_any_file() = 0; - virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0; - virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0; - virtual int sparse_end(int start) const; - virtual bool move_storage(fs::path save_path) = 0; - virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0; - virtual bool write_resume_data(entry& rd) const = 0; - virtual bool move_slot(int src_slot, int dst_slot) = 0; - virtual bool swap_slots(int slot1, int slot2) = 0; - virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0; - virtual bool rename_file(int file, std::string const& new_name) = 0; - virtual bool release_files() = 0; - virtual bool delete_files() = 0; - virtual void finalize_file(int index) {} - virtual ~storage_interface() {} - - // non virtual functions - - disk_buffer_pool* disk_pool(); - void set_error(boost::filesystem::path const& file, error_code const& ec) const; - error_code const& error() const; - std::string const& error_file() const; - void clear_error(); - }; - - -initialize() ------------- - - :: - - bool initialize(bool allocate_files) = 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. - -has_any_file() --------------- - - :: - - virtual bool has_any_file() = 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. - -readv() writev() ----------------- - - :: - - int readv(file::iovec_t const* buf, int slot, int offset, int num_bufs) = 0; - int write(const char* buf, int slot, int offset, int size) = 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. - -sparse_end() ------------- - - :: - - int sparse_end(int start) const; - -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. - -move_storage() --------------- - - :: - - bool move_storage(fs::path save_path) = 0; - -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()``. - -Returning ``false`` indicates an error occurred. - - -verify_resume_data() --------------------- - - :: - - bool verify_resume_data(lazy_entry const& rd, error_code& error) = 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. - - -write_resume_data() -------------------- - - :: - - bool write_resume_data(entry& rd) const = 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. - - -move_slot() ------------ - - :: - - bool move_slot(int src_slot, int dst_slot) = 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. - - -swap_slots() ------------- - - :: - - bool swap_slots(int slot1, int slot2) = 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. - - -swap_slots3() -------------- - - :: - - bool swap_slots3(int slot1, int slot2, int slot3) = 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. - - -rename_file() -------------- - - :: - - bool rename_file(int file, std::string const& new_name) = 0; - -Rename file with index ``file`` to the thame ``new_name``. If there is an error, -``true`` should be returned. - - -release_files() ---------------- - - :: - - bool release_files() = 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. - - -delete_files() --------------- - - :: - - bool delete_files() = 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(); - }; - -finalize_file() ---------------- - - :: - - virtual void finalize_file(int index); - -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. - -magnet links -============ - -Magnet links are URIs that includes an info-hash, a display name and optionally -a tracker url. The idea behind magnet links is that an end user can click on a -link in a browser and have it handled by a bittorrent application, to start a -download, without any .torrent file. - -The format of the magnet URI is: - -**magnet:?xt=urn:btih:** *Base32 encoded info-hash* [ **&dn=** *name of download* ] [ **&tr=** *tracker URL* ]* - -queuing -======= - -libtorrent supports *queuing*. Which means it makes sure that a limited number of -torrents are being downloaded at any given time, and once a torrent is completely -downloaded, the next in line is started. - -Torrents that are *auto managed* are subject to the queuing and the active torrents -limits. To make a torrent auto managed, set ``auto_managed`` to true when adding the -torrent (see `add_torrent()`_). - -The limits of the number of downloading and seeding torrents are controlled via -``active_downloads``, ``active_seeds`` and ``active_limit`` in session_settings_. -These limits takes non auto managed torrents into account as well. If there are -more non-auto managed torrents being downloaded than the ``active_downloads`` -setting, any auto managed torrents will be queued until torrents are removed so -that the number drops below the limit. - -The default values are 8 active downloads and 5 active seeds. - -At a regular interval, torrents are checked if there needs to be any re-ordering of -which torrents are active and which are queued. This interval can be controlled via -``auto_manage_interval`` in session_settings_. It defaults to every 30 seconds. - -For queuing to work, resume data needs to be saved and restored for all torrents. -See `save_resume_data()`_. - -downloading ------------ - -Torrents that are currently being downloaded or incomplete (with bytes still to download) -are queued. The torrents in the front of the queue are started to be actively downloaded -and the rest are ordered with regards to their queue position. Any newly added torrent -is placed at the end of the queue. Once a torrent is removed or turns into a seed, its -queue position is -1 and all torrents that used to be after it in the queue, decreases their -position in order to fill the gap. - -The queue positions are always in a sequence without any gaps. - -Lower queue position means closer to the front of the queue, and will be started sooner than -torrents with higher queue positions. - -To query a torrent for its position in the queue, or change its position, see: -`queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom()`_. - -seeding -------- - -Auto managed seeding torrents are rotated, so that all of them are allocated a fair -amount of seeding. Torrents with fewer completed *seed cycles* are prioritized for -seeding. A seed cycle is completed when a torrent meets either the share ratio limit -(uploaded bytes / downloaded bytes), the share time ratio (time seeding / time -downloaing) or seed time limit (time seeded). - -The relevant settings to control these limits are ``share_ratio_limit``, -``seed_time_ratio_limit`` and ``seed_time_limit`` in session_settings_. - - -fast resume -=========== - -The fast resume mechanism is a way to remember which pieces are downloaded -and where they are put between sessions. You can generate fast resume data by -calling `save_resume_data()`_ on torrent_handle_. You can -then save this data to disk and use it when resuming the torrent. libtorrent -will not check the piece hashes then, and rely on the information given in the -fast-resume data. The fast-resume data also contains information about which -blocks, in the unfinished pieces, were downloaded, so it will not have to -start from scratch on the partially downloaded pieces. - -To use the fast-resume data you simply give it to `add_torrent()`_, and it -will skip the time consuming checks. It may have to do the checking anyway, if -the fast-resume data is corrupt or doesn't fit the storage for that torrent, -then it will not trust the fast-resume data and just do the checking. - -file format ------------ - -The file format is a bencoded dictionary containing the following fields: - -+--------------------------+--------------------------------------------------------------+ -| ``file-format`` | string: "libtorrent resume file" | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``file-version`` | integer: 1 | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``info-hash`` | string, the info hash of the torrent this data is saved for. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``blocks per piece`` | integer, the number of blocks per piece. Must be: piece_size | -| | / (16 * 1024). Clamped to be within the range [1, 256]. It | -| | is the number of blocks per (normal sized) piece. Usually | -| | each block is 16 * 1024 bytes in size. But if piece size is | -| | greater than 4 megabytes, the block size will increase. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``pieces`` | A string with piece flags, one character per piece. | -| | Bit 1 means we have that piece. | -| | Bit 2 means we have verified that this piece is correct. | -| | This only applies when the torrent is in seed_mode. | -+--------------------------+--------------------------------------------------------------+ -| ``slots`` | list of integers. The list maps slots to piece indices. It | -| | tells which piece is on which slot. If piece index is -2 it | -| | means it is free, that there's no piece there. If it is -1, | -| | means the slot isn't allocated on disk yet. The pieces have | -| | to meet the following requirement: | -| | | -| | If there's a slot at the position of the piece index, | -| | the piece must be located in that slot. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``total_uploaded`` | integer. The number of bytes that have been uploaded in | -| | total for this torrent. | -+--------------------------+--------------------------------------------------------------+ -| ``total_downloaded`` | integer. The number of bytes that have been downloaded in | -| | total for this torrent. | -+--------------------------+--------------------------------------------------------------+ -| ``active_time`` | integer. The number of seconds this torrent has been active. | -| | i.e. not paused. | -+--------------------------+--------------------------------------------------------------+ -| ``seeding_time`` | integer. The number of seconds this torrent has been active | -| | and seeding. | -+--------------------------+--------------------------------------------------------------+ -| ``num_seeds`` | integer. An estimate of the number of seeds on this torrent | -| | when the resume data was saved. This is scrape data or based | -| | on the peer list if scrape data is unavailable. | -+--------------------------+--------------------------------------------------------------+ -| ``num_downloaders`` | integer. An estimate of the number of downloaders on this | -| | torrent when the resume data was last saved. This is used as | -| | an initial estimate until we acquire up-to-date scrape info. | -+--------------------------+--------------------------------------------------------------+ -| ``upload_rate_limit`` | integer. In case this torrent has a per-torrent upload rate | -| | limit, this is that limit. In bytes per second. | -+--------------------------+--------------------------------------------------------------+ -| ``download_rate_limit`` | integer. The download rate limit for this torrent in case | -| | one is set, in bytes per second. | -+--------------------------+--------------------------------------------------------------+ -| ``max_connections`` | integer. The max number of peer connections this torrent | -| | may have, if a limit is set. | -+--------------------------+--------------------------------------------------------------+ -| ``max_uploads`` | integer. The max number of unchoked peers this torrent may | -| | have, if a limit is set. | -+--------------------------+--------------------------------------------------------------+ -| ``seed_mode`` | integer. 1 if the torrent is in seed mode, 0 otherwise. | -+--------------------------+--------------------------------------------------------------+ -| ``file_priority`` | list of integers. One entry per file in the torrent. Each | -| | entry is the priority of the file with the same index. | -+--------------------------+--------------------------------------------------------------+ -| ``piece_priority`` | string of bytes. Each byte is interpreted as an integer and | -| | is the priority of that piece. | -+--------------------------+--------------------------------------------------------------+ -| ``auto_managed`` | integer. 1 if the torrent is auto managed, otherwise 0. | -+--------------------------+--------------------------------------------------------------+ -| ``sequential_download`` | integer. 1 if the torrent is in sequential download mode, | -| | 0 otherwise. | -+--------------------------+--------------------------------------------------------------+ -| ``paused`` | integer. 1 if the torrent is paused, 0 otherwise. | -+--------------------------+--------------------------------------------------------------+ -| ``trackers`` | list of lists of strings. The top level list lists all | -| | tracker tiers. Each second level list is one tier of | -| | trackers. | -+--------------------------+--------------------------------------------------------------+ -| ``mapped_files`` | list of strings. If any file in the torrent has been | -| | renamed, this entry contains a list of all the filenames. | -| | In the same order as in the torrent file. | -+--------------------------+--------------------------------------------------------------+ -| ``url-list`` | list of strings. List of url-seed URLs used by this torrent. | -| | The urls are expected to be properly encoded and not contain | -| | any illegal url characters. | -+--------------------------+--------------------------------------------------------------+ -| ``httpseeds`` | list of strings. List of httpseed URLs used by this torrent. | -| | The urls are expected to be properly encoded and not contain | -| | any illegal url characters. | -+--------------------------+--------------------------------------------------------------+ -| ``merkle tree`` | string. In case this torrent is a merkle torrent, this is a | -| | string containing the entire merkle tree, all nodes, | -| | including the root and all leaves. The tree is not | -| | necessarily complete, but complete enough to be able to send | -| | any piece that we have, indicated by the have bitmask. | -+--------------------------+--------------------------------------------------------------+ -| ``peers`` | list of dictionaries. Each dictionary has the following | -| | layout: | -| | | -| | +----------+-----------------------------------------------+ | -| | | ``ip`` | string, the ip address of the peer. This is | | -| | | | not a binary representation of the ip | | -| | | | address, but the string representation. It | | -| | | | may be an IPv6 string or an IPv4 string. | | -| | +----------+-----------------------------------------------+ | -| | | ``port`` | integer, the listen port of the peer | | -| | +----------+-----------------------------------------------+ | -| | | -| | These are the local peers we were connected to when this | -| | fast-resume data was saved. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``unfinished`` | list of dictionaries. Each dictionary represents an | -| | piece, and has the following layout: | -| | | -| | +-------------+--------------------------------------------+ | -| | | ``piece`` | integer, the index of the piece this entry | | -| | | | refers to. | | -| | +-------------+--------------------------------------------+ | -| | | ``bitmask`` | string, a binary bitmask representing the | | -| | | | blocks that have been downloaded in this | | -| | | | piece. | | -| | +-------------+--------------------------------------------+ | -| | | ``adler32`` | The adler32 checksum of the data in the | | -| | | | blocks specified by ``bitmask``. | | -| | | | | | -| | +-------------+--------------------------------------------+ | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``file sizes`` | list where each entry corresponds to a file in the file list | -| | in the metadata. Each entry has a list of two values, the | -| | first value is the size of the file in bytes, the second | -| | is the time stamp when the last time someone wrote to it. | -| | This information is used to compare with the files on disk. | -| | All the files must match exactly this information in order | -| | to consider the resume data as current. Otherwise a full | -| | re-check is issued. | -+--------------------------+--------------------------------------------------------------+ -| ``allocation`` | The allocation mode for the storage. Can be either ``full`` | -| | or ``compact``. If this is full, the file sizes and | -| | timestamps are disregarded. Pieces are assumed not to have | -| | moved around even if the files have been modified after the | -| | last resume data checkpoint. | -+--------------------------+--------------------------------------------------------------+ - -threads -======= - -libtorrent starts 2 or 3 threads. - - * The first thread is the main thread that will sit - idle in a ``select()`` call most of the time. This thread runs the main loop - that will send and receive data on all connections. - - * The second thread is the disk I/O thread. All disk read and write operations - are passed to this thread and messages are passed back to the main thread when - the operation completes. The disk thread also verifies the piece hashes. - - * The third and forth threads are spawned by asio on systems that don't support - non-blocking host name resolution to simulate non-blocking getaddrinfo(). - - -storage allocation -================== - -There are three modes in which storage (files on disk) are allocated in libtorrent. - -1. The traditional *full allocation* mode, where the entire files are filled up with - zeros before anything is downloaded. libtorrent will look for sparse files support - in the filesystem that is used for storage, and use sparse files or file system - zero fill support if present. This means that on NTFS, full allocation mode will - only allocate storage for the downloaded pieces. - -2. The *compact allocation* mode, where only files are allocated for actual - pieces that have been downloaded. - -3. The *sparse allocation*, sparse files are used, and pieces are downloaded directly - to where they belong. This is the recommended (and default) mode. - -The allocation mode is selected when a torrent is started. It is passed as an -argument to ``session::add_torrent()`` (see `add_torrent()`_). - -The decision to use full allocation or compact allocation typically depends on whether -any files have priority 0 and if the filesystem supports sparse files. - -sparse allocation ------------------ - -On filesystems that supports sparse files, this allocation mode will only use -as much space as has been downloaded. - - * It does not require an allocation pass on startup. - - * It supports skipping files (setting prioirty to 0 to not download). - - * Fast resume data will remain valid even when file time stamps are out of date. - - -full allocation ---------------- - -When a torrent is started in full allocation mode, the disk-io thread (see threads_) -will make sure that the entire storage is allocated, and fill any gaps with zeros. -This will be skipped if the filesystem supports sparse files or automatic zero filling. -It will of course still check for existing pieces and fast resume data. The main -drawbacks of this mode are: - - * It may take longer to start the torrent, since it will need to fill the files - with zeros on some systems. This delay is linearly dependent on the size of - the download. - - * The download may occupy unnecessary disk space between download sessions. In case - sparse files are not supported. - - * Disk caches usually perform extremely poorly with random access to large files - and may slow down a download considerably. - -The benefits of this mode are: - - * Downloaded pieces are written directly to their final place in the files and the - total number of disk operations will be fewer and may also play nicer to - filesystems' file allocation, and reduce fragmentation. - - * No risk of a download failing because of a full disk during download. Unless - sparse files are being used. - - * The fast resume data will be more likely to be usable, regardless of crashes or - out of date data, since pieces won't move around. - - * Can be used with prioritizing files to 0. - -compact allocation ------------------- - -The compact allocation will only allocate as much storage as it needs to keep the -pieces downloaded so far. This means that pieces will be moved around to be placed -at their final position in the files while downloading (to make sure the completed -download has all its pieces in the correct place). So, the main drawbacks are: - - * More disk operations while downloading since pieces are moved around. - - * Potentially more fragmentation in the filesystem. - - * Cannot be used while having files with priority 0. - -The benefits though, are: - - * No startup delay, since the files doesn't need allocating. - - * The download will not use unnecessary disk space. - - * Disk caches perform much better than in full allocation and raises the download - speed limit imposed by the disk. - - * Works well on filesystems that doesn't support sparse files. - -The algorithm that is used when allocating pieces and slots isn't very complicated. -For the interested, a description follows. - -storing a piece: - -1. let **A** be a newly downloaded piece, with index **n**. -2. let **s** be the number of slots allocated in the file we're - downloading to. (the number of pieces it has room for). -3. if **n** >= **s** then allocate a new slot and put the piece there. -4. if **n** < **s** then allocate a new slot, move the data at - slot **n** to the new slot and put **A** in slot **n**. - -allocating a new slot: - -1. if there's an unassigned slot (a slot that doesn't - contain any piece), return that slot index. -2. append the new slot at the end of the file (or find an unused slot). -3. let **i** be the index of newly allocated slot -4. if we have downloaded piece index **i** already (to slot **j**) then - - 1. move the data at slot **j** to slot **i**. - 2. return slot index **j** as the newly allocated free slot. - -5. return **i** as the newly allocated slot. - - -extensions -========== - -These extensions all operates within the `extension protocol`__. The -name of the extension is the name used in the extension-list packets, -and the payload is the data in the extended message (not counting the -length-prefix, message-id nor extension-id). - -__ extension_protocol.html - -Note that since this protocol relies on one of the reserved bits in the -handshake, it may be incompatible with future versions of the mainline -bittorrent client. - -These are the extensions that are currently implemented. - -metadata from peers -------------------- - -Extension name: "LT_metadata" - -The point with this extension is that you don't have to distribute the -metadata (.torrent-file) separately. The metadata can be distributed -through the bittorrent swarm. The only thing you need to download such -a torrent is the tracker url and the info-hash of the torrent. - -It works by assuming that the initial seeder has the metadata and that -the metadata will propagate through the network as more peers join. - -There are three kinds of messages in the metadata extension. These packets -are put as payload to the extension message. The three packets are: - - * request metadata - * metadata - * don't have metadata - -request metadata: - -+-----------+---------------+----------------------------------------+ -| size | name | description | -+===========+===============+========================================+ -| uint8_t | msg_type | Determines the kind of message this is | -| | | 0 means 'request metadata' | -+-----------+---------------+----------------------------------------+ -| uint8_t | start | The start of the metadata block that | -| | | is requested. It is given in 256:ths | -| | | of the total size of the metadata, | -| | | since the requesting client don't know | -| | | the size of the metadata. | -+-----------+---------------+----------------------------------------+ -| uint8_t | size | The size of the metadata block that is | -| | | requested. This is also given in | -| | | 256:ths of the total size of the | -| | | metadata. The size is given as size-1. | -| | | That means that if this field is set | -| | | 0, the request wants one 256:th of the | -| | | metadata. | -+-----------+---------------+----------------------------------------+ - -metadata: - -+-----------+---------------+----------------------------------------+ -| size | name | description | -+===========+===============+========================================+ -| uint8_t | msg_type | 1 means 'metadata' | -+-----------+---------------+----------------------------------------+ -| int32_t | total_size | The total size of the metadata, given | -| | | in number of bytes. | -+-----------+---------------+----------------------------------------+ -| int32_t | offset | The offset of where the metadata block | -| | | in this message belongs in the final | -| | | metadata. This is given in bytes. | -+-----------+---------------+----------------------------------------+ -| uint8_t[] | metadata | The actual metadata block. The size of | -| | | this part is given implicit by the | -| | | length prefix in the bittorrent | -| | | protocol packet. | -+-----------+---------------+----------------------------------------+ - -Don't have metadata: - -+-----------+---------------+----------------------------------------+ -| size | name | description | -+===========+===============+========================================+ -| uint8_t | msg_type | 2 means 'I don't have metadata'. | -| | | This message is sent as a reply to a | -| | | metadata request if the the client | -| | | doesn't have any metadata. | -+-----------+---------------+----------------------------------------+ - -HTTP seeding ------------- - -There are two kinds of HTTP seeding. One with that assumes a smart -(and polite) client and one that assumes a smart server. These -are specified in `BEP 19`_ and `BEP 17`_ respectively. - -libtorrent supports both. In the libtorrent source code and API, -BEP 19 urls are typically referred to as *url seeds* and BEP 17 -urls are typically referred to as *HTTP seeds*. - -The libtorrent implementation of `BEP 19`_ assumes that, if the URL ends with a slash -('/'), the filename should be appended to it in order to request pieces from -that file. The way this works is that if the torrent is a single-file torrent, -only that filename is appended. If the torrent is a multi-file torrent, the -torrent's name '/' the file name is appended. This is the same directory -structure that libtorrent will download torrents into. - -.. _`BEP 17`: http://bittorrent.org/beps/bep_0017.html -.. _`BEP 19`: http://bittorrent.org/beps/bep_0019.html - -filename checks -=============== - -Boost.Filesystem will by default check all its paths to make sure they conform -to filename requirements on many platforms. If you don't want this check, you can -set it to either only check for native filesystem requirements or turn it off -altogether. You can use:: - - boost::filesystem::path::default_name_check(boost::filesystem::native); - -for example. For more information, see the `Boost.Filesystem docs`__. - -__ http://www.boost.org/libs/filesystem/doc/index.htm - diff --git a/libtorrent_utp/docs/merkle_tree.graffle b/libtorrent_utp/docs/merkle_tree.graffle deleted file mode 100644 index 4e474e9ca..000000000 --- a/libtorrent_utp/docs/merkle_tree.graffle +++ /dev/null @@ -1,1718 +0,0 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 137.11.0.108132 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {576, 733}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2009-05-24 19:30:52 -0700 - Creator - Arvid Norberg - DisplayScale - 1.000 cm = 1.000 cm - GraphDocumentVersion - 6 - GraphicsList - - - Bounds - {{9, 163}, {81, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - b - 0 - g - 0 - r - 0 - - Font - Helvetica - Size - 12 - - ID - 59 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 piece hashes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 40 - - ID - 58 - Points - - {74, 99} - {95.4083, 136.566} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 47 - Info - 1 - - - - Class - LineGraphic - Head - - ID - 1 - - ID - 57 - Points - - {74, 99} - {46.1044, 136.598} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - - - Class - LineGraphic - Head - - ID - 42 - - ID - 56 - Points - - {192, 99} - {216.65, 136.582} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 48 - Info - 1 - - - - Class - LineGraphic - Head - - ID - 41 - - ID - 55 - Points - - {192, 99} - {167.35, 136.582} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 48 - Info - 1 - - - - Class - LineGraphic - Head - - ID - 46 - - ID - 54 - Points - - {432, 99} - {459.896, 136.598} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 50 - Info - 1 - - - - Class - LineGraphic - Head - - ID - 45 - - ID - 53 - Points - - {432, 99} - {410.592, 136.566} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 50 - Info - 1 - - - - Class - LineGraphic - Head - - ID - 44 - - ID - 52 - Points - - {314, 99} - {338.651, 136.582} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 51 - - - - Class - LineGraphic - Head - - ID - 43 - - ID - 51 - Points - - {314, 99} - {289.349, 136.582} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 49 - Info - 1 - - - - Class - LineGraphic - ID - 50 - Points - - {375, 60.5} - {432, 99} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 49 - - - - Class - LineGraphic - ID - 49 - Points - - {375, 60.5} - {314, 99} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - - - Class - LineGraphic - ID - 48 - Points - - {135, 60.5} - {192, 99} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 47 - - - - Class - LineGraphic - ID - 47 - Points - - {135, 60.5} - {74, 99} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - - - Bounds - {{436, 137}, {61, 17}} - Class - ShapedGraphic - ID - 46 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 7} - - - - Bounds - {{375, 137}, {61, 17}} - Class - ShapedGraphic - ID - 45 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 6} - - - - Bounds - {{314, 137}, {61, 17}} - Class - ShapedGraphic - ID - 44 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 5} - - - - Bounds - {{253, 137}, {61, 17}} - Class - ShapedGraphic - ID - 43 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 4} - - - - Bounds - {{192, 137}, {61, 17}} - Class - ShapedGraphic - ID - 42 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 3} - - - - Bounds - {{131, 137}, {61, 17}} - Class - ShapedGraphic - ID - 41 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 2} - - - - Bounds - {{70, 137}, {61, 17}} - Class - ShapedGraphic - ID - 40 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 1} - - - - Bounds - {{9, 137}, {61, 17}} - Class - ShapedGraphic - ID - 1 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 0} - - - - Class - LineGraphic - Head - - ID - 49 - - ID - 32 - Points - - {253, 36} - {375, 60.5} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - - - Class - LineGraphic - Head - - ID - 47 - - ID - 31 - Points - - {253, 36} - {135, 60.5} - - Style - - stroke - - HeadArrow - 0 - TailArrow - FilledArrow - - - Tail - - ID - 32 - - - - Bounds - {{227, 7}, {61, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - b - 0 - g - 0 - r - 0 - - Font - Helvetica - Size - 12 - - ID - 3 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 root hash} - - Wrap - NO - - - GridInfo - - SnapsToGrid - YES - - GuidesLocked - NO - GuidesVisible - YES - HPages - 1 - ImageCounter - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2009-05-24 19:41:56 -0700 - Modifier - Arvid Norberg - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - PageBreaks - YES - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {612, 792} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - QuickLookPreview - - JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGVWMtu5DYQvOsreMwelstm83mNkwDZ - UxIbyHlhjGEvxvH6kXx/qimR4kiyPOOBAXvEZheL1UW2ntWf6lkZfHwMKjKrl4P6W/2j - jA6m/KgH9eXqldTtq6Lyeb1Vn43209P5r2mCARPcqS9/HF5uDz/e/v12VC8PSJG4ZOGs - Plvlg1O3j+rL74+kfnkqEObnPk/PBzy36+dkAbZNwBsDEnUDHAYMssg5g3U9BL+egQ13 - M4SNAcgPDMO4iLge4KzvZkjjAPBohUejwKERYv86HL+9Pfx3uHo6Pr08PB7eXh5uh8IX - Fbo+k7I+aq8iFcZ+vlFkpycWTHo8cuoGVP5G2mB7bu7UTy9PT2/q/tvr/afh5rv69Qb8 - kvqK3+/rXb26LrMZdX0FilpSU6SA1VnHmk2IAydtCFjUoyL2KmDf1VFdY+6qDhFRWZn1 - pIlzVCEHHYgTYkC5xr8Z30WdrYvDscydKQR8Z7WR6e6x2nXsHab9it/v2winiIGD5hQs - 99nYaxO9DZhbVlKyOaOzy3mRr4u+hlwqYZucBKujCVYxluKytcjI8YQT650GljQUCiwk - DQoQRuQxDsslUwDYwNo2WrKliYQxeiRwjL4b9kmYImQZ0ZlCec2HBadc+K3pBuHFxMyn - 6bpg2VgqH0gVBRmxc/g/ij3cvcuO1EOGPrD1dfCkkFPdP2MGEQvkzRlC8kk0juCFvqE9 - DhnSd8NC4+aTqtredaooxnaC/XlouZvKMeoi0GRE/gW1LPld1MvKpF3UACYfEE4MB1vA - nilrsGXYJu6hN5mObApUce+y3eEexFHsLu6CesSdYaz7uIUvwrBN3Cfm2OG21p6Fe6kS - PhO39fD7E9xDl73xLcMuw5244r5IJ24X96wTRmmc4t60SBm2ifs9nbBzFfdFOvG7uGed - iFt+gFv4kmGbuN/TiTNyIF7uJuFM3I7DAvemTmTYZbhDqLgv0kncxr3Ubqk5i7PYJhW8 - jomzw4EUncoZJ8DmIU4M8824D4ZI2qVyguH818YGnGoh64hiOOI2krS1qOcQnA4xjwfY - VvD7p3iB19LBocgR4NVsSs5IF41MPadLmq1BXrk0zOnG4OGxrGnvDCfcBqzBbRTMG0e4 - IYy21AghHIY+Oj+U9XPwMsBhfutAIhbroo2CCKWSEoxYOHEOD0dEY/TI3hi9f4LXfDg5 - tQ+Mefp8CXkjdm/ON4AV74FmkW+OHo/w8d63VIRUNWPpvSLAmhhFY4BjUQC2dlYAVlIU - UFY7KoDDSgHDPUp3Hfy+AgqcFjFuItZfs8laqwLmdL0C5nQteG/5xVzgzUsFOLb9+td7 - yAnzA0pTAK6xnLYUwHEdvUvAMEfMezjnmxUw5+sVsIo+owjYYNaIXodM1DaibcItNWXN - DpfTIshEMIhBqGSyOhfPZqsdkVyz0RhoE3ApDDZqDlGqkQmNSEyoEevwB+pG6qFGYydb - 9C4bLQLYcNdKSRqPlo/g85gpCfs1H2Ex2aI16fP10XuCgAAHJhSwRdvSscFoe4LH3Xlm - o1gLE+rA4brerYcpAADas7pyQWdJZ0bn2TEkbKyjd9kAtimiZ6Pm61be8lWGJjYW0Wdo - w2FLTU64qEEbwZT9doStQA81shE82qqiDcfYcy+HAHYX4pGGyGH7XS7aCDBIOPoR3+HY - MA4Nn5VeDIeL7FWNLtqYonfZaBFld7E94pUtH0GUaLMy2G/50G1pk4L49Zyvj/5IGw6t - XsoJvQ1mz96IGp3P+A4kFG3kJE4s8zgcZYSNGLURy1nm0HC5lGXlTttAolyHvgwVKHrB - 5OhzJnTr6F02hpavaGPsFls+0UZkJOnyiTYo4Mga2Zjy1egztEFJvO3UNyjAAjZ8gzLa - 7YVvEBxz6RsE31n7Ro0+3zdqRNndyTdavloVYL/l66oHN4mKtquzj7RBeKOx9A2L2tzy - Dcq88g3KbuUblPOGb2xF72ujRXTrafm6lbd8laGijVX0GdrAbYAMEZ1UCirSoIS6Sim+ - ES1ekkRUVXHRsVJwwUDHf1oVEbaDCVBRU/UUdDV61MYYvcuGqhGjNsZKaflqVUAbLV9X - Pfeb0R9pI+JMMoSDtXPRDCswCQdV8Y3iosU3ImTg8Laqd1FcImz2aCeqYwKdXEFgG/Jd - c1ag24jeZWNoEaKNyUVbvs4xW77qrCP7NV+NnrSxesfZXT1dedfj01ZPefomEy2uvHf4 - 8XC4PZRXmYfXudX5839hibOwCmVuZHN0cmVhbQplbmRvYmoKNSAwIG9iagoxNjc0CmVu - ZG9iagoyIDAgb2JqCjw8IC9UeXBlIC9QYWdlIC9QYXJlbnQgMyAwIFIgL1Jlc291cmNl - cyA2IDAgUiAvQ29udGVudHMgNCAwIFIgL01lZGlhQm94IFswIDAgNTc2IDczM10KPj4K - ZW5kb2JqCjYgMCBvYmoKPDwgL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0lt - YWdlQyAvSW1hZ2VJIF0gL0NvbG9yU3BhY2UgPDwgL0NzMSA3IDAgUgovQ3MyIDI0IDAg - UiA+PiAvRm9udCA8PCAvRjEuMCAyNSAwIFIgPj4gL1hPYmplY3QgPDwgL0ltNSAxNiAw - IFIgL0ltMSA4IDAgUgovSW04IDIyIDAgUiAvSW0zIDEyIDAgUiAvSW00IDE0IDAgUiAv - SW03IDIwIDAgUiAvSW0yIDEwIDAgUiAvSW02IDE4IDAgUiA+Pgo+PgplbmRvYmoKMTYg - MCBvYmoKPDwgL0xlbmd0aCAxNyAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0lt - YWdlIC9XaWR0aCAxNjYgL0hlaWdodCA3OCAvQ29sb3JTcGFjZQoyNiAwIFIgL1NNYXNr - IDI3IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+ - CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0PiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwa+BgaXvAABCmVu - ZHN0cmVhbQplbmRvYmoKMTcgMCBvYmoKMTkzCmVuZG9iago4IDAgb2JqCjw8IC9MZW5n - dGggOSAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAxNjYg - L0hlaWdodCA3OCAvQ29sb3JTcGFjZQoyNiAwIFIgL1NNYXNrIDI5IDAgUiAvQml0c1Bl - ckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEA - AADCoPVPbQ0PiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwa+BgaXvAABCmVuZHN0cmVhbQplbmRvYmoK - OSAwIG9iagoxOTMKZW5kb2JqCjIyIDAgb2JqCjw8IC9MZW5ndGggMjMgMCBSIC9UeXBl - IC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTY2IC9IZWlnaHQgNzggL0Nv - bG9yU3BhY2UKMjYgMCBSIC9TTWFzayAzMSAwIFIgL0JpdHNQZXJDb21wb25lbnQgOCAv - RmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt0DEBAAAAwqD1T20ND4hAYcCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGvgYGl7wAAQplbmRzdHJlYW0KZW5kb2JqCjIzIDAgb2JqCjE5Mwpl - bmRvYmoKMTIgMCBvYmoKPDwgL0xlbmd0aCAxMyAwIFIgL1R5cGUgL1hPYmplY3QgL1N1 - YnR5cGUgL0ltYWdlIC9XaWR0aCAxNjYgL0hlaWdodCA3OCAvQ29sb3JTcGFjZQoyNiAw - IFIgL1NNYXNrIDMzIDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRl - RGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0PiEBhwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwa+ - BgaXvAABCmVuZHN0cmVhbQplbmRvYmoKMTMgMCBvYmoKMTkzCmVuZG9iagoxNCAwIG9i - ago8PCAvTGVuZ3RoIDE1IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug - L1dpZHRoIDE2NiAvSGVpZ2h0IDc4IC9Db2xvclNwYWNlCjI2IDAgUiAvU01hc2sgMzUg - MCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3Ry - ZWFtCngB7dAxAQAAAMKg9U9tDQ+IQGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBr4GBpe8AAEKZW5kc3Ry - ZWFtCmVuZG9iagoxNSAwIG9iagoxOTMKZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGgg - MjEgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTY2IC9I - ZWlnaHQgNzggL0NvbG9yU3BhY2UKMjYgMCBSIC9TTWFzayAzNyAwIFIgL0JpdHNQZXJD - b21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt0DEBAAAA - wqD1T20ND4hAYcCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGvgYGl7wAAQplbmRzdHJlYW0KZW5kb2JqCjIx - IDAgb2JqCjE5MwplbmRvYmoKMTAgMCBvYmoKPDwgL0xlbmd0aCAxMSAwIFIgL1R5cGUg - L1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAxNjYgL0hlaWdodCA3OCAvQ29s - b3JTcGFjZQoyNiAwIFIgL1NNYXNrIDM5IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9G - aWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0PiEBhwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwa+BgaXvAABCmVuZHN0cmVhbQplbmRvYmoKMTEgMCBvYmoKMTkzCmVu - ZG9iagoxOCAwIG9iago8PCAvTGVuZ3RoIDE5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3Vi - dHlwZSAvSW1hZ2UgL1dpZHRoIDE2NiAvSGVpZ2h0IDc4IC9Db2xvclNwYWNlCjI2IDAg - UiAvU01hc2sgNDEgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE - ZWNvZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9tDQ+IQGHAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBr4G - Bpe8AAEKZW5kc3RyZWFtCmVuZG9iagoxOSAwIG9iagoxOTMKZW5kb2JqCjI5IDAgb2Jq - Cjw8IC9MZW5ndGggMzAgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAv - V2lkdGggMTY2IC9IZWlnaHQgNzggL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQ - ZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtmulP - GmsUxrWiyA6KoCwXHNwAkU7FoqIFAnG37hZbNSpqikWpRiKpSzFWicS11bjErcYlao0a - osZUc/+1ewZ701Sx3vthDJP6fJpP8/7ynHPed+Y9JyTkUY8OPDrwZzoQ+uD6Xz4D3ZOf - CnsA/VztCSz+H2CvCYGMRAp/YJFIsCwGfB+on/EaMIJMjrwWBWf9WIZMjgBbAPUezh+M - 4eERAEihUmk0Gp1OZ+AuWASWolIpkZEY6e85McgwiDMgAiCdwWSy2GzOg4jNZjGZDGCl - Aug15x1h90OCjxgjg8nicKKiudyYGB6Pj7N4vJgYLjc6isNhMRkYJ/gJYQ+MiTmJGYkx - soGQx4+NEwiEIpEYZ4lEQoEgLpbPA1K2nxPsxDADFLsfEhKSRgdGQAQ+sUQqjUdkuAuJ - l0olYmAFUOCk07D0DIwZiuUkmQJGcqJ5sQIgRGSJSckpcrlCocRRCoVcnpKclChDgFQQ - y4vmgJ0UMlZDt80EKwEyksZgcbh8gViKJCTJFakqtRpF0We4ChZQq1WpCnlSAiIVC/hc - DosBboaTAsQcrITCofohhRIkMUWpUqPpGc+1WdkgHW7C3p6lfZ6RjqpVypRERCL0Y1Kh - hAKYGQpWRlLpTA43ViiRJSvTUI02S5erNxhNJpMZR8HrjQZ9ri5Lq0HTlMkyiTCWy2HS - qZFg5s2QX1tJA0i+UJogV6GaTJ3eaM4rKCwuKX2Jq0pLigsL8sxGvS5Tg6rkCVLMTSYt - kJkYJcSbDZCSBLk6XZtjMOUXlZZVVtXUWnBWbU1VZVlpUb7JkKNNV8sTsKCzGWDmrZBD - wCMiacwonkAik6dpsvXmwpKKasubhsamFqu1FUdZrS1NjQ1vLNUVJYVmfbYmTS6TCHhR - mJm3Qh76BHYhsDJWjCSr0rMNecXlNXUNTdZ22zt7Z5cDR3V12t/Z2q1NDXU15cV5hux0 - VTIijgUzYTe6mZhYwCEreQJpohLV6vNKKi31zW02u6On19nnwlV9zt4eh93W1lxvqSzJ - 02tRZaJUwMMyE0L+6/Hjp2RF88VISpomxwyQjVZbZ7fT1T845B7GVe6hwX6Xs7vTZm0E - THOOJi0FEfOjWQEpIyh0NjdOkqBEMw2F5ZbG1g6H0zXoHhn1jHtx1bhndMQ96HI6Olob - LeWFhkxUmSCJ47LplIhbXpIiKAws4Ekqjc5UWlNv7Xjv7Hd/8ngnp2c+46qZ6Umv55O7 - 3/m+w1pfU2rSaVRJWMgZFCifGxEnkanMKL4ISVFrX+RX1DW/dTj7h8e8U7Nz84uLSzhq - cXF+bnbKOzbc73S8ba6ryH+hVacgIn4Uk0oOQEljQlrKFGiWsai6oc0OkJ6JmbmF5ZW1 - 9Q0ctb62srwwNzPhAUx7W0N1kTELVcggMZm0AJRQ4ty4vxJT0yHgliZbt+vj2MTs/NLq - xubW9g6O2t7a3Fhdmp+dGPvo6rY1WSDk6amJf8VxochveQkbEYuLpWVGbl7Za6vdOTDi - nZlfXvu6vbu3f4Cj9vd2t7+uLc/PeEcGnHbr67K83AwsMblYkd/MS6BkA2WyWqsvqGxo - d7jcnqm5pbXNnf2Dw6NjHHV0eLC/s7m2NDflcbsc7Q2VBXqtOhko2QEp6ewYQTwUj6Gw - qtHW82HEO7uwCpCHxyc+XHVyfAiYqwuz3pEPPbbGqkIDlE+8IIZND+QlnRMjRORPs4zF - NU0dvQOjk1+WN7b3Do99p2fnOOrs1Hd8uLe9sfxlcnSgt6OpptiY9VSOCGM4d1DyRIgc - zTaV1LbYnUOeqfmVzd1vR77T8wtcdX7qO/q2u7kyP+UZctpbaktM2agcEfHupoSNCChf - WTv73OPTC2tbe4cnAPn9Ekd9vzg/PTnc21pbmB5393VaX2GUCtm9lKUWa5dr2Du7uL69 - f+Q7A8grHHX5/eLMd7S/vb446x12dVlhK7qLEj7cIukcnsjvZQDKv3HT1e8pf/mnCA0L - h2Mcjh7lM535paXVAV5+XtrYOTj2nV9cXuHGCC++urw49x0f7GwsfQYvHa2Wl2bdMyUc - PnCQh4cRkZIQEQ+5hzI4qucXyqDfiYiwqxPihCTE1wYxvtyI8BVMIsQfBYlMiL8zYvzp - EuLWIIwQNzDYnRsBbrOIcDNIjFtWotxYE+P2H2tKBXsnJYQQXSmgJEaHjwjdUszM4O88 - AyUW82Dv4l9jBvtERAgxpkuuMYN9UifEj+mfKArmqSfoU/2YzsJG3IJ2ggxrp/k5nwT3 - NJ6/7UeAyUY/57+wwPuQ+rn249OjA48O/FkO/AOh7c+tCmVuZHN0cmVhbQplbmRvYmoK - MzAgMCBvYmoKMTcyMgplbmRvYmoKMzUgMCBvYmoKPDwgL0xlbmd0aCAzNiAwIFIgL1R5 - cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAxNjYgL0hlaWdodCA3OCAv - Q29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIg - L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae2a6U8aaxTGtaLIDoqgLBcc3ACRTsWiogUC - cbfuFls1KmqKRalGIqlLMVaJxLXVuMStxiVqjRqixlRz/7V7BnvTVLHe+2EMk/p8mk/z - /vKcc9535j0nJORRjw48OvBnOhD64PpfPgPdk58KewD9XO0JLP4fYK8JgYxECn9gkUiw - LAZ8H6if8RowgkyOvBYFZ/1YhkyOAFsA9R7OH4zh4REASKFSaTQanU5n4C5YBJaiUimR - kRjp7zkxyDCIMyACIJ3BZLLYbM6DiM1mMZkMYKUC6DXnHWH3Q4KPGCODyeJwoqK53JgY - Ho+Ps3i8mBguNzqKw2ExGRgn+AlhD4yJOYkZiTGygZDHj40TCIQikRhniURCgSAuls8D - UrafE+zEMAMUux8SEpJGB0ZABD6xRCqNR2S4C4mXSiViYAVQ4KTTsPQMjBmK5SSZAkZy - onmxAiBEZIlJySlyuUKhxFEKhVyekpyUKEOAVBDLi+aAnRQyVkO3zQQrATKSxmBxuHyB - WIokJMkVqSq1GkXRZ7gKFlCrVakKeVICIhUL+FwOiwFuhpMCxByshMKh+iGFEiQxRalS - o+kZz7VZ2SAdbsLenqV9npGOqlXKlEREIvRjUqGEApgZClZGUulMDjdWKJElK9NQjTZL - l6s3GE0mkxlHweuNBn2uLkurQdOUyTKJMJbLYdKpkWDmzZBfW0kDSL5QmiBXoZpMnd5o - zisoLC4pfYmrSkuKCwvyzEa9LlODquQJUsxNJi2QmRglxJsNkJIEuTpdm2Mw5ReVllVW - 1dRacFZtTVVlWWlRvsmQo01XyxOwoLMZYOatkEPAIyJpzCieQCKTp2my9ebCkopqy5uG - xqYWq7UVR1mtLU2NDW8s1RUlhWZ9tiZNLpMIeFGYmbdCHvoEdiGwMlaMJKvSsw15xeU1 - dQ1N1nbbO3tnlwNHdXXa39narU0NdTXlxXmG7HRVMiKOBTNhN7qZmFjAISt5AmmiEtXq - 80oqLfXNbTa7o6fX2efCVX3O3h6H3dbWXG+pLMnTa1FlolTAwzITQv7r8eOnZEXzxUhK - mibHDJCNVltnt9PVPzjkHsZV7qHBfpezu9NmbQRMc44mLQUR86NZASkjKHQ2N06SoEQz - DYXllsbWDofTNegeGfWMe3HVuGd0xD3ocjo6Whst5YWGTFSZIInjsumUiFtekiIoDCzg - SSqNzlRaU2/teO/sd3/yeCenZz7jqpnpSa/nk7vf+b7DWl9TatJpVElYyBkUKJ8bESeR - qcwovghJUWtf5FfUNb91OPuHx7xTs3Pzi4tLOGpxcX5udso7NtzvdLxtrqvIf6FVpyAi - fhSTSg5ASWNCWsoUaJaxqLqhzQ6QnomZuYXllbX1DRy1vrayvDA3M+EBTHtbQ3WRMQtV - yCAxmbQAlFDi3Li/ElPTIeCWJlu36+PYxOz80urG5tb2Do7a3trcWF2an50Y++jqtjVZ - IOTpqYl/xXGhyG95CRsRi4ulZUZuXtlrq905MOKdmV9e+7q9u7d/gKP293a3v64tz894 - RwacduvrsrzcDCwxuViR38xLoGQDZbJaqy+obGh3uNyeqbmltc2d/YPDo2McdXR4sL+z - ubY0N+VxuxztDZUFeq06GSjZASnp7BhBPBSPobCq0dbzYcQ7u7AKkIfHJz5cdXJ8CJir - C7PekQ89tsaqQgOUT7wghk0P5CWdEyNE5E+zjMU1TR29A6OTX5Y3tvcOj32nZ+c46uzU - d3y4t72x/GVydKC3o6mm2Jj1VI4IYzh3UPJEiBzNNpXUttidQ56p+ZXN3W9HvtPzC1x1 - fuo7+ra7uTI/5Rly2ltqS0zZqBwR8e6mhI0IKF9ZO/vc49MLa1t7hycA+f0SR32/OD89 - OdzbWluYHnf3dVpfYZQK2b2UpRZrl2vYO7u4vr1/5DsDyCscdfn94sx3tL+9vjjrHXZ1 - WWEruosSPtwi6RyeyO9lAMq/cdPV7yl/+acIDQuHYxyOHuUznfmlpdUBXn5e2tg5OPad - X1xe4cYIL766vDj3HR/sbCx9Bi8drZaXZt0zJRw+cJCHhxGRkhARD7mHMjiq5xfKoN+J - iLCrE+KEJMTXBjG+3IjwFUwixB8FiUyIvzNi/OkS4tYgjBA3MNidGwFus4hwM0iMW1ai - 3FgT4/Yfa0oFeyclhBBdKaAkRoePCN1SzMzg7zwDJRbzYO/iX2MG+0RECDGmS64xg31S - J8SP6Z8oCuapJ+hT/ZjOwkbcgnaCDGun+TmfBPc0nr/tR4DJRj/nv7DA+5D6ufbj06MD - jw78WQ78A6Htz60KZW5kc3RyZWFtCmVuZG9iagozNiAwIG9iagoxNzIyCmVuZG9iagoz - MyAwIG9iago8PCAvTGVuZ3RoIDM0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAv - SW1hZ2UgL1dpZHRoIDE2NiAvSGVpZ2h0IDc4IC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5 - IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFt - CngB7ZrpTxprFMa1osgOiqAsFxzcAJFOxaKiBQJxt+4WWzUqaopFqUYiqUsxVonEtdW4 - xK3GJWqNGqLGVHP/tXsGe9NUsd77YQyT+nyaT/P+8pxz3nfmPSck5FGPDjw68Gc6EPrg - +l8+A92Tnwp7AP1c7Qks/h9grwmBjEQKf2CRSLAsBnwfqJ/xGjCCTI68FgVn/ViGTI4A - WwD1Hs4fjOHhEQBIoVJpNBqdTmfgLlgElqJSKZGRGOnvOTHIMIgzIAIgncFksthszoOI - zWYxmQxgpQLoNecdYfdDgo8YI4PJ4nCiorncmBgej4+zeLyYGC43OorDYTEZGCf4CWEP - jIk5iRmJMbKBkMePjRMIhCKRGGeJREKBIC6WzwNStp8T7MQwAxS7HxISkkYHRkAEPrFE - Ko1HZLgLiZdKJWJgBVDgpNOw9AyMGYrlJJkCRnKiebECIERkiUnJKXK5QqHEUQqFXJ6S - nJQoQ4BUEMuL5oCdFDJWQ7fNBCsBMpLGYHG4fIFYiiQkyRWpKrUaRdFnuAoWUKtVqQp5 - UgIiFQv4XA6LAW6GkwLEHKyEwqH6IYUSJDFFqVKj6RnPtVnZIB1uwt6epX2ekY6qVcqU - REQi9GNSoYQCmBkKVkZS6UwON1YokSUr01CNNkuXqzcYTSaTGUfB640Gfa4uS6tB05TJ - Mokwlsth0qmRYObNkF9bSQNIvlCaIFehmkyd3mjOKygsLil9iatKS4oLC/LMRr0uU4Oq - 5AlSzE0mLZCZGCXEmw2QkgS5Ol2bYzDlF5WWVVbV1FpwVm1NVWVZaVG+yZCjTVfLE7Cg - sxlg5q2QQ8AjImnMKJ5AIpOnabL15sKSimrLm4bGphartRVHWa0tTY0NbyzVFSWFZn22 - Jk0ukwh4UZiZt0Ie+gR2IbAyVowkq9KzDXnF5TV1DU3Wdts7e2eXA0d1ddrf2dqtTQ11 - NeXFeYbsdFUyIo4FM2E3upmYWMAhK3kCaaIS1erzSiot9c1tNrujp9fZ58JVfc7eHofd - 1tZcb6ksydNrUWWiVMDDMhNC/uvx46dkRfPFSEqaJscMkI1WW2e309U/OOQexlXuocF+ - l7O702ZtBExzjiYtBRHzo1kBKSModDY3TpKgRDMNheWWxtYOh9M16B4Z9Yx7cdW4Z3TE - PehyOjpaGy3lhYZMVJkgieOy6ZSIW16SIigMLOBJKo3OVFpTb+147+x3f/J4J6dnPuOq - melJr+eTu9/5vsNaX1Nq0mlUSVjIGRQonxsRJ5GpzCi+CElRa1/kV9Q1v3U4+4fHvFOz - c/OLi0s4anFxfm52yjs23O90vG2uq8h/oVWnICJ+FJNKDkBJY0JayhRolrGouqHNDpCe - iZm5heWVtfUNHLW+trK8MDcz4QFMe1tDdZExC1XIIDGZtACUUOLcuL8SU9Mh4JYmW7fr - 49jE7PzS6sbm1vYOjtre2txYXZqfnRj76Oq2NVkg5OmpiX/FcaHIb3kJGxGLi6VlRm5e - 2Wur3Tkw4p2ZX177ur27t3+Ao/b3dre/ri3Pz3hHBpx26+uyvNwMLDG5WJHfzEugZANl - slqrL6hsaHe43J6puaW1zZ39g8OjYxx1dHiwv7O5tjQ35XG7HO0NlQV6rToZKNkBKens - GEE8FI+hsKrR1vNhxDu7sAqQh8cnPlx1cnwImKsLs96RDz22xqpCA5RPvCCGTQ/kJZ0T - I0TkT7OMxTVNHb0Do5Nflje29w6Pfadn5zjq7NR3fLi3vbH8ZXJ0oLejqabYmPVUjghj - OHdQ8kSIHM02ldS22J1Dnqn5lc3db0e+0/MLXHV+6jv6tru5Mj/lGXLaW2pLTNmoHBHx - 7qaEjQgoX1k7+9zj0wtrW3uHJwD5/RJHfb84Pz053NtaW5ged/d1Wl9hlArZvZSlFmuX - a9g7u7i+vX/kOwPIKxx1+f3izHe0v72+OOsddnVZYSu6ixI+3CLpHJ7I72UAyr9x09Xv - KX/5pwgNC4djHI4e5TOd+aWl1QFefl7a2Dk49p1fXF7hxggvvrq8OPcdH+xsLH0GLx2t - lpdm3TMlHD5wkIeHEZGSEBEPuYcyOKrnF8qg34mIsKsT4oQkxNcGMb7ciPAVTCLEHwWJ - TIi/M2L86RLi1iCMEDcw2J0bAW6ziHAzSIxbVqLcWBPj9h9rSgV7JyWEEF0poCRGh48I - 3VLMzODvPAMlFvNg7+JfYwb7REQIMaZLrjGDfVInxI/pnygK5qkn6FP9mM7CRtyCdoIM - a6f5OZ8E9zSev+1HgMlGP+e/sMD7kPq59uPTowOPDvxZDvwDoe3PrQplbmRzdHJlYW0K - ZW5kb2JqCjM0IDAgb2JqCjE3MjIKZW5kb2JqCjQxIDAgb2JqCjw8IC9MZW5ndGggNDIg - MCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTY2IC9IZWln - aHQgNzggL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAv - RmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtmulPGmsUxrWiyA6KoCwXHNwA - kU7FoqIFAnG37hZbNSpqikWpRiKpSzFWicS11bjErcYlao0aosZUc/+1ewZ701Sx3vth - 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 - KaAkRoePCN1SzMzg7zwDJRbzYO/iX2MG+0RECDGmS64xg31SJ8SP6Z8oCuapJ+hT/ZjO - wkbcgnaCDGun+TmfBPc0nr/tR4DJRj/nv7DA+5D6ufbj06MDjw78WQ78A6Htz60KZW5k - c3RyZWFtCmVuZG9iagoyOCAwIG9iagoxNzIyCmVuZG9iagozMSAwIG9iago8PCAvTGVu - Z3RoIDMyIDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDE2 - NiAvSGVpZ2h0IDc4IC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9u - ZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7ZrpTxprFMa1osgO - iqAsFxzcAJFOxaKiBQJxt+4WWzUqaopFqUYiqUsxVonEtdW4xK3GJWqNGqLGVHP/tXsG - e9NUsd77YQyT+nyaT/P+8pxz3nfmPSck5FGPDjw68Gc6EPrg+l8+A92Tnwp7AP1c7Qks - /h9grwmBjEQKf2CRSLAsBnwfqJ/xGjCCTI68FgVn/ViGTI4AWwD1Hs4fjOHhEQBIoVJp - NBqdTmfgLlgElqJSKZGRGOnvOTHIMIgzIAIgncFksthszoOIzWYxmQxgpQLoNecdYfdD - go8YI4PJ4nCiorncmBgej4+zeLyYGC43OorDYTEZGCf4CWEPjIk5iRmJMbKBkMePjRMI - hCKRGGeJREKBIC6WzwNStp8T7MQwAxS7HxISkkYHRkAEPrFEKo1HZLgLiZdKJWJgBVDg - pNOw9AyMGYrlJJkCRnKiebECIERkiUnJKXK5QqHEUQqFXJ6SnJQoQ4BUEMuL5oCdFDJW - Q7fNBCsBMpLGYHG4fIFYiiQkyRWpKrUaRdFnuAoWUKtVqQp5UgIiFQv4XA6LAW6GkwLE - HKyEwqH6IYUSJDFFqVKj6RnPtVnZIB1uwt6epX2ekY6qVcqUREQi9GNSoYQCmBkKVkZS - 6UwON1YokSUr01CNNkuXqzcYTSaTGUfB640Gfa4uS6tB05TJMokwlsth0qmRYObNkF9b - SQNIvlCaIFehmkyd3mjOKygsLil9iatKS4oLC/LMRr0uU4Oq5AlSzE0mLZCZGCXEmw2Q - kgS5Ol2bYzDlF5WWVVbV1FpwVm1NVWVZaVG+yZCjTVfLE7Cgsxlg5q2QQ8AjImnMKJ5A - IpOnabL15sKSimrLm4bGphartRVHWa0tTY0NbyzVFSWFZn22Jk0ukwh4UZiZt0Ie+gR2 - IbAyVowkq9KzDXnF5TV1DU3Wdts7e2eXA0d1ddrf2dqtTQ11NeXFeYbsdFUyIo4FM2E3 - upmYWMAhK3kCaaIS1erzSiot9c1tNrujp9fZ58JVfc7eHofd1tZcb6ksydNrUWWiVMDD - MhNC/uvx46dkRfPFSEqaJscMkI1WW2e309U/OOQexlXuocF+l7O702ZtBExzjiYtBRHz - o1kBKSModDY3TpKgRDMNheWWxtYOh9M16B4Z9Yx7cdW4Z3TEPehyOjpaGy3lhYZMVJkg - ieOy6ZSIW16SIigMLOBJKo3OVFpTb+147+x3f/J4J6dnPuOqmelJr+eTu9/5vsNaX1Nq - 0mlUSVjIGRQonxsRJ5GpzCi+CElRa1/kV9Q1v3U4+4fHvFOzc/OLi0s4anFxfm52yjs2 - 3O90vG2uq8h/oVWnICJ+FJNKDkBJY0JayhRolrGouqHNDpCeiZm5heWVtfUNHLW+trK8 - MDcz4QFMe1tDdZExC1XIIDGZtACUUOLcuL8SU9Mh4JYmW7fr49jE7PzS6sbm1vYOjtre - 2txYXZqfnRj76Oq2NVkg5OmpiX/FcaHIb3kJGxGLi6VlRm5e2Wur3Tkw4p2ZX177ur27 - t3+Ao/b3dre/ri3Pz3hHBpx26+uyvNwMLDG5WJHfzEugZANlslqrL6hsaHe43J6puaW1 - zZ39g8OjYxx1dHiwv7O5tjQ35XG7HO0NlQV6rToZKNkBKensGEE8FI+hsKrR1vNhxDu7 - sAqQh8cnPlx1cnwImKsLs96RDz22xqpCA5RPvCCGTQ/kJZ0TI0TkT7OMxTVNHb0Do5Nf - lje29w6Pfadn5zjq7NR3fLi3vbH8ZXJ0oLejqabYmPVUjghjOHdQ8kSIHM02ldS22J1D - nqn5lc3db0e+0/MLXHV+6jv6tru5Mj/lGXLaW2pLTNmoHBHx7qaEjQgoX1k7+9zj0wtr - W3uHJwD5/RJHfb84Pz053NtaW5ged/d1Wl9hlArZvZSlFmuXa9g7u7i+vX/kOwPIKxx1 - +f3izHe0v72+OOsddnVZYSu6ixI+3CLpHJ7I72UAyr9x09XvKX/5pwgNC4djHI4e5TOd - +aWl1QFefl7a2Dk49p1fXF7hxggvvrq8OPcdH+xsLH0GLx2tlpdm3TMlHD5wkIeHEZGS - EBEPuYcyOKrnF8qg34mIsKsT4oQkxNcGMb7ciPAVTCLEHwWJTIi/M2L86RLi1iCMEDcw - 2J0bAW6ziHAzSIxbVqLcWBPj9h9rSgV7JyWEEF0poCRGh48I3VLMzODvPAMlFvNg7+Jf - Ywb7REQIMaZLrjGDfVInxI/pnygK5qkn6FP9mM7CRtyCdoIMa6f5OZ8E9zSev+1HgMlG - P+e/sMD7kPq59uPTowOPDvxZDvwDoe3PrQplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2Jq - CjE3MjIKZW5kb2JqCjM3IDAgb2JqCjw8IC9MZW5ndGggMzggMCBSIC9UeXBlIC9YT2Jq - ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTY2IC9IZWlnaHQgNzggL0NvbG9yU3Bh - Y2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl - Y29kZSA+PgpzdHJlYW0KeAHtmulPGmsUxrWiyA6KoCwXHNwAkU7FoqIFAnG37hZbNSpq - ikWpRiKpSzFWicS11bjErcYlao0aosZUc/+1ewZ701Sx3vthDJP6fJpP8/7ynHPed+Y9 - JyTkUY8OPDrwZzoQ+uD6Xz4D3ZOfCnsA/VztCSz+H2CvCYGMRAp/YJFIsCwGfB+on/Ea - MIJMjrwWBWf9WIZMjgBbAPUezh+M4eERAEihUmk0Gp1OZ+AuWASWolIpkZEY6e85Mcgw - iDMgAiCdwWSy2GzOg4jNZjGZDGClAug15x1h90OCjxgjg8nicKKiudyYGB6Pj7N4vJgY - Ljc6isNhMRkYJ/gJYQ+MiTmJGYkxsoGQx4+NEwiEIpEYZ4lEQoEgLpbPA1K2nxPsxDAD - FLsfEhKSRgdGQAQ+sUQqjUdkuAuJl0olYmAFUOCk07D0DIwZiuUkmQJGcqJ5sQIgRGSJ - SckpcrlCocRRCoVcnpKclChDgFQQy4vmgJ0UMlZDt80EKwEyksZgcbh8gViKJCTJFakq - tRpF0We4ChZQq1WpCnlSAiIVC/hcDosBboaTAsQcrITCofohhRIkMUWpUqPpGc+1Wdkg - HW7C3p6lfZ6RjqpVypRERCL0Y1KhhAKYGQpWRlLpTA43ViiRJSvTUI02S5erNxhNJpMZ - R8HrjQZ9ri5Lq0HTlMkyiTCWy2HSqZFg5s2QX1tJA0i+UJogV6GaTJ3eaM4rKCwuKX2J - q0pLigsL8sxGvS5Tg6rkCVLMTSYtkJkYJcSbDZCSBLk6XZtjMOUXlZZVVtXUWnBWbU1V - ZVlpUb7JkKNNV8sTsKCzGWDmrZBDwCMiacwonkAik6dpsvXmwpKKasubhsamFqu1FUdZ - rS1NjQ1vLNUVJYVmfbYmTS6TCHhRmJm3Qh76BHYhsDJWjCSr0rMNecXlNXUNTdZ22zt7 - Z5cDR3V12t/Z2q1NDXU15cV5hux0VTIijgUzYTe6mZhYwCEreQJpohLV6vNKKi31zW02 - u6On19nnwlV9zt4eh93W1lxvqSzJ02tRZaJUwMMyE0L+6/Hjp2RF88VISpomxwyQjVZb - Z7fT1T845B7GVe6hwX6Xs7vTZm0ETHOOJi0FEfOjWQEpIyh0NjdOkqBEMw2F5ZbG1g6H - 0zXoHhn1jHtx1bhndMQ96HI6OlobLeWFhkxUmSCJ47LplIhbXpIiKAws4Ekqjc5UWlNv - 7Xjv7Hd/8ngnp2c+46qZ6Umv55O73/m+w1pfU2rSaVRJWMgZFCifGxEnkanMKL4ISVFr - X+RX1DW/dTj7h8e8U7Nz84uLSzhqcXF+bnbKOzbc73S8ba6ryH+hVacgIn4Uk0oOQElj - QlrKFGiWsai6oc0OkJ6JmbmF5ZW19Q0ctb62srwwNzPhAUx7W0N1kTELVcggMZm0AJRQ - 4ty4vxJT0yHgliZbt+vj2MTs/NLqxubW9g6O2t7a3Fhdmp+dGPvo6rY1WSDk6amJf8Vx - ochveQkbEYuLpWVGbl7Za6vdOTDinZlfXvu6vbu3f4Cj9vd2t7+uLc/PeEcGnHbr67K8 - 3AwsMblYkd/MS6BkA2WyWqsvqGxod7jcnqm5pbXNnf2Dw6NjHHV0eLC/s7m2NDflcbsc - 7Q2VBXqtOhko2QEp6ewYQTwUj6GwqtHW82HEO7uwCpCHxyc+XHVyfAiYqwuz3pEPPbbG - qkIDlE+8IIZND+QlnRMjRORPs4zFNU0dvQOjk1+WN7b3Do99p2fnOOrs1Hd8uLe9sfxl - cnSgt6OpptiY9VSOCGM4d1DyRIgczTaV1LbYnUOeqfmVzd1vR77T8wtcdX7qO/q2u7ky - P+UZctpbaktM2agcEfHupoSNCChfWTv73OPTC2tbe4cnAPn9Ekd9vzg/PTnc21pbmB53 - 93VaX2GUCtm9lKUWa5dr2Du7uL69f+Q7A8grHHX5/eLMd7S/vb446x12dVlhK7qLEj7c - IukcnsjvZQDKv3HT1e8pf/mnCA0Lh2Mcjh7lM535paXVAV5+XtrYOTj2nV9cXuHGCC++ - urw49x0f7GwsfQYvHa2Wl2bdMyUcPnCQh4cRkZIQEQ+5hzI4qucXyqDfiYiwqxPihCTE - 1wYxvtyI8BVMIsQfBYlMiL8zYvzpEuLWIIwQNzDYnRsBbrOIcDNIjFtWotxYE+P2H2tK - BXsnJYQQXSmgJEaHjwjdUszM4O88AyUW82Dv4l9jBvtERAgxpkuuMYN9UifEj+mfKArm - qSfoU/2YzsJG3IJ2ggxrp/k5nwT3NJ6/7UeAyUY/57+wwPuQ+rn249OjA48O/FkO/AOh - 7c+tCmVuZHN0cmVhbQplbmRvYmoKMzggMCBvYmoKMTcyMgplbmRvYmoKMzkgMCBvYmoK - PDwgL0xlbmd0aCA0MCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9X - aWR0aCAxNjYgL0hlaWdodCA3OCAvQ29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1Bl - ckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae2a6U8a - axTGtaLIDoqgLBcc3ACRTsWiogUCcbfuFls1KmqKRalGIqlLMVaJxLXVuMStxiVqjRqi - xlRz/7V7BnvTVLHe+2EMk/p8mk/z/vKcc9535j0nJORRjw48OvBnOhD64PpfPgPdk58K - ewD9XO0JLP4fYK8JgYxECn9gkUiwLAZ8H6if8RowgkyOvBYFZ/1YhkyOAFsA9R7OH4zh - 4REASKFSaTQanU5n4C5YBJaiUimRkRjp7zkxyDCIMyACIJ3BZLLYbM6DiM1mMZkMYKUC - 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 - KUFUc0OVK6Wnbk5Ni4MfKUUd1E5YphX46WJxjLHruZK/u9fWZ9LYst7HtXb79j21lWVg - IeottrcQ+iGRZgAfmZ8oZYCzwB2Wr9g+ATxYDqwa8COiAw+auTDT0Zx0pbItkVPmoigq - r2I7Sa77+bnGvou1iYP+XI9m1o69s+qq0UzUtPdEobwPrkQZz19U9mw1FKcN45xIQxop - 8q7V3ytMxxGRKxBKBlI1ZLmfak6ddeB1GLtdupPj+PYQpT7JYKiJtemymR2FfQB2Ksvs - EPAF6PGyYg/ngXth/1tRw5PAJ2E/ZId51q0f9heuU+B7hD014M4UrsXx2oofXi0BQ/dU - I2iMc03E09c5c6SI7zHUGZj3RjmmCzF3lqoTN4A7YR9ZqmYKsV37ruol7nsCd9PjO9Gb - OQtcoBxJcrEV2RTQPAlYFH2LsEkOPD7OHlXgd6iYwBy5idzNKPce1REbZ6NSgVZ6jVfG - T+O58cX4ZWwYz4B+rHbXe3z/6eMVdde2Pjz5jXrcOa69nRtVYVZxZQvd/8cyhI/ZJzmm - wdOhWVhr2HbkD5rMTLAMKMR/BT6X+pITVdzV7u24RRLMUD4sbCW6S1RuKdTqPYNKrBwr - 2AB2cJLELFocuFNrujl4d9giem35TVey64b++vZ6+9ryHm3KqCkoE82zRGaUsVuj5N14 - 2/1mkRGfODq+572KWsn+SUUQP4U5WiryFFX0VlDWxG9nDn4btn5cP6Xn9UH9PAk9rZ/R - r+ijEb4MdEnPwnNRH6NJ8LBpIeISoIqDM9ROVGONA+Ip8fK0W2SR/Q9AGf1mCmVuZHN0 - cmVhbQplbmRvYmoKNDQgMCBvYmoKNzA0CmVuZG9iagoyNCAwIG9iagpbIC9JQ0NCYXNl - ZCA0MyAwIFIgXQplbmRvYmoKNDUgMCBvYmoKPDwgL0xlbmd0aCA0NiAwIFIgL04gMyAv - QWx0ZXJuYXRlIC9EZXZpY2VSR0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFt - CngBrVh5OFVd219ndMzHkDEcypB5yjyPGTNnKPMYjuMYQkhRlLnMhCSJepSIBlKmzKJE - iTJFZiIyvfvQ0/N91/e91/vPu65r7fVb9/rdv7X2vs/a91kbAOxDJwLBBw4A8MUHEs10 - NXDWNrY4siGAArQAC0QBzMklgKBuYmIIUf5NWR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHB - svl/4/S3mYYITQgATBgyMHocYDUSdj7AFiR8LpAQCHE8SdjF08kVwuEQFiZamGlCuBTC - NB4HuJaEnQ/wGxIOdvEg+Q4CgKbHu3rhASCbg7CKq1uACzRMmtfVNcDFF8IpEK/P19cP - 0sdCFQi4EIiQLzYBwkdJzwVqoWIL6UqVQzrJ/9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1 - 239WMKaeAHcpyX05GJUGAKiRvb1VPkjzOgA7qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguE - dQHwn/oH9/zbAwEFBwowzA6+igxBLZJ5YcYp3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUe - ZuVI4cLgIrgXj7gcfcenw195DCeYKLQu4ijaLC4skSS5KG18/J4sXM5O/qEiUslSuVhl - SU1RPVqjVYtK21gnUbdDD62vbhBq+NBo8iSriaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY - 7ZUdnBwvOt1ybnAZcd1yZ/GQ8jT28jx7wTvD54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhr - KD4s5HxUeFxEcmTaheyovIv5lwqjb8Xculx4JT82Ny7ratq15PirCdGJ4UkByd4pzqnW - 141vaKbJpQtn4DIZs8iyNrO/5wznduXV33yQf7MgvjDslkeR5W2NYtE7bCXokuW7n0qb - 71WUZZVH3fd8YPKXbAXHQ8TD6UddlZWPM6pCq08/Ua3hroXXTjxtelb8POaFS516PVf9 - zsuhhievUl97N2o3cTX9bO5pKWkNf2PWxtf2q72rI78T36XSTdX9saek1/+tYh+yr6v/ - xjvr9xzvvwwUfXAe5Bn8OpT/0fYT06fe4auf1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J9 - 8s6UxTfkt8fTzjN0Mw3fCbO42d65i/OS8xMLWYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/ - bTc4NsY2S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCsp5amKcdy02Uw0DPGHNpi - xrNMsFmwt3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixVfEFSR6pAel1GTzZXbkZB - RjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0TflNTsxDzmxavLb+dorAW - sTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWHzJcVL+inSND3tya6BwQE - Xgi6Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX6648ja2Kq7hadu1OfH5C - ZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZuvstvKagtLLuVW5RwO7zY - 987pEsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJVvM8oX6yWTNe2/209lnR - 84QXQXX29bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9v6OqM70rsNu8R7IX2zv3 - 9k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh873PoiN4o8+jkl8qvkWN644zjIxN3JwlT - slO735qm42YMv2O/v529Pmc2TzffvXBtUXtxd6l6+ewK50rvatQP0R8f16LXBdd7fvpv - 0G9UbOpvTvwK28JuFW1LbzfumO6M7p7dXdsL248/Ai6MUEVaojzRkWQZmHvkdRQDlLPU - cBpGWn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf2Nc54JwUXEw4Lm5eHqEjokfFeSX4JPhF - BQSP8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUjbXZcRoZZZkN2UK5WPl2BqGisJKiMUh5R - qVVNVfNS19Bg01jRbNcq0g7TMdMVPAE/8VHvkf41AxdDRSNGo3njlpOFJqGm5mYi5mjz - LxZPLdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9skO7422ncGcrF1FXMtdRtxr3JA93T2Uv - Rq/Zs6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd+DwoMdjxnGQIMmQgtDQs9Lx+OHv4XMSL - yMQLZ6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76quDVnWtv44sTQhINk7iT1pM7U4pSQ6+b - 3BBIA2lD6ZUZCZnuWerZh7M3ct7nPspLvumbb1AgWEhR+P1WZ9GD28nFxDtWJQp3uUpR - pbP3+suelxffT3oQ+pdbhdlDtUdilVyP6aqQVRvVC08maz7Xfnja/6z3ee+LvrqB+uGX - kw3Lr/YasU28zcotNq0hb/LamtpXO3m77Lvzer68FeqL6H//Xm6gcJBuKP4T9XDmiPBo - 21e/ca6JD1M5067fZefo5lcXPy93rTavNf6s22zYat8ZJsX/IPeRcgJaBoBMLACnjgJg - XgRAbA6U6iyhXFUKgAk1ABbyAG6TDeD2tQDm7g5+5w8oh6AAJaAH7IAPSAI1cBI4AH8Q - A7JBBWgBI2ADxgATgxnBvGHxsPuwbtgKnBmuDHeFJ8CfwL8gqBDyCE9EJuIN4hdSBOmM - zEL2oMhQ6qhwVC1qHX0cHYSuQf8iUyW7TNaFYcI4YR5gtsj1yXPJFyg0KLIolij1KIup - YFSOVA3UPNRXqOdozGnqaAVo07FIbCD2G50d3Vt6ffomBlWGOkZlxvpD6odamYyZPjA7 - My+wRLDSsBayHWfrYHdl3z2cyyHPMcQZzoXjasH5cbNxt/AEHeE7MnQ0iVeHd4/vOX+o - gLzA9rFXgnFCZsKcwvMi9aKpYh7iqhJsEhuSg1LPpAuOx8r4y9rLGcmrKkgpCiodUeZS - 4VTlVONRF9AQ11TUOqFtrXNWN/JEul6FfofBjBGFsehJC5MI01KzAQu0pbyV36m71mO2 - ODvH07fPzDhIOkY4dbgcdiW4tXrgPMO9hr1VfG7jKf2CCeNEi4A3QarBT0NkQ2vOK4a/ - jjS6MHwRH42Myb+iFDtyNSZeLGE06UaK0XXKGz3pWZke2Uq5THk/80cKO4saip+XvCht - Lnt3f7YC80jksXX1tZrGZ4gXevVpDZONSs2ZrWvt1p0veo68vda/MmA72PDp6Ocro1Nj - GhO5U8szmrPX50eXjq0QfjxZ39xU3YreaSb9fn7HnwEcBvxACqgDE+AEAkAsyAOVoB2M - gx0YK+w4zBwWALsBq4YNwrbhPHA9uD88B94EX0JwIAwQ5xHliM9IOqQ28jyyEjmL4kU5 - om6iPqHZ0Hbom+ivZPxkeLInGIAxxGRjpsnlyePJv1Acp0ik+EapQVlAuUvlQPWKmo86 - gXqNxoGmg1ae9h6WHZtMh6QLp1un96dfYPBlWGQkMq4fimBCM6UwH2YuZ1Fk6WJ1Yv3J - lsQuwN542IkDcBRxanPOcl3HKeK+cafzaPGsHyk76sjLwtvPl8J/UgAr0H8sW9BJSFho - U/iNSI4oXkxLnEN8U+KDZI1UjvSF4x4yprLKcsLynAr0ihgloLSlvKmyofpLbVcDpUmj - xarNpyOje+LEaT0i9Fa7Y/ja6OtJBJQDjcyCzAssOi23TolYO9pk2HafJj+jbR/j0OJE - 7nzSJdN1zF3UI8Kz+yyP9zmfHij7XSFMEnUCyoKwwSHnxkNNwhrCpSJKL3BFZV9ijs64 - zHqlIE7galW8ZsL7JO8UZOqtG+ppkxmJWfLZM7l5Ny0KsIW9RenF9iVCd3fuvSuveJBc - EfDozGODapWa408ln0vVKbzUeXWqEd8c31rRNtRJ0a3ZG9PX/f7oh5ihpeGzI6tfEyfk - ptZn2uceLz5eaVv7uSm5fWk//ghADhgADogDTWANCCAelIJWMAOjhknDzsDioJhPQjve - AH4J/gy+hpBCEBFViA2kCjIW2Y/CoQioRjQLmoDuIBMgiyObxZhinpLzkqdRoCnCKX5Q - EiiXqAKoNqmjabA0t2hlaLuxXnQYunJ6U/pfDCWMVofID71kCmGWZl5hqWINYVNlx7C/ - O3ybI5BTl4uDaw3Xy32fJ+GI71FTXjk+bn4q/k2B78eGBd8KtQk3irwUhdK0eLNEp+SA - 1Jj0sgxClkVOXF5fwUMxVqlMuUdlQ+2IuplGtGaN1oLOMV33EyV63w3EDcOMWk4eMvEw - rTNntPCz7ILilmzzw+706WZ7CYcCJ1rnKJcfbl7uo57WXm+9DX3a8Lp+Lf46xNZA/aDu - c+YhQ2GO56cj/CO3omIvMUQXXha7Uh9nfHUknpAIT0pPEUh9ccM0bSrjfBY2uyRXKe9t - vkfBzq3rt/mLX5SY3J24F1yOuZ/917GK2kc6lX1Vp6snarxrF5/5P1+uw9dPNzi86m/U - anrUwt568c1Yu1pHTudit3pPUu9AH3u/3bus990f4IMSQ2c+Rn+6O9z6eXxk+wvdV54x - iXGlCe1Jgymjb8bTBjO639Vm5eZE5nEL9IuwxeWlz8ttK49Xb/6IWfNeN/l5fINjE7k5 - /atzq2I7Zcdv13iPjxT/g/PS/pmCQtPPx4+IM9TU2u/+9y6+PkHQOWq/0EJXKryz8Umo - pYdqU0CwuTbUQnkLDLh76ej9xlOuTloGED4M1fUwT01jqKWC/u3SuxN1zCDMBGHOs076 - JhCmgbC0G97SHMKQJkyDEKhB4rBA2MItQPtvu0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAs - vI+xIWQn6Zd6Bertn1MhXA90oPciEXgANyACDKG9ogVZJvYtf/et9vtef8YPWCLAfd8z - GPIMAN5gCvLxdfC6RIT23AHj4IoDLsAPBAEfiBcEiOLl4jPi2384mtCYD1T/f6+DES/g - CjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noae - FEBDscuzJ6EGvkJS879KoFsIdNYGQNOPEEr08vAMxKlDXxPchHF6eBdRYZykuLg8+BcC - xWXcCmVuZHN0cmVhbQplbmRvYmoKNDYgMCBvYmoKMzgyNwplbmRvYmoKMjYgMCBvYmoK - WyAvSUNDQmFzZWQgNDUgMCBSIF0KZW5kb2JqCjQ3IDAgb2JqCjw8IC9MZW5ndGggNDgg - MCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2Rl - ID4+CnN0cmVhbQp4AYWUTUgUYRjH/7ONBLEG0ZcIxdDBJFQmC1IC0/UrU7Zl1UwJYp19 - d50cZ6eZ3S1FIoTomHWMLlZEh4hO4aFDpzpEBJl1iaCjRRAFXiK2/zuTu2NUvjAzv3me - //t8vcMAVY9SjmNFNGDKzrvJ3ph2enRM2/waVahGFFwpw3M6EokBn6mVz/Vr9S0UaVlq - lLHW+zZ8q3aZEFA0KndkAz4seTzg45Iv5J08NWckGxOpNNkhN7hDyU7yLfLWbIjHQ5wW - ngFUtVOTMxyXcSI7yC1FIytjPiDrdtq0ye+lPe0ZU9Sw38g3OQvauPL9QNseYNOLim3M - Ax7cA3bXVWz1NcDOEWDxUMX2PenPR9n1ysscavbDKdEYa/pQKn2vAzbfAH5eL5V+3C6V - ft5hDtbx1DIKbtHXsjDlJRDUG+xm/OQa/YuDnnxVC7DAOY5sAfqvADc/AvsfAtsfA4lq - YKgVkctsN7jy4iLnAnTmnGnXzE7ktWZdP6J18GiF1mcbTQ1ayrI03+VprvCEWxTpJkxZ - Bc7ZX9t4jwp7eJBP9he5JLzu36zMpVNdnCWa2NantOjqJjeQ72fMnj5yPa/3GbdnOGDl - gJnvGwo4csq24jwXqYnU2OPxk2TGV1QnH5PzkDznFQdlTN9+LnUiQa6lPTmZ65eaXdzb - PjMxxDOSrFgzE53x3/zGLSRl3n3U3HUs/5tnbZFnGIUFARM27zY0JNGLGBrhwEUOGXpM - KkxapV/QasLD5F+VFhLlXRYVvVjhnhV/z3kUuFvGP4VYHHMN5Qia/k7/oi/rC/pd/fN8 - baG+4plzz5rGq2tfGVdmltXIuEGNMr6sKYhvsNoOei1kaZ3iFfTklfWN4eoy9nxt2aPJ - HOJqfDXUpQhlasQ448muZfdFssU34edby/av6VH7fPZJTSXXsrp4Zin6fDZcDWv/s6tg - 0rKr8OSNkC48a6HuVQ+qfWqL2gpNPaa2q21qF9+OqgPlHcOclYkLrNtl9Sn2YGOa3spJ - V2aL4N/CL4b/pV5hC9c0NPkPTbi5jGkJ3xHcNnCHlP/DX7MDDd4KZW5kc3RyZWFtCmVu - ZG9iago0OCAwIG9iago3OTIKZW5kb2JqCjcgMCBvYmoKWyAvSUNDQmFzZWQgNDcgMCBS - IF0KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDU3 - NiA3MzNdIC9Db3VudCAxIC9LaWRzIFsgMiAwIFIgXSA+PgplbmRvYmoKNDkgMCBvYmoK - PDwgL1R5cGUgL0NhdGFsb2cgL1BhZ2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5k - b2JqCjUwIDAgb2JqCjw8IC9MZW5ndGggNTEgMCBSIC9MZW5ndGgxIDk2NDAgL0ZpbHRl - ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBvVoJeFNluv7+s+ScpNmXZmnS5DTN0n0j - paWVhtK0ZRWoQotUW6DQskjFUgEBi4JCBUZFC6LjLiKIhLKlIA5iEZxRh3HBDe+4gKNz - p4N3Bh0XmtzvnJQK8zhenuf6mPP8+/b97/8t/xIgAKCEDqAhOGN+Yyv0kqGY8xq6IzPa - 21zrvxz+CABZB0DPm9U6e77u45d/D8B8C6BQzp63ZJb8hPbvABoGQDevualx5ldr93EA - rlZsX9iMGYoULhPT2zCd2jy/bfHVr8kCmBb7nzBvwYzGjI/9EwAEO6bT5zcubuUXK77D - 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 - x6frjuku1+45rQPvwN6G6TOaxbe6xqZwq7upIjzHXeHa3S61E7MvKZ4lFre7K3bDrNA1 - tbtnBZsqutuD7SHxyXjP9PKF9ZeNtXZwrIXlPzFWudjZQnGs6VK7fxurXiyeLo5VL45V - L441PThdGkuEINRSU35zG3InPqfic6a/Jjxq4tRa/NdAXUWEbBXfWBfB/wKqhZvrCmVu - ZHN0cmVhbQplbmRvYmoKNTEgMCBvYmoKNjQ0OQplbmRvYmoKNTIgMCBvYmoKPDwgL1R5 - cGUgL0ZvbnREZXNjcmlwdG9yIC9Bc2NlbnQgNzcwIC9DYXBIZWlnaHQgNzI3IC9EZXNj - ZW50IC0yMzAgL0ZsYWdzIDMyCi9Gb250QkJveCBbLTk1MSAtNDgxIDE0NDUgMTEyMl0g - L0ZvbnROYW1lIC9MRU5CVk8rSGVsdmV0aWNhIC9JdGFsaWNBbmdsZSAwCi9TdGVtViA5 - OCAvTWF4V2lkdGggMTUwMCAvU3RlbUggODUgL1hIZWlnaHQgNTMxIC9Gb250RmlsZTIg - NTAgMCBSID4+CmVuZG9iago1MyAwIG9iagpbIDI3OCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCA1NTYgNTU2IDU1NiA1NTYgNTU2IDU1NiA1NTYgNTU2IDAgMCAwCjAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAKMCA1NTYgMCA1MDAgMCA1NTYgMCAwIDU1NiAyMjIg - MCAwIDAgMCAwIDU1NiA1NTYgMCAzMzMgNTAwIDI3OCBdCmVuZG9iagoyNSAwIG9iago8 - PCAvVHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9MRU5CVk8r - SGVsdmV0aWNhIC9Gb250RGVzY3JpcHRvcgo1MiAwIFIgL1dpZHRocyA1MyAwIFIgL0Zp - cnN0Q2hhciAzMiAvTGFzdENoYXIgMTE2IC9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGlu - Zwo+PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9yIChB - cnZpZCBOb3JiZXJnKSAvQ3JlYXRvciAoT21uaUdyYWZmbGUpIC9Qcm9kdWNlcgooTWFj - IE9TIFggMTAuNS43IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRpb25EYXRlIChEOjIw - MDkwNTI1MDI0MjAxWjAwJzAwJykKL01vZERhdGUgKEQ6MjAwOTA1MjUwMjQyMDFaMDAn - MDAnKSA+PgplbmRvYmoKeHJlZgowIDU0CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAz - Mzc3NCAwMDAwMCBuIAowMDAwMDAxNzkwIDAwMDAwIG4gCjAwMDAwMjY0MjggMDAwMDAg - biAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxNzcwIDAwMDAwIG4gCjAwMDAwMDE4 - OTQgMDAwMDAgbiAKMDAwMDAyNjM5MiAwMDAwMCBuIAowMDAwMDAyNTM0IDAwMDAwIG4g - CjAwMDAwMDI5MDcgMDAwMDAgbiAKMDAwMDAwNDUwNiAwMDAwMCBuIAowMDAwMDA0ODgx - IDAwMDAwIG4gCjAwMDAwMDMzMjEgMDAwMDAgbiAKMDAwMDAwMzY5NiAwMDAwMCBuIAow - MDAwMDAzNzE2IDAwMDAwIG4gCjAwMDAwMDQwOTEgMDAwMDAgbiAKMDAwMDAwMjEzOSAw - MDAwMCBuIAowMDAwMDAyNTE0IDAwMDAwIG4gCjAwMDAwMDQ5MDEgMDAwMDAgbiAKMDAw - MDAwNTI3NiAwMDAwMCBuIAowMDAwMDA0MTExIDAwMDAwIG4gCjAwMDAwMDQ0ODYgMDAw - MDAgbiAKMDAwMDAwMjkyNiAwMDAwMCBuIAowMDAwMDAzMzAxIDAwMDAwIG4gCjAwMDAw - MjE0NTIgMDAwMDAgbiAKMDAwMDAzMzU5OSAwMDAwMCBuIAowMDAwMDI1NDQwIDAwMDAw - IG4gCjAwMDAwMTI5NjAgMDAwMDAgbiAKMDAwMDAxNDg1NSAwMDAwMCBuIAowMDAwMDA1 - Mjk2IDAwMDAwIG4gCjAwMDAwMDcxOTEgMDAwMDAgbiAKMDAwMDAxNDg3NiAwMDAwMCBu - IAowMDAwMDE2NzcxIDAwMDAwIG4gCjAwMDAwMDkxMjggMDAwMDAgbiAKMDAwMDAxMTAy - MyAwMDAwMCBuIAowMDAwMDA3MjEyIDAwMDAwIG4gCjAwMDAwMDkxMDcgMDAwMDAgbiAK - MDAwMDAxNjc5MiAwMDAwMCBuIAowMDAwMDE4Njg3IDAwMDAwIG4gCjAwMDAwMTg3MDgg - MDAwMDAgbiAKMDAwMDAyMDYwMyAwMDAwMCBuIAowMDAwMDExMDQ0IDAwMDAwIG4gCjAw - MDAwMTI5MzkgMDAwMDAgbiAKMDAwMDAyMDYyNCAwMDAwMCBuIAowMDAwMDIxNDMyIDAw - MDAwIG4gCjAwMDAwMjE0ODkgMDAwMDAgbiAKMDAwMDAyNTQxOSAwMDAwMCBuIAowMDAw - MDI1NDc3IDAwMDAwIG4gCjAwMDAwMjYzNzIgMDAwMDAgbiAKMDAwMDAyNjUxMSAwMDAw - MCBuIAowMDAwMDI2NTc1IDAwMDAwIG4gCjAwMDAwMzMxMTQgMDAwMDAgbiAKMDAwMDAz - MzEzNSAwMDAwMCBuIAowMDAwMDMzMzcxIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUg - NTQgL1Jvb3QgNDkgMCBSIC9JbmZvIDEgMCBSIC9JRCBbIDw3ZjliNTI5NGE0ZDUwNTg2 - MmRmY2IwMDhiNWI0NmM0Zj4KPDdmOWI1Mjk0YTRkNTA1ODYyZGZjYjAwOGI1YjQ2YzRm - PiBdID4+CnN0YXJ0eHJlZgozMzk4MQolJUVPRgoxIDAgb2JqCjw8L0F1dGhvciAoQXJ2 - aWQgTm9yYmVyZykvQ3JlYXRpb25EYXRlIChEOjIwMDkwNTI1MDIzMDAwWikvQ3JlYXRv - ciAoT21uaUdyYWZmbGUgNS4xLjEpL01vZERhdGUgKEQ6MjAwOTA1MjUwMjQxMDBaKS9Q - cm9kdWNlciAoTWFjIE9TIFggMTAuNS43IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAo - bWVya2xlX3RyZWUuZ3JhZmZsZSk+PgplbmRvYmoKeHJlZgoxIDEKMDAwMDAzNTIxOSAw - MDAwMCBuIAp0cmFpbGVyCjw8L0lEIFs8N2Y5YjUyOTRhNGQ1MDU4NjJkZmNiMDA4YjVi - NDZjNGY+IDw3ZjliNTI5NGE0ZDUwNTg2MmRmY2IwMDhiNWI0NmM0Zj5dIC9JbmZvIDEg - MCBSIC9QcmV2IDMzOTgxIC9Sb290IDQ5IDAgUiAvU2l6ZSA1ND4+CnN0YXJ0eHJlZgoz - NTQyNAolJUVPRgo= - - QuickLookThumbnail - - TU0AKgAABkqAMyBMwZwUZgCEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+OwOCQaQSW - TSeURJ/ysAPSXAAEzEAPWaAB9zcAPOdAAKz0AAGgAB4UMABajAAC0mU0umU2QwODQenV - OqVWFvGsABa1sAAKvAAJWGi0dk2WeT5u2kAC22WcKgCSVa5XOlyKo3S8Xm9QiVv+F0AA - 3vBYONXa44TEYnFYvGVTDQXG5HCOrKABWZcAErNABh50ABnQAAiaPJaXEY+pabVRW+gB - 76/XbDXvcAPbbAB+bkAP3eABT78AHvhABacXd70acncbqYgmYTLm8/nAbqavrRjUdfJP - jubHabZ7d4APnyAAEefpAAFev0gf3R9/fHxbP57Dcvz09H9TLqAbtMk7L/qafUCNq276 - Po7h8KQpT1gU/LoJk7T4n8+raQQ+zmQi5z9um6sBKbAMQIydcSpal76AJFUIOdBz0sBE - amQpAzwww2kFOW/DoxcC8exiwqoMPH6EtaRsjJsnD3AOAB2SaABBSgAEVAJIcfpufYAD - 9LQAAfLsGAKhY5zFKqHxFGKXHoABzTWAB0TcAASTi9JizoAATTuAAOT09T2TI0x5UAAB - z0GABx0MADkhpGgAGnRoABHSE8z284ETJMzVN4ftBUI8AAA1T4AAZUSJp0eYAHLVAAAh - VYAR6C6fqDPy6SvNU2QIfQAA3XU+QeiShnhNs3gnYa3RjS7G1/YJ0VVVlXVgwKNUzWpz - ABHE9A49NZKYdtuAAyh1AAClxWKjz7gAcl0OPTVrgBJTV2Ovdb1PVMZ10DbzPQqdAHla - YAAjf6xgtZ9tIvHFUHKrqv3sAD+qod+H2VYtxApAEgsgwUSnXJknKNgVhgmwlpYOAF5X - ZSmCIU1p05WoSiAxl9mAgwlaXQcgANbdmGsHeCmVpNdqIVT4NS+1dkrSboABRpQAAXps - yVobmogABuqM+0IB6w1Z3a2ABxa8AAS7DpmnLzniTNaMm0gAD+2AAE+3gAK+5P+b26gA - R28SlFe0jIAAWb+/7WjPwdc13SARgALnFP/rxxAASXIRPNI2cpv3ALlsyS4ed4AFLzwA - Bt0IAGh0gADL0+BtUanVgAY/XXa94VdlRDlO1fYAEx3IACH3gAGv34ACz4QAawAbVmd5 - HW9esIJAB2QVAAF/pLnzKOU7xs4TlKd54R4oAZeDDJKweNvMrsISoXo6wLFf4Islcxt/ - iAARfp2El4zRe2A+7bunB/ydk8FeAE11r5SUwGgAyY5ixqSPrSaiNxTyoGqANIW/B+Th - 2GIfMEtJ+I221ttOiQo8g+QAN1G89kEjRDFDhhYeljpD4HrhXGqtmRihvw3Ziv5gBCkZ - wxgQ1NqpdYFkmhYOF+z32YESSaOxRYHonGEUMOODJ/lnESfwTQeoAAQRbMI+MAD+E4wp - IkjiG432wNie2Xl2634UETPpEVR6kYDEneqRNbg7WWrAgwRY1sHXCr3aaAsuapVNjnjO - +hGBFoYsThyXKDj8otggXwpUizKx0njPLE4DxehtSdj+2OQRFo7gAi8/QEUdIFurGoRO - LAAHbwTlA6khL/hwRai4Q5c0nRtPOdmRZNyy3wEPkfB4DsxZJvEayQ5zcOn3Q8PlL8AC - M12ESjg/oicbGGnRXcQ6aEwSHKdZrAAExE4/PnInF5aSHSHxRABMUDpD1OyjOjIwizfw - WAANRKpy09yHT6kIDmgBExc0DAAEegxE3kDOAADKhhF06DFAADuiRDz6CxosAALFGSLj - So4AAF1HyHv4GVSMAAS6TETF1SkAARqWETK2LWj1IJ3ETdcMcAFAAc0UNgKqngAAv0/I - mK+oQAAo1FImL6pAAAHVLdoooiQvKoGiNIRIatVZePQkSRGfU9p8SpdZVwpdAxc0FoOV - Oh9EaJlTo4NKmILiJmtqyRClIuqV0tKnTWm9ASl1CFfUSoxU6oC8qkEQqlW3AT5dYVOE - z836lTnZTMqc3YkkpjKAAENlyqThmmSmcrYipy0ltJIuVXI6sotNae1B2IhkJWktJnRD - U0KhVGQq2KogGWptxbkyJqK1gAlGp22MP1pK0VojNZM1nsIuClcu3VzbnFONRDFd0cz6 - W2SQlhLoD4vomRnAKGTFI2IwsuCG595bzEgICAAADwEAAAMAAAABAFkAAAEBAAMAAAAB - AB8AAAECAAMAAAAEAAAHBAEDAAMAAAABAAUAAAEGAAMAAAABAAIAAAERAAQAAAABAAAA - CAESAAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABAXAAAAEXAAQAAAABAAAGQQEc - AAMAAAABAAEAAAE9AAMAAAABAAIAAAFSAAMAAAABAAEAAAFTAAMAAAAEAAAHDIdzAAcA - ABCwAAAHFAAAAAAACAAIAAgACAABAAEAAQABAAAQsGFwcGwCAAAAbW50clJHQiBYWVog - B9kABQAPABAALgABYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEA - AAAA0y1hcHBsmlaR7nq8UbLhUKKr2Cqq7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgA - AAAUY2hhZAAAAXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAO - dmNndAAAAdgAAAYSbmRpbgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAHSbW1v - ZAAAEGQAAAAoY3BydAAAEIwAAAAkWFlaIAAAAAAAAFrIAAAzqwAAB49YWVogAAAAAAAA - dmMAALWIAAAmcVhZWiAAAAAAAAAlqwAAFugAAKUlWFlaIAAAAAAAAPNSAAEAAAABFs9z - ZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1cnYAAAAA - AAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAA - AAADAQAAAgAAAVsC8wR4Be8HaQjjCmUL4g1XDtMQUhHKE0UUvxY6F7UZKxqgHBkdkB8I - IH0h7yNjJNQmRieyKSAqjSv2LWAuxTAsMY4y7zRONak3AjhbObA7BDxVPaQ+8EA7QYNC - x0QMRU5GjUfLSQZKQUt5TLBN5U8ZUExRelKqU9hVBFYwV1tYglmpWtBb9l0bXj1fYWCB - YaFiwGPeZPtmGGczaE5paGp/a5ZsrW3Cbtdv63D9cg9zH3QvdT52S3dYeGR5bnp4e4B8 - iH2PfpR/mYCcgZ6CoIOhhKGFoIaeh5uImImUio+LiYyDjXyOc49rkGKRWJJOk0OUOJUs - liCXFJgHmPmZ6prcm82cvp2tnp2fi6B6oWeiVaNCpC6lGqYGpvGn26jFqa+qmKuArGit - UK43rx2wA7Dosc2ysrOWtHm1XLY/tyG4ArjkucS6pLuEvGO9Qb4fvv2/2cC2wZHCa8NF - xB/E98XPxqbHfMhSySbJ+srNy57Mbs0+zgzO2s+m0HHRO9IE0s3Tk9RY1R3V4Nai12LY - Itjh2Z7aWtsW29Dcid1B3fner99k4BngzeGA4jLi5OOV5EXk9uWl5lTnBOez6GLpEenA - 6nDrIOvQ7IHtMu3k7pjvTPAB8LfxbvIn8uHznPRZ9Rf11vaX91r4Hfji+aj6cPs4/AL8 - y/2W/mH/L///AAABWwLzBFsF2AdWCNIKSAu+DTQOqxAhEZYTEhSEFfsXbhjkGlQbxh06 - HqwgHCGKIvokZiXQJzsooioIK20szy4yL5Ew7jJGM5809jZJN5s46To2O348xj4KP01A - jkHMQwlEQ0V7RrBH5UkYSklLeEymTdNO/VAnUVFSeFOdVMJV51cKWC1ZT1puW45crV3L - XuhgBWEhYjtjVmRuZYdmnme1aMtp32rzbAdtGW4qbztwSnFYcmZzc3R/dYp2lHeceKR5 - q3qxe7d8u32/fsF/woDDgcOCw4PBhL+FvYa5h7SIr4mqiqWLnoyXjZCOiI9/kHaRbJJj - k1mUTpVDljiXLZgimRaaCZr9m/Cc4p3UnsafuKCqoZuijKN8pG2lXKZMpzuoKqkaqgiq - 9qvkrNKtv66sr5qwhrFzsl+zS7Q3tSK2Dbb5t+O4zrm4uqK7jLx1vV6+R78vwBfA/sHm - ws3Ds8SZxX7GY8dHyCvJDsnxytPLtMyVzXTOU88y0BDQ7NHI0qPTftRY1TDWCNbg17bY - i9lf2jPbBtvY3Knded5J3xff5eCy4X7iSeMU497kp+Vw5jfm/ufE6IjpTOoQ6tHrkuxS - 7RHtzu6K70Xv/vC18WryHvLQ84D0LvTa9YP2KvbP93H4Eviv+Ur55Pp6+xD7ovw0/MP9 - Uf3f/mr+9f96//8AAAEDAiwDPwRVBWgGfgeWCKkJvQrWC+sNAg4UDycQPBFREmITdRSH - FZkWqxe6GMoZ2RrnG/YdAh4KHxYgHyElIisjLiQwJTEmMScuKCopJiodKxMsCCz6Lesu - 2y/HMLIxnDKFM2w0UjU2Nhc2+DfYOLg5lTpyO048Kj0FPd4+uD+RQGlBQkIaQvJDykSi - RXpGUkcqSAJI2kmxSopLY0w7TRRN7U7GT6BQeVFTUi1TBlPgVLlVlFZuV0hYIlj8WdZa - sFuKXGVdPV4XXvFfymCjYXxiVmMuZAdk32W3Zo5nZmg9aRRp62rCa5hsbm1Ebhlu7m/E - cJhxbXJAcxRz6HS7dY12YHcyeAR41nmoenl7SnwbfOx9vH6Nf12ALoD+gc+Cn4NwhEGF - EoXjhrSHh4hYiSqJ/YrRi6SMeI1MjiGO9o/MkKKReZJQkyiUAJTZlbKWjJdmmEGZHJn4 - mtWbsZyPnW2eS58qoAqg6qHMoq2jj6RypVamOqcfqAWo66nTqrurpKyOrXmuZa9SsECx - L7IfsxG0BLT4te625bfduNe507rQu9C80r3Wvty/5cDxwf/DEMQlxT3GWcd4yJzJxMrz - zCXNXs6cz+DRK9J909XVONag2BHZi9sN3JfeLN/J4W/jH+TX5proZOo37BHt8+/d8c3z - xfXD9735wPvK/dz//wAAbmRpbgAAAAAAAAY2AACXEAAAVyQAAFOiAACHmgAAKFUAABao - AABQDQAAVDkAAlmZAAJeuAABZmYAAwEAAAIAAAACAAUACwASABsAJgAyAEAATwBfAHEA - hACZAK8AxgDeAPgBEwEvAU0BawGLAawBzgHyAhcCPQJkAowCtgLgAwwDOQNoA5cDyAP6 - BC0EYgSYBM8FBwVBBXwFuAX2BjUGdQa3BvoHPweFB80IFghhCK0I+wlKCZsJ7gpCCpgK - 8AtJC6QMAQxgDMANIg2GDewOUw69DygPlRAEEHQQ5xFbEdESShLEEz8TvRQ9FL4VQRXH - Fk4W1xdiF+4YfRkOGaAaNRrLG2Qb/hybHTkd2R58HyAfxiBvIRohxiJ1IyYj2SSOJUYl - /ya7J3koOSj8KcEqiCtSLB4s7S2+LpEvZzA/MRox+DLYM7o0nzWHNnE3XjhNOT86Mzsq - PCM9Hz4ePx5AIkEoQjBDO0RIRVhGakd+SJVJr0rLS+lNCk4uT1RQfVGoUtZUBlU5Vm9X - p1jiWiBbYFyjXelfMmB9YctjHGRwZcdnIGh8adxrPmyjbgtvdnDjclRzyHU/drl4N3m3 - ezt8wn5Nf9yBboMEhJ6GPIfeiYSLL4zfjpOQTZILk8+VmJdnmTybFpz3nt6gzKLApLum - vKjFqtSs668IsS2zWLWKt8O6A7xJvpTA5cM8xZfH9cpYzLzPI9GK0/LWWtjA2yTdht/m - 4kLkm+bx6UPrku3f8CnycPS29vn5P/uD/cX//wAAAAIABQALABMAHAAnADMAQQBQAGEA - cwCHAJwAsgDKAOMA/QEZATUBUwFzAZMBtQHYAfwCIgJJAnECmgLEAvADHQNLA3sDqwPd - BBEERQR7BLIE6wUlBWAFnQXbBhoGWwadBuEHJwduB7YIAAhMCJkI6Ak5CYsJ4Ao1Co0K - 5wtCC58L/gxfDMENJg2MDfUOXw7LDzkPqRAbEI8RBBF8EfYScRLuE20T7xRyFPYVfRYG - FpAXHBerGDsYzRlhGfcajhsoG8McYR0AHaIeRR7rH5IgOyDnIZUiRCL2I6okYCUYJdMm - kCdPKBAo0ymZKmErLCv5LMgtmi5uL0QwHTD5MdcytzOaNH81ZzZRNz04LDkeOhI7CDwA - PPs9+T74P/pA/kIFQw5EGUUmRjZHSEhcSXJKikulTMJN4U8DUCdRTVJ1U59UzFX7Vy1Y - YFmWWs5cCV1FXoRfxmEJYk9jl2TiZi5nfWjOaiJrd2zPbipvhnDlckZzqnUQdnh343lQ - esB8M32ofx+AmYIXg5aFGYafiCiJtItDjNWOa5AEkaGTQZTllo2YOZnpm5ydVJ8QoNCi - laRepiun/anUq6+tj69zsV2zS7U+tzW5MrszvTq/RsFXw27Fi8evydnMCs5D0ITSztUk - 14TZ8Nxq3vPhjeQ45vbpyuyz77PyyfX3+TL8gv//AAAAAwAJABMAIAAwAEMAWABwAIsA - qADHAOkBDQE0AV0BiAG1AeUCFwJLAoICugL1AzMDcgO0A/gEPgSHBNMFIAVwBcMGGAZw - BssHKAeIB+sIUQi5CSUJlAoGCnwK9AtwC/AMcwz5DYMOEA6hDzYPzhBpEQgRqxJREvoT - phRWFQkVvxZ4FzQX8hi0GXgaPxsIG9Qcox10HkcfHR/1INAhrCKMI20kUSU4JiEnDCf5 - KOop3CrRK8kswy2/Lr8vwDDFMcwy1jPiNPE2AzcYODA5SjpnO4c8qj3PPvhAI0FSQoND - t0TuRilHZkimSepLMEx6TcZPFVBnUb1TFFRvVc1XLViPWfRbXFzFXjFfn2EPYoBj9GVp - Zt9oWGnRa0xsyW5Hb8ZxRnLHdEp1zndTeNl6YHvofXF++4CHghODoIUvhr6ITonei3CN - Ao6VkCiRvJNRlOaWe5gQmaabPJzRnmef/aGSoyekvKZQp+SpdqsIrJmuKa+4sUay0rRc - teS3a7jvunG78b1uvujAX8HTw0TEscYbx4HI4spAy5rM785Bz47Q1tIb01vUmNXO1wLY - Mdld2oPbp9zH3ePe/OAR4SLiMeM85EXlS+ZN507oS+lH6kDrOOwt7SDuEu8B7/Hw3fHJ - 8rPznfSE9Wv2UPc1+B35BPno+sz7r/yQ/W/+Tv8m//8AAGRlc2MAAAAAAAAACkNvbG9y - IExDRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMbmJO - TwAAABIAAADEc3ZTRQAAABAAAADWZmlGSQAAABAAAADmZGFESwAAABwAAAD2emhDTgAA - AAwAAAESZnJGUgAAABYAAAEeamFKUAAAAA4AAAE0ZW5VUwAAABIAAAFCcHRCUgAAABgA - AAFUZXNFUwAAABIAAAFsemhUVwAAAA4AAAF+a29LUgAAAAwAAAGMZGVERQAAABAAAAGY - bmxOTAAAABYAAAGoaXRJVAAAABQAAAG+AEYAYQByAGcAZQAtAEwAQwBEAEYA5AByAGcA - LQBMAEMARABWAOQAcgBpAC0ATABDAEQATABDAEQALQBmAGEAcgB2AGUAcwBrAOYAcgBt - X2mCcgAgAEwAQwBEAEwAQwBEACAAYwBvAHUAbABlAHUAcjCrMOkw/AAgAEwAQwBEAEMA - bwBsAG8AcgAgAEwAQwBEAEwAQwBEACAAYwBvAGwAbwByAGkAZABvAEwAQwBEACAAYwBv - AGwAbwByX2mCcm2yZnaYb3k6VmjO7LfsACAATABDAEQARgBhAHIAYgAtAEwAQwBEAEsA - bABlAHUAcgBlAG4ALQBMAEMARABMAEMARAAgAGMAbwBsAG8AcgBpAABtbW9kAAAAAAAA - BhAAAJxeAAAAAMAmoAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQgQXBw - bGUsIEluYy4sIDIwMDkA - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - - name - Canvas 1 - - - Frame - {{72, 0}, {710, 778}} - ListView - - OutlineWidth - 142 - RightSidebar - - ShowRuler - - Sidebar - - SidebarWidth - 120 - VisibleRegion - {{-53, 0}, {682, 624}} - Zoom - 1 - ZoomValues - - - Canvas 1 - 1 - 1 - - - - saveQuickLookFiles - YES - - diff --git a/libtorrent_utp/docs/merkle_tree.png b/libtorrent_utp/docs/merkle_tree.png deleted file mode 100644 index 303b68f90004933b3ef604d4092d4f9af7252cfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18792 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 diff --git a/libtorrent_utp/docs/miro.jpg b/libtorrent_utp/docs/miro.jpg deleted file mode 100644 index 69eb2bfb860f17be61ece028ab63497a32615cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8299 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 D3H9euN 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 JLlOxv{Xe_UJRtx8 diff --git a/libtorrent_utp/docs/our_delay_base.png b/libtorrent_utp/docs/our_delay_base.png deleted file mode 100644 index d14ad9ed86ad21a5fed783be6a3beecd1f9c1f96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34999 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 - - - - - -projects using libtorrent - - - - - - -

-
-
- -
- -
-

projects using libtorrent

- -

These are some of the public projects that uses libtorrent. If you want your -project listed here, let me know.

-
-firetorrent.png -

firetorrent

-

firetorrent is a single click torrent downloader plugin -for Firefox and Wyzo.

-
-
-tonidoplug.png -

tonidoplug

-

Tonidoplug is a tiny, low-power, low-cost home server and -NAS device powered by Tonido software that allows you to access -your apps, files, music and media from anywhere.

-
-
-folx.png -

Folx

-

Folx is a torrent client and download manager for Mac OS X. -The Free version of Folx has all the basic functionality of the torrent -client, which allows users to download and create torrent files. -Folx PRO (available for a small fee) features the possibility to search -for torrent files just from Folx interface. So there is no need to -browse through multiple torrent trackers searching for particular file.

-
-
-qbittorrent_thumb.jpg -

qBittorrent

-

qBittorrent is a QT bittorrent -client available for linux (likely -portable to most other desktops as -well). Written by Christophe Dumez.

-
-
-limewire.png -

Limewire

-

LimeWire is the world's most popular peer-to-peer file-sharing -program. With over 70 million unique monthly users, the software -is downloaded hundreds of thousands of times every day and boasts -millions of active users at any given moment. LimeWire uses the -BitTorrent protocol and the Gnutella network to provide an -unparalleled searches and download speed to the user

-
-
-deluge.png -

deluge torrent

-

deluge Torrent is a more full-featured yet still lightweight bittorrent -client. It has the ability to automatically resume partial downloads and -background to the system tray.

-
-
-bubba.png -

bubba

-

Bubba is a mini-sized server, designed to fit your home better than -an always running PC. Boasting Torrent downloader, DAAP streaming, -Web, E-mail, printer and FTP server etc.

-
-
-btg_thumb.jpg -

btg

-

btg is a unix bittorrent client -which is run as a daemon. It has -multiple user interfaces which -connects to the daemon. One GUI -(Gtkmm), one terminal interface -(ncurses) and one web interface -(accessable through a web browser). -Written by Michael Wojciechowski -and Johan Strˆm.

-
-
-tvblob.jpg -

tvblob

-

The BLOBbox represents the ability to harness all of the content available -on the web, without any filtering or pre-selection by a third party ø just -like surfing the web.

-

This means that anyone will have the ability to reach viewers via the Internet -directly on TV, without them having to connect a PC.

-
-
-halite_thumb.png -

halite BitTorrent

-

Halite is a windows bittorrent client controllabel via an xml-rpc -interface.

-
-
-fdm.png -

Free download manager

-

FDM is a powerful, easy-to-use and absolutely free download accelerator and -manager. Moreover, FDM is 100% safe, open-source software distributed under -GPL License.

-
-
-zyxel.png -

ZyXEL NSA-220

-

ZyXEL NSA220 makes it easy to store, protect and share files between users -on your home network. The built-in DLNA server works with many set top boxes -to allow you to play back music, watch video files, or view photos on your -home theater system, while the built in download manager can automatically -download video and audio podcasts as well as allow you to download bittorrent -files without needing to leave your computer on.

-
-
-t2e.jpg -

Torrent2Exe

-

Torrent2Exe Torrent2exe is a small BitTorrent client. Its basic idea is to -let users download a custom-built EXE program with the torrent file -integrated into it.

-
-
-flush.jpg -

Flush

-

Flush is a GTK-based BitTorrent client.

-
-
-pump.png -

Pump

-

Pump Pump brings together all the main features you need in order to find -content online and enjoy it on your PC and on your Mobile.

-
-
-lince.png -

Lince

-

Lince is a bittorrent client using libtorrent to handle bittorrent protocol -and gtkmm for the interface, it has been designed to be a light and full -featured client.

-
-
-electric_sheep_thumb.jpg -

electric sheep

-

electric sheep is a screensaver -which collectively generates -animations and lets the users -vote which one to live on.

-
-
-miro.jpg -

Miro

-

Miro is a free application for channels of internet video (also known as -video podcasts and video rss). Miro is designed to be easy to use and to give -you an elegant fullscreen viewing experience.

-
-
-tvitty.jpg -

Tvitty

-

tvitty is a bittorrent client for Vista Media Center, which allows -searching and downloading of torrents directly on your TV.

-
-
-fatrat.png -

FatRat

-

FatRat is an open source download manager for Linux/Unix systems written in -C++ with the help of the Trolltech Qt 4 library. It's simple to use and -install.

-
-
-leechcraft.png -

LeechCraft

-

LeechCraft LeechCraft is a free open source cross-platform extensible -software, which primary goal is support of file sharing networks and -protocols like HTTP and FTP

-
-
-moopolice_thumb.gif -

MooPolice

-

MooPolice is a windows bittorrent -client with a quite unique look.

-
-
-Linkage.png -

Linkage

-

Linkage is a gtkmm client that aims to be middle weight.

-
-
-bitrocket_thumb.png -

BitRocket

-

BitRocket is a MacOS X bittorrent client.

-
-
-bitscast_thumb.png -

BitsCast

-

RSS reader, podcast subscriber, -video feed downloader, bittorrent -feed downloader. See BitsCast -homepage for more info.

-
-
-bitfox.png -

Bitfox

-

Bitfox is a firefox plugin integrating bittorrent downloads in firefox.

-
-
-bitslug_thumb.png -

BitSlug

-

BitSlug is a MacOSX cocoa client.

-
-
-arctic_thumb.png -

Arctic Torrent

-

Arctic Torrent is a light-weight -bittorrent client for windows. -Written by Cory Nelson.

-
-

hrktorrent

-

hrktorrent hrktorrent is a light console torrent client written in C++.

-

DelCo

-

DelCo is a research project at Tampere university of technology, finland.

-

FireANT

-

FireANT is a video blog available -for Windows and Mac OSX.

-
- -
- - -
- - diff --git a/libtorrent_utp/docs/projects.rst b/libtorrent_utp/docs/projects.rst deleted file mode 100644 index cd764a4e4..000000000 --- a/libtorrent_utp/docs/projects.rst +++ /dev/null @@ -1,463 +0,0 @@ -projects using libtorrent -========================= - -These are some of the public projects that uses libtorrent. If you want your -project listed here, let me_ know. - -.. _me: mailto:arvid@rasterbar.com - - -.. container:: entry - - .. image:: firetorrent.png - :align: right - :target: firetorrent_ - - **firetorrent** - - firetorrent_ is a single click torrent downloader plugin - for Firefox and Wyzo. - - .. _firetorrent: http://www.fireaddons.com/ - - -.. container:: entry - - .. image:: tonidoplug.png - :align: right - :target: Tonidoplug_ - - **tonidoplug** - - Tonidoplug_ is a tiny, low-power, low-cost home server and - NAS device powered by Tonido software that allows you to access - your apps, files, music and media from anywhere. - - .. _Tonidoplug: http://www.tonidoplug.com/ - - -.. container:: entry - - .. image:: folx.png - :align: right - :target: folx_ - - **Folx** - - Folx_ is a torrent client and download manager for Mac OS X. - The Free version of Folx has all the basic functionality of the torrent - client, which allows users to download and create torrent files. - Folx PRO (available for a small fee) features the possibility to search - for torrent files just from Folx interface. So there is no need to - browse through multiple torrent trackers searching for particular file. - - .. _folx: http://www.mac-downloader.com/ - - -.. container:: entry - - .. image:: qbittorrent_thumb.jpg - :align: right - :target: qBittorrent_ - - **qBittorrent** - - qBittorrent_ is a QT bittorrent - client available for linux (likely - portable to most other desktops as - well). Written by Christophe Dumez. - - .. _qBittorrent: http://www.qbittorrent.org/ - - -.. container:: entry - - .. image:: limewire.png - :align: right - :target: Limewire_ - - **Limewire** - - LimeWire_ is the world's most popular peer-to-peer file-sharing - program. With over 70 million unique monthly users, the software - is downloaded hundreds of thousands of times every day and boasts - millions of active users at any given moment. LimeWire uses the - BitTorrent protocol and the Gnutella network to provide an - unparalleled searches and download speed to the user - - -.. container:: entry - - .. image:: deluge.png - :align: right - :target: `deluge Torrent`_ - - **deluge torrent** - - `deluge Torrent`_ is a more full-featured yet still lightweight bittorrent - client. It has the ability to automatically resume partial downloads and - background to the system tray. - - - -.. container:: entry - - .. image:: bubba.png - :align: right - :target: bubba2_ - - **bubba** - - Bubba_ is a mini-sized server, designed to fit your home better than - an always running PC. Boasting Torrent downloader, DAAP streaming, - Web, E-mail, printer and FTP server etc. - - .. _bubba2: http://excito.com/bubba/about-bubba.html - .. _Bubba: http://excito.com/bubba/about-bubba.html - - - -.. container:: entry - - .. image:: btg_thumb.jpg - :align: right - :target: btg_ - - **btg** - - btg_ is a unix bittorrent client - which is run as a daemon. It has - multiple user interfaces which - connects to the daemon. One GUI - (Gtkmm), one terminal interface - (ncurses) and one web interface - (accessable through a web browser). - Written by Michael Wojciechowski - and Johan Ström. - - .. _btg: http://btg.berlios.de// - - - -.. container:: entry - - .. image:: tvblob.jpg - :align: right - :target: BLOBbox_ - - **tvblob** - - The BLOBbox_ represents the ability to harness all of the content available - on the web, without any filtering or pre-selection by a third party ż just - like surfing the web. - - This means that anyone will have the ability to reach viewers via the Internet - directly on TV, without them having to connect a PC. - - - -.. container:: entry - - .. image:: halite_thumb.png - :align: right - :target: Halite_ - - **halite BitTorrent** - - Halite_ is a windows bittorrent client controllabel via an xml-rpc - interface. - - - -.. container:: entry - - .. image:: fdm.png - :align: right - :target: FDM_ - - **Free download manager** - - FDM_ is a powerful, easy-to-use and absolutely free download accelerator and - manager. Moreover, FDM is 100% safe, open-source software distributed under - GPL License. - - - -.. container:: entry - - .. image:: zyxel.png - :align: right - :target: ZyXEL_ - - **ZyXEL NSA-220** - - ZyXEL_ NSA220 makes it easy to store, protect and share files between users - on your home network. The built-in DLNA server works with many set top boxes - to allow you to play back music, watch video files, or view photos on your - home theater system, while the built in download manager can automatically - download video and audio podcasts as well as allow you to download bittorrent - files without needing to leave your computer on. - - .. _ZyXEL: http://us.zyxel.com/Products/details.aspx?PC1IndexFlag=20050125090459&CategoryGroupNo=758BFE64-3A95-463C-9E1E-3D30E3B58D9C - - - -.. container:: entry - - .. image:: t2e.jpg - :align: right - :target: Torrent2Exe_ - - **Torrent2Exe** - - Torrent2Exe_ Torrent2exe is a small BitTorrent client. Its basic idea is to - let users download a custom-built EXE program with the torrent file - integrated into it. - - .. _Torrent2Exe: http://torrent2exe.com - - - -.. container:: entry - - .. image:: flush.jpg - :align: right - :target: Flush_ - - **Flush** - - Flush_ is a GTK-based BitTorrent client. - - .. _Flush: https://sourceforge.net/projects/flush/ - - - -.. container:: entry - - .. image:: pump.png - :align: right - :target: Pump_ - - **Pump** - - Pump_ Pump brings together all the main features you need in order to find - content online and enjoy it on your PC and on your Mobile. - - .. _Pump: http://pump.vipeers.com/ - - -.. container:: entry - - .. image:: lince.png - :align: right - :target: Lince_ - - **Lince** - - Lince_ is a bittorrent client using libtorrent to handle bittorrent protocol - and gtkmm for the interface, it has been designed to be a light and full - featured client. - - .. _Lince: http://lincetorrent.sourceforge.net/ - - - -.. container:: entry - - .. image:: electric_sheep_thumb.jpg - :align: right - :target: `electric sheep`_ - - **electric sheep** - - `electric sheep`_ is a screensaver - which collectively generates - animations and lets the users - vote which one to live on. - - .. _`electric sheep`: http://electricsheep.org - - - -.. container:: entry - - .. image:: miro.jpg - :align: right - :target: Miro_ - - **Miro** - - Miro_ is a free application for channels of internet video (also known as - video podcasts and video rss). Miro is designed to be easy to use and to give - you an elegant fullscreen viewing experience. - - .. _Miro: http://getmiro.com - - -.. container:: entry - - .. image:: tvitty.jpg - :align: right - :target: tvitty_ - - **Tvitty** - - tvitty_ is a bittorrent client for Vista Media Center, which allows - searching and downloading of torrents directly on your TV. - - .. _tvitty: http://tvitty.com - - -.. container:: entry - - .. image:: fatrat.png - :align: right - :target: FatRat_ - - **FatRat** - - FatRat_ is an open source download manager for Linux/Unix systems written in - C++ with the help of the Trolltech Qt 4 library. It's simple to use and - install. - - - -.. container:: entry - - .. image:: leechcraft.png - :align: right - :target: LeechCraft_ - - **LeechCraft** - - LeechCraft_ LeechCraft is a free open source cross-platform extensible - software, which primary goal is support of file sharing networks and - protocols like HTTP and FTP - - - -.. container:: entry - - .. image:: moopolice_thumb.gif - :align: right - :target: MooPolice_ - - **MooPolice** - - MooPolice_ is a windows bittorrent - client with a quite unique look. - - .. _MooPolice: http://www.moopolice.de - - - -.. container:: entry - - .. image:: Linkage.png - :align: right - :target: Linkage_ - - **Linkage** - - Linkage_ is a gtkmm client that aims to be middle weight. - - .. _Linkage: http://code.google.com/p/linkage/ - - - -.. container:: entry - - .. image:: bitrocket_thumb.png - :align: right - :target: BitRocket_ - - **BitRocket** - - BitRocket_ is a MacOS X bittorrent client. - - - -.. container:: entry - - .. image:: bitscast_thumb.png - :align: right - :target: BitsCast_ - - **BitsCast** - - RSS reader, podcast subscriber, - video feed downloader, bittorrent - feed downloader. See BitsCast_ - homepage for more info. - - - -.. container:: entry - - .. image:: bitfox.png - :align: right - :target: BitFox_ - - **Bitfox** - - Bitfox_ is a firefox plugin integrating bittorrent downloads in firefox. - - .. _Bitfox: http://code.google.com/p/bitfox/ - - -.. container:: entry - - .. image:: bitslug_thumb.png - :align: right - :target: BitSlug_ - - **BitSlug** - - BitSlug_ is a MacOSX cocoa client. - - - -.. container:: entry - - .. image:: arctic_thumb.png - :align: right - :target: `Arctic Torrent`_ - - **Arctic Torrent** - - `Arctic Torrent`_ is a light-weight - bittorrent client for windows. - Written by Cory Nelson. - -.. _`Arctic Torrent`: http://www.int64.org/arctic.html - - -**hrktorrent** - -hrktorrent_ hrktorrent is a light console torrent client written in C++. - -.. _hrktorrent: http://50hz.ws/hrktorrent/ - -**DelCo** - -DelCo_ is a research project at Tampere university of technology, finland. - -**FireANT** - -FireANT_ is a video blog available -for Windows and Mac OSX. - -.. _FireANT: http://antisnottv.net/ - -.. _FDM: http://www.freedownloadmanager.org/ -.. _LeechCraft: http://leechcraft.org/ -.. _FatRat: http://fatrat.dolezel.info -.. _BitRocket: http://www.bitrocket.org/ -.. _BitsCast: http://www.bitscast.com/software/ -.. _BitSlug: http://bitslug.sourceforge.net/ -.. _`deluge Torrent`: http://deluge-torrent.org/ -.. _Limewire: http://www.limewire.com/ -.. _BLOBbox: http://www.tvblob.com -.. _Halite: http://www.binarynotions.com/halite-bittorrent-client -.. _DelCo: http://delco.cs.tut.fi/ - diff --git a/libtorrent_utp/docs/pump.png b/libtorrent_utp/docs/pump.png deleted file mode 100644 index 8c395989cf3dadda33ec4e0bf1499b317fded5dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68026 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 DV>Xj> diff --git a/libtorrent_utp/docs/python_binding.html b/libtorrent_utp/docs/python_binding.html deleted file mode 100644 index 3d80b0e85..000000000 --- a/libtorrent_utp/docs/python_binding.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - -libtorrent python binding - - - - - - - - - - diff --git a/libtorrent_utp/docs/python_binding.rst b/libtorrent_utp/docs/python_binding.rst deleted file mode 100644 index 1aa92803e..000000000 --- a/libtorrent_utp/docs/python_binding.rst +++ /dev/null @@ -1,129 +0,0 @@ -========================= -libtorrent python binding -========================= - -:Author: Arvid Norberg, arvid@rasterbar.com - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -building -======== - -Building the libtorrent python bindings will produce a shared library (DLL) -which is a python module that can be imported in a python program. - -building using setup.py ------------------------ - -There is a ``setup.py`` shipped with libtorrent that can be used on windows. -On windows the setup.py will invoke ``bjam`` and assume that you have boost -sources at ``$BOOST_PATH``. The resulting executable is self-contained, it does -not depend any boost or libtorrent dlls. - -On other systems, the setup.py is generated by running -``./configure --enable-python-binding``. - -To build the Python bindings do: - -1. Run:: - - python setup.py build - -2. As root, run:: - - python setup.py install - - -building using boost build --------------------------- - -To set up your build environment, you need to add some settings to your -``$BOOST_BUILD_PATH/user-config.jam``. - -Make sure your user config contains the following line:: - - using python : 2.3 ; - -Set the version to the version of python you have installed or want to use. If -you've installed python in a non-standard location, you have to add the prefix -path used when you installed python as a second option. Like this:: - - using python : 2.3 : /usr ; - -The bindings require *at least* python version 2.2. - -For more information on how to install and set up boost-build, see the -`building libtorrent`__ section. - -.. __: building.html#step-2-setup-bbv2 - -Once you have boost-build set up, you cd to the ``bindings/python`` -directory and invoke ``bjam`` with the apropriate settings. For the available -build variants, see `libtorrent build options`_. - -.. _`libtorrent build options`: building.html#step-3-building-libtorrent - -For example:: - - $ bjam dht-support=on boost=source release link=static - -On Mac OS X, this will produce the following python module:: - - bin/darwin-4.0/release/dht-support-on/link-static/logging-none/threading-multi/libtorrent.so - -using libtorrent in python -========================== - -The python interface is nearly identical to the C++ interface. Please refer to -the `main library reference`_. The main differences are: - -asio::tcp::endpoint - The endpoint type is represented as a tuple of a string (as the address) and an int for - the port number. E.g. ``('127.0.0.1', 6881)`` represents the localhost port 6881. - -libtorrent::time_duration - The time duration is represented as a number of seconds in a regular integer. - -The following functions takes a reference to a container that is filled with -entries by the function. The python equivalent of these functions instead returns -a list of entries. - -* torrent_handle::get_peer_info -* torrent_handle::file_progress -* torrent_handle::get_download_queue -* torrent_handle::piece_availability - -``create_torrent::add_node()`` takes two arguments, one string and one integer, -instead of a pair. The string is the address and the integer is the port. - -.. _`main library reference`: manual.html - -For an example python program, see ``client.py`` in the ``bindings/python`` -directory. - -A very simple example usage of the module would be something like this:: - - import libtorrent as lt - import time - - ses = lt.session() - ses.listen_on(6881, 6891) - - e = lt.bdecode(open("test.torrent", 'rb').read()) - info = lt.torrent_info(e) - - h = ses.add_torrent(info, "./", storage_mode=storage_mode_sparse) - - while (not h.is_seed()): - s = h.status() - - state_str = ['queued', 'checking', 'downloading metadata', \ - 'downloading', 'finished', 'seeding', 'allocating'] - print '%.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d) %s' % \ - (s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \ - s.num_peers, state_str[s.state]) - - time.sleep(1) - diff --git a/libtorrent_utp/docs/qbittorrent_thumb.jpg b/libtorrent_utp/docs/qbittorrent_thumb.jpg deleted file mode 100644 index a445698fa8b741d1d505e0e15295f9eaddcc6c8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5009 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 iRF|Om0&l!EY_AL#*Lp)k>`u(W3Vm;>B0^c "send buffer" [label="copy into peer's send buffer (copy)"] - "send buffer" -> "encrypted send buffer" [label="encrypt in-place (no copy)" style=dashed]; - } - - subgraph kernel { - rank=same; - "kernel page cache"; - "socket kernel buffer" - } - - "encrypted send buffer" -> "socket kernel buffer" [label="write() to socket (copy)"]; - "kernel page cache" -> "disk cache" [label="read() from file (copy)"] -} - diff --git a/libtorrent_utp/docs/read_disk_buffers.graffle b/libtorrent_utp/docs/read_disk_buffers.graffle deleted file mode 100644 index b9cfece81..000000000 --- a/libtorrent_utp/docs/read_disk_buffers.graffle +++ /dev/null @@ -1,2834 +0,0 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 137.11.0.108132 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {830, 733}} - Class - SolidGraphic - ID - 2 - Style - - fill - - GradientColor - - w - 0.666667 - - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - CanvasOrigin - {0, 0} - CanvasSize - {830, 733} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2009-05-24 19:00:26 -0700 - Creator - Arvid Norberg - DisplayScale - 1.000 cm = 1.000 cm - FileType - flat - GraphDocumentVersion - 6 - GraphicsList - - - Bounds - {{185.661, 16}, {119.839, 40}} - Class - ShapedGraphic - Head - - ID - 49 - - ID - 59 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 1 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 copy into send buffer (copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{672.538, 90.9619}, {94.9237, 40}} - Class - ShapedGraphic - Head - - ID - 55 - - ID - 56 - Rotation - 90 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 51 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 writev() to file (copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{630, 158.924}, {180, 39.0763}} - Class - ShapedGraphic - ID - 55 - Shape - Rectangle - Style - - fill - - Color - - b - 0.853983 - g - 0.680927 - r - 0.98913 - - GradientColor - - b - 0.762853 - g - 0.292179 - r - 0.853261 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 kernel page cache} - - - - Bounds - {{486.5, 16}, {134, 40}} - Class - ShapedGraphic - Head - - ID - 51 - - ID - 54 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.4 - g - 1 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.8 - r - 1 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.4822 - r - 0.910613 - - - - Tail - - ID - 49 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 in-place decrypt (no-copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{621, 9}, {198, 54}} - Class - ShapedGraphic - ID - 51 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 encrypted send buffer\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\fs24 \cf0 managed by the disk_io_thread} - - - - Bounds - {{306, 9}, {180, 54}} - Class - ShapedGraphic - ID - 49 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 send buffer\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\fs24 \cf0 managed by peer_connection} - - - - Bounds - {{54.1186, 90.9619}, {94.9237, 40}} - Class - ShapedGraphic - Head - - ID - 1 - - ID - 30 - Rotation - 270 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 3 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 read() from file (copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{18, 158.924}, {167.161, 39.0763}} - Class - ShapedGraphic - ID - 3 - Shape - Rectangle - Style - - fill - - Color - - b - 0.853983 - g - 0.680927 - r - 0.98913 - - GradientColor - - b - 0.79381 - g - 0.730413 - r - 0.853261 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 kernel page cache} - - - - Bounds - {{18, 9}, {167.161, 54}} - Class - ShapedGraphic - ID - 1 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 disk cache -\fs24 \ -managed by disk_io_thread} - - - - GridInfo - - SnapsToGrid - YES - - GuidesLocked - NO - GuidesVisible - YES - HPages - 2 - ImageCounter - 3 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - AutoLayout - 2 - LineLength - 0.4643835723400116 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2009-05-24 19:20:44 -0700 - Modifier - Arvid Norberg - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - OutlineStyle - Basic - PageBreaks - NO - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {612, 792} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - QuickLookPreview - - JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGlWMty4zYQvPMr5mgfRAMgHsQ1TlKV - nLKxq3JxlUsr05F29VpZu6n92XxLevAgYYqSncQ+GADBGaBnpqfpL/SBvpDAb9sIck1D - h47+oC2J2orwQyu6uX2RtHghGX5fFjQTtUlPh1EyUMHAM9381h0W3f74db6mwwoupGcn - cGHJkTWGFhu6+WUj6cddOEJ+bhWeG+lr4WxTxU0qb8JDtiGlIwfHrTBkrKyzsSbvUyJu - hDPlDbtLlnS/Q/XHsVIUBzJ5hzQ2OINP7Qx8WKvToW2VDp39YI+Vvj922uayqeLYtsW2 - 4shtNiV1PDL2Sqdra2Xh0EdLN7936/lx9a273a13h9WmOx5WC4ZXtmQdcLGulnjR6BgE - Sb8iYp9i/G7vwnUE3d1yOMJkxn846MAZNnxhgfPgDpFB7FWF2AtC3AWSoXhXCmDPQbDe - sYkf7tkKAzvD35lWtVFS88o9gv2zrEUl6f6Zrp5WL59pMV8su2u6/0Q/3ccUSK/SlN2I - zkwqmrmmdtK2JE2V7eI+bHcz387/7J7o43diF4+r3eNxeejmT4ObqbwWtW+9bISu8LQV - XlmD/G9N41tgg3vjNqYxPTpNyE7lGaPnEpASTGna2qvGnbzFscnIclVdRNYYXZumRfmd - wAsYjJEKpTCG4XN32HZr2gOLMcpc7VKmsBkkmkGZ0YZ8WNK+6tfW/RpZ7ZH/a2pzuNN8 - iJONG6Qqd1TrwVU2sRyWek+bQDgwLkJB41Bc+6QleRMglAwZk9IJqyDnQyoPJ1GqFgmv - m7ulpJclcmvEcdlcro+TnOBaQSZ4YZgmaqGc1Eh0hGqieJhaekg56pqjPkCKPE1rBaQt - glcgGqfVcI34uMczbe/dpHmBZu9kw8lVcdmOkusiVly6RYkpX1snWxRxM04uLqeHq4dr - ej7sNjQUFneRC8EYO7C1ELbVQG7s4O//bBOROmNz3dHD1WK3//5wPVg/CXvZ4hphI6ci - 7JlPY8uYSAGuTn4BBBq2l+Q5ikJPvI2HA+84e8a8qevWqdB+Rvz20m1Bbl+fhzsUbHzW - oGrq1lqH+IzMPXeH91gq8sKB+cAY6FFmbKzg3n3XHR4Xu+22WxxXuy37qALHX0TcKrQ8 - 7mIebKvPcmvF+wB0v+stLnWo4ekG1dq69U6jz49zsNsuDt/3R7SSi5CfNW0NLMsJw+cg - H1kqIEepO6XR1S+2u+OyO9vymP007oo+3TIxhXHlhAMBQeRh/dUY4grritcZtrzHumIM - O2taJkvRKvMzLCOE7EmiXYLAz/F2XwXGMNOl8ICwVTVF2NnOZcL2Ulh0cRC2bpWCPq0m - +ZqLNcKByOehsv1FiyEimKCommEDKjODAgs9DhTMvZWN/YVz4RehhmBqjNYerDuurtV2 - tl/PFwhyFzKzJF6IKmYmiLneeJYLhXFl6kZDyEhIpaTGkmp6uNruZokcqyzHpkq110mh - O07oJAv0g1ACC2aRFKXoRerk1wa5VLw74lHE81SEcuW8SyW9vnZ19YZKcvxZEHTNBhI5 - j9dhbHwSTphqUZVTPk2WVXgqMC02w2YxXVLpBAVUWc9Cig2MJVAugQkJhMBHIc9El/RP - +N65W0K9TuifbOtyOSX9A018Wf9wroV7cCUnrKJ8Ydhk04sfhqqc4ri9aolQlU8BVTFN - UEUXucZG3yWhBE6QmCoFWzeNRv8t6ix9mfx1WB27b0HfHHdlkcUCe6f1czJkJG2mAjd1 - 3H+pa4LGb038hgyML9Ms8rzCzCT+D2OEj/m8EeEzN3B+XI+cn8aJ87OtnvUrXuHPzsT8 - 6Bxt49/F/krD4/ABWaEB6P+fsYViP98B8jUCaedJoP543WIYukAAJ3aBuCF0gThMXSBb - eVcfKK8+0QpQNRZfjyzAX/MWBRlLqy3SM2iTSQX3lnXUkdMKMu6kGZxTl29YlPjPg2jM - lEVIngnx/eEft2EOGAplbmRzdHJlYW0KZW5kb2JqCjUgMCBvYmoKMTQ2NQplbmRvYmoK - MiAwIG9iago8PCAvVHlwZSAvUGFnZSAvUGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAw - IFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDgzMCA3MzNdCj4+CmVuZG9i - ago2IDAgb2JqCjw8IC9Qcm9jU2V0IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMg - L0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8IC9DczEgNyAwIFIKL0NzMiAyNiAwIFIgPj4g - L0ZvbnQgPDwgL0YxLjAgMjcgMCBSID4+IC9YT2JqZWN0IDw8IC9JbTcgMjAgMCBSIC9J - bTQgMTQgMCBSCi9JbTkgMjQgMCBSIC9JbTMgMTIgMCBSIC9JbTIgMTAgMCBSIC9JbTYg - MTggMCBSIC9JbTEgOCAwIFIgL0ltOCAyMiAwIFIgL0ltNQoxNiAwIFIgPj4gL1NoYWRp - bmcgPDwgL1NoMSAyOCAwIFIgL1NoMyAzMCAwIFIgL1NoNCAzMSAwIFIgL1NoMiAyOSAw - IFIgPj4gPj4KZW5kb2JqCjI4IDAgb2JqCjw8IC9Db2xvclNwYWNlIDcgMCBSIC9TaGFk - aW5nVHlwZSAyIC9Db29yZHMgWyA0Ny45NjE4NiAtMjAuNSA0Ny45NjE4NiAyMC41MDAw - MgpdIC9Eb21haW4gWyAwIDEgXSAvRXh0ZW5kIFsgZmFsc2UgZmFsc2UgXSAvRnVuY3Rp - b24gMzIgMCBSID4+CmVuZG9iagozMCAwIG9iago8PCAvQ29sb3JTcGFjZSA3IDAgUiAv - U2hhZGluZ1R5cGUgMiAvQ29vcmRzIFsgNDcuOTYxODUgLTIwLjUgNDcuOTYxODUgMjAu - NTAwMDIKXSAvRG9tYWluIFsgMCAxIF0gL0V4dGVuZCBbIGZhbHNlIGZhbHNlIF0gL0Z1 - bmN0aW9uIDMzIDAgUiA+PgplbmRvYmoKMzEgMCBvYmoKPDwgL0NvbG9yU3BhY2UgNyAw - IFIgL1NoYWRpbmdUeXBlIDIgL0Nvb3JkcyBbIDYwLjQxOTUgLTIwLjUgNjAuNDE5NDkg - MjAuNTAwMDIKXSAvRG9tYWluIFsgMCAxIF0gL0V4dGVuZCBbIGZhbHNlIGZhbHNlIF0g - L0Z1bmN0aW9uIDM0IDAgUiA+PgplbmRvYmoKMjkgMCBvYmoKPDwgL0NvbG9yU3BhY2Ug - NyAwIFIgL1NoYWRpbmdUeXBlIDIgL0Nvb3JkcyBbIDY3LjUgLTIwLjUgNjcuNDk5OTgg - MjAuNTAwMDMKXSAvRG9tYWluIFsgMCAxIF0gL0V4dGVuZCBbIGZhbHNlIGZhbHNlIF0g - L0Z1bmN0aW9uIDM1IDAgUiA+PgplbmRvYmoKMjAgMCBvYmoKPDwgL0xlbmd0aCAyMSAw - IFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA0MDQgL0hlaWdo - dCAxMjQgL0NvbG9yU3BhY2UKMzYgMCBSIC9TTWFzayAzNyAwIFIgL0JpdHNQZXJDb21w - b25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt0DEBAAAAwqD1 - T20MH4hAYcCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG/gMD - Sy4AAQplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2JqCjY3OQplbmRvYmoKMTQgMCBvYmoK - PDwgL0xlbmd0aCAxNSAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9X - aWR0aCA0MDQgL0hlaWdodCAxNTIgL0NvbG9yU3BhY2UKMzkgMCBSIC9TTWFzayA0MCAw - IFIgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJl - YW0KeAHt0DEBAAAAwqD1T+1pCYhAYcCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYOADA8++AAEKZW5kc3RyZWFtCmVuZG9iagoxNSAwIG9iago4MjcKZW5kb2Jq - CjI0IDAgb2JqCjw8IC9MZW5ndGggMjUgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBl - IC9JbWFnZSAvV2lkdGggMjg0IC9IZWlnaHQgMTI0IC9Db2xvclNwYWNlCjQyIDAgUiAv - U01hc2sgNDMgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNv - ZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9tDB+IQGHAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMPAcGJy/AAEKZW5kc3RyZWFtCmVu - ZG9iagoyNSAwIG9iago0ODQKZW5kb2JqCjEyIDAgb2JqCjw8IC9MZW5ndGggMTMgMCBS - IC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTI0IC9IZWlnaHQg - MjM0IC9Db2xvclNwYWNlCjQ1IDAgUiAvU01hc2sgNDYgMCBSIC9CaXRzUGVyQ29tcG9u - ZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9t - Cy+IQGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMPAYGFQXAAEKZW5kc3RyZWFtCmVuZG9iagox - MyAwIG9iago0MDMKZW5kb2JqCjEwIDAgb2JqCjw8IC9MZW5ndGggMTEgMCBSIC9UeXBl - IC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMzgwIC9IZWlnaHQgMTI0IC9D - b2xvclNwYWNlCjQ4IDAgUiAvU01hc2sgNDkgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDgg - L0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dCBAAAAAMOg+VMf5IVQYcCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMDA - y8AAKE4AAQplbmRzdHJlYW0KZW5kb2JqCjExIDAgb2JqCjYzOQplbmRvYmoKMTggMCBv - YmoKPDwgL0xlbmd0aCAxOSAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdl - IC9XaWR0aCAzMTIgL0hlaWdodCAxMjQgL0NvbG9yU3BhY2UKNTEgMCBSIC9TTWFzayA1 - MiAwIFIgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+Pgpz - dHJlYW0KeAHt0AENAAAAwqD3T20PBxEoDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGngcGxW8AAQplbmRzdHJlYW0KZW5kb2JqCjE5IDAgb2Jq - CjUyOQplbmRvYmoKOCAwIG9iago8PCAvTGVuZ3RoIDkgMCBSIC9UeXBlIC9YT2JqZWN0 - IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMzgwIC9IZWlnaHQgMTUyIC9Db2xvclNwYWNl - CjU0IDAgUiAvU01hc2sgNTUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAv - RmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9tDQ+IQGHAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgy8Dwyk/gABCmVuZHN0cmVhbQplbmRv - YmoKOSAwIG9iago3NzkKZW5kb2JqCjIyIDAgb2JqCjw8IC9MZW5ndGggMjMgMCBSIC9U - eXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTI0IC9IZWlnaHQgMjM0 - IC9Db2xvclNwYWNlCjU3IDAgUiAvU01hc2sgNTggMCBSIC9CaXRzUGVyQ29tcG9uZW50 - IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9tCy+I - QGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMPAYGFQXAAEKZW5kc3RyZWFtCmVuZG9iagoyMyAw - IG9iago0MDMKZW5kb2JqCjE2IDAgb2JqCjw8IC9MZW5ndGggMTcgMCBSIC9UeXBlIC9Y - T2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggNDQwIC9IZWlnaHQgMTUyIC9Db2xv - clNwYWNlCjYwIDAgUiAvU01hc2sgNjEgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0Zp - bHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dABDQAAAMKg909tDjeIQGHAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MPA0MA/tAAEKZW5kc3RyZWFtCmVuZG9iagoxNyAwIG9iago4OTgKZW5kb2JqCjUyIDAg - b2JqCjw8IC9MZW5ndGggNTMgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFn - ZSAvV2lkdGggMzEyIC9IZWlnaHQgMTI0IC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9C - aXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB - 7Z1nQ9vIFoZDloTeu8GAMS5gTDNgenXovYaQLGQ3e///T7jvOTOj5pEsJOfeLNH5IE05 - M5IfjaZp5vjdu0giAhGBiEBEICIQEYgIRAQiAhGBiEBEwEqgRog1KHJXICCZqVMF7Sia - CUha74VIX8SmEgEGBWZ/SIGTgyql+83jiZGAVstC9AS63xyM989X1GprPyiprWV0NTXe - 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 - ZpAwCMWGlsoOprEojcvUlhCcbMUjMEKbYcO4CRagSczLmy4ydcx2pWKyxM0v0A74KtiV - YnDCzH4tW32DFTPmF9aIGfhbHoHwOY7OS5jRxi8nICZM00XhENKjVM6cDL8wjULGKghc - Ne2YYVkcWoefYTePn4DzUNn+nkhh/HJ6AbQiNaDukafVbh4MwMHuQtXs5tF6QiIHec9S - JQON0nKh8+SZu6lsYeFEz34jnpK450m9ejmM5MYhW007jUTOYCcIhjsK/jhafpAR5sdh - SQcziC6ilFSGmnsWZozw7ekn2QUV5Kp11PyAIEGKhw2/F6zy+6eJC5qapcbhZ9mhLb9q - NUIYWJCM3Ei/x9Yfm3hmLmd8TLvHGMD93+wee95p9SNtlKweH5eqMaYYuTsS2dn2wYxU - aBwZ2XX3CcuqJmo4aRgw+h8BKxpPN7hRgSNw0f9WeJKyR9KLGv1Pip2JHx9xM8BF/8vj - B5nQMcCJiTKaBcCYto7/W0a7ndl/1m9aE9y4xAnTzjwPgJFt9L9jlR46OnwMLvqfu0qk - HPESnDT3z/MB0f8qOhhpvTTEEGNcMUFAQ1yMfRGqVY8CFQECR3NkljkBDlLx0dmFAFNi - dDTHIn0uulGwjYCEpU62uMjjTSAqaN58otiIQEQgIhARiAhEBCICEYGIQEQgOIH/AhkC - ktwKZW5kc3RyZWFtCmVuZG9iago1MyAwIG9iago0MTM1CmVuZG9iago2MSAwIG9iago8 - PCAvTGVuZ3RoIDYyIDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dp - ZHRoIDQ0MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1Bl - ckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae2a6U9T - 3RaHZe48QGmhg62nZWhrqZVqBdSWQBCkKKBoHSBohVgsVg0NjTiUIDQ0iFQhDEFAAhpQ - AoYgMWruv3bXqeZeX6jb9/1wjzs36/eBmKwSF8/DWnuflgMHMEgACSABJIAEkAASQAJI - 4H9BIA3zxwn8I6/Qbfp/k4H5AwT+yz8ddPwNfd+dQaeZmVmYP0wgMxNEsAp/py5p7buy - 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 - Hf9R83f+Aa/H/GECf8cTvgYJIAEkgASQABJAAkgACfxzAv8GW8H8TwplbmRzdHJlYW0K - ZW5kb2JqCjYyIDAgb2JqCjM2NjgKZW5kb2JqCjQwIDAgb2JqCjw8IC9MZW5ndGggNDEg - MCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggNDA0IC9IZWln - aHQgMTUyIC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDgg - L0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7Z39T5J/F8fzWZ5BERQk6AIf - gJCupEitwOHMByyzNHrQWVQLw6gWi2UPNFMmM9PS+TA1c2pTY9qcuVbt/tfuc2H3/b1L - +vT9blf355fz/qG1HfO416tzzgX+wL59GCSABJAAEkACSAAJIAG6BNIwf5TAP7ILP0n6 - X8nA8EzgL7bpgPpvqNn1AT9FZmYW5g8SyMwEyJye32lJGtnVkZ2Tk7sbAYZHAt+h5uRk - g3AQ8xsr341kZWWDDoFQKBKJxGKxBMMrAUAKYIVCQW4u54VshVOSARsLhIAOsUQqlcnl - CgzvBORymVQqATNC0LJr5RcLLKkEZoQzIpHKFIq8fKWyoEClUmN4JKBSFRQolfl5CoVM - KuGswKzAAksthZsSbkg4I3LwoVIXFmk02uJiHYZHAsXFWo2mqFCtAi/ypBUYFU5Kigew - pBI4JCIxGAEhYEOnNxgOMEYMrwSYAwaDXgdmQAtYEYu4s5JaShp3S3IEMCSKfFWhBnww - xpLSsnKz2WKxYngiYLGYzeVlpSVGBrxoClX5ChgVQQ536fcOCowJKMkVSWQKpVqjMzCm - UrPloM1uZ1n2MIY3AoDTbrcdtJhLTYxBp1ErFTIJTEpWZortBWMC512YVKLVMyXlVpud - rTxy1FlVDanB8EKAY1nlPHqkkrXbrOUljF6blCKEQ59iUNJgTHKFYqlCWajVG8usFazD - WVVzwuWu9Xg8dRieCADMWrfrRE2V08FWWMuMem2hUiEVC3NhUH5eXrtjIgIlaq3BZLax - jmM1rtq6+obGZm/LaQxvBFq8zY0N9XW1rppjDtZmNhm4SZGKUg0K5wQ2lxyU6E1me6Xz - uNtzqqnlTNu59g4fhkcCHe3n2s60NJ3yuI87K+1mE7e+5BIYlD3LC1ZXdq5ImqfS6I3m - Cke1q67Re/a873JnV/c1v/86hicCfv+17q7Oy77zZ72Nda5qR4XZqNeo8rhB2bO80tLh - ORjGpFDHlNkqq931za3tFzu7/TcDt4O9d0IYngjc6Q3eDtz0d3debG9trndXV9rKGF0h - DAo8D/98ULjVBddEpTGUWFmnq97b5rty9UYgGLr/IPwwguGNwMPwg/uhYODG1Su+Nm+9 - y8laSwwaFXdRYHn9+FI+6USWr9Yx5RWO43WgpMsf6L0XjvQ9eRp9juGNQPTpk75I+F5v - wN8FUuqOOyrKGZ06X5bSSbZALFcW6U1W9pi7sdXXdb0nFI48ifYPxIbiGN4IDMUG+qNP - IuFQz/UuX2uj+xhrNemLlHKxIHvPnGRmCyTc6iq1OWo8Le1X/D13w33RF7H4y1ejrzG8 - ERh99TIeexHtC9/t8V9pb/HUOGyl3PKSCODI/7S7MnOE0jx1MVNud548dfbi1VuhcN/z - wfjI2PjE1NQ0hicCU1MT42Mj8cHnfeHQrasXz5466bSXM8XqPKkwJ4UTkRTOidHCVtU2 - ne+8EQQlseHR8cmZ2fm3CxieCLydn52ZHB8djoGU4I3O8021VazFCAdFKkrhBB67lEX7 - Sw5WwurydQfuRZ4NDo9NTM8tLC4tr2B4IrC8tLgwNz0xNjz4LHIv0O2D5VV5sGR/kRIe - vPbMCTwKy5TcOTlyov7MJX8w/Lg/PjoxM/9u+f3q2jqGJwJrq++X383PTIzG+x+Hg/5L - Z+pPHOEOipJ78Pr5noATOTgpsztdDW2dN0ORaGxkfHp+cWVtPbGxieGJwEZifW1lcX56 - fCQWjYRudrY1uJz2MnAiT+lELC/QHIAT72481xW4/6g/PjY5B0oSmx+3MLwR+LiZAClz - k2Px/kf3A13nGt1w5A9oCuTiVHMiVhRoGfOhqtrm9u6eB48HXr6ZWVheTWxubX/awfBE - 4NP21mZidXlh5s3LgccPerrbm2urDpkZbYHiF05UxYyZrfZ4O64Fw09jIxOzi+8/bGxt - 73zG8EZgZ3tr48P7xdmJkdjTcPBah9dTzZqZYtWvncCjMDi54O99GB16NTm/tJr4CEq+ - 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 - YTAoIEUgkkgV+apCjU5vYIwlpWXlZrPFYsXwRMBiMZvLy0pLjIxBr9MUqvIVUokInoMz - 91x47rkYBgW2F0yKWCrPU6rUGm0xeDEcYIwYXgkwBwzgo1irUauUeXKpGKaE21wpxuS7 - FFhfQhgVeV4+aCks0oCZYh2GRwLFYENTVAhC8sGIRCSExfUrJfvSkpMChz5pRaZQgBdl - QYFKpcbwSEClKihQgg+FQpY0Auc9qeSnFyffX9InpWRmwaiAFZFYIpXK5HIFhncCcrlM - KpWIRTAj3JDALUlPS60EthdMCnfpubOSKxCCGJFYLJZgeCUASAGsUCgAITAjnJFfK+EO - /a4V0AJeQEwyAgyPBL5DzeF8ZGX+1kjy8Yuzkp6RkZHJicH8MQKgI4MbEeKMfL8q3LAk - vXBfD4F/ieGVwC7X5J+A+r/Y/85f4Osxf5DA33GAX4MEkAASQAJIAAkgASTwJwn8G4fx - fKoKZW5kc3RyZWFtCmVuZG9iago0MSAwIG9iagozNTkyCmVuZG9iago0MyAwIG9iago8 - PCAvTGVuZ3RoIDQ0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dp - ZHRoIDI4NCAvSGVpZ2h0IDEyNCAvQ29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1Bl - ckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae2dB1fb - yhaFk9zQe++9mGaDqcY0Gwi993ohvOT9/5/w9j4zI8uyIahkrWfQXivSaDQj4U9nmnRm - 8uVLqJBASCAkEBIICYQEQgIhgZBASOAjE/gq+si/0NNvU1gyW08X+ZCZNJNvSvroQ/5S - tz9KWBDLP0oMSpzbC3289MSgwHxXIiFF5+P9WJe/SNAAx/fvRVrfvxs6Li/10ZITDcmA - S3GJqLi4qIh4aDsf7de6+z2CBjZTDDKlZVBpaUkJ8IDOZ4fDuoZGU1xSWlpWXgGVl4MP - 8BTRdD615dBsFBqQqayiKsEHdEI4X7+yrikuETLVNaJq4FF0PrXlKLMRNFXVNXV19VBd - bU2N0IHpfGY4ZKOspqqmtq6hsQlqbCCeqsryss8NR2obtE8VQFPf2NzSCrW0AE9dbXVV - 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 - PjeNPL9ekclzIowKCYQEQgIhgZBASCAkEBIICYQE3k3gf/68w4kKZW5kc3RyZWFtCmVu - ZG9iago0NCAwIG9iagozODE5CmVuZG9iago1OCAwIG9iago8PCAvTGVuZ3RoIDU5IDAg - UiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDEyNCAvSGVpZ2h0 - IDIzNCAvQ29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1BlckNvbXBvbmVudCA4IC9G - aWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae2daVsiRxeGMzPquC8oqCgiAoIg - myAiICKDOyruOq7jmPz/n/A+51R106ANDfRo3lzUhyQi4fYsdaq6loe//uq0jgc6Hvh3 - eOCLwWb2XyuxX7l9e7eJ38k3msenD8RHM7JLtO6qJl/kN+CN9H5z6IKMz+3qImAPte81 - jV+k33Z14Y2m0dlmAWZmb29v3zsNL+Pv6ekReKa3bTqhmQxbCdrfP0BtsKrxS/39+DX+ - gB42nhzfJlyiu5kM7ODQ0NDw8MibNjyMXwwODPRLOhzfLryC7u0jMKijY2NjFotlXNPw - I14cHRkZJjzoML19ODu8u/v7977+AYBHx8CcsFqtNpttUm34AS9NjI9bwB8egu3fv3d3 - M7wNr4tYdyPQA4PDIwBPWG2TU9PTdrt9RtPw4/T01KQNfMvYyPAgmS4tbx1O7C6JHrWM - W8G1z8w6HHPOmjbncMzO2MG3jltGVThC3gab0D3f++DvUSLbZ4B1uRbcbk9Vc7sXXC7n - nGPGTvRR+L2PY/61dTabDXT/4PDYuHXKPguw2+Nd9Pn9S1XN7/ctej1u4GftU9bxseFB - xLyni3K9VcO/fGGPAw2rp2cc8wueRd9SILgcCkeqWji0HAws+RY9C/OOmWmyfLCfQ94G - Gy5njwM9Net0eXxLwVAkGluJJxKrmpZIxFdi0UgouOTzuJyzsHyUvU6Gt2z3129ItP6h - EYt1asa54PWDHIuvJlPr6+l0Rm3p9Pp6Krkaj4Hu9y44Z6aslpEhMrx1tnB53wCCPWl3 - uLxLwUgskUylM9lcbjOvaZu5XDaTTiUTsUhwyety2CcRcqQb+lmrTid2T2//IJk9O+/x - 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 - RubhWpca+W+N27GsjkVHUmF3zs87SXF9GoLrDZfvjFB03qOBY8VzguTnheI9Cc3TkmmD - lUOdjzX2sgrvH8TiH8TvpdK/DRL3tEjOa+SNH3yMwWrfpcB7NV83oPliAVoh/1NoJdtp - fb2fv2ZhFF+zMErfaEAbAw3Wx2sNafpntpw2FojOXy9R+SIJ5ascmv5Qo/+DhDNdfqsG - fYMGuZt3ofRWz4x+ft33Ac7bOaDjaz240TeHCLLuwl3djzT+S16S4e8UEV+i0tMjt/3o - yb7eqoJxhP47QWDb8S0u/DUu+JeZ39SiD+bfCDpcrzRTv6GmAZzWwtCA5CZ+avT/mPh7 - AVT+aeIHdz6q44GOBzoe+H/2wP8ABFmDywplbmRzdHJlYW0KZW5kb2JqCjU5IDAgb2Jq - CjQzODIKZW5kb2JqCjQ2IDAgb2JqCjw8IC9MZW5ndGggNDcgMCBSIC9UeXBlIC9YT2Jq - ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMTI0IC9IZWlnaHQgMjM0IC9Db2xvclNw - YWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE - ZWNvZGUgPj4Kc3RyZWFtCngB7Z15WxrLEodzE09ccF9AQEBANtlEUBAREVTco3HXJHqS - 3Hz/j3B/Vd0zDMgywgB5nkv/4QnK4aWqq6urq7trPnwYtIEGBhoYaODv0sB/0Hr/jYiq - bT37BgL6UW3idS/wTPr48ZOmffzIv+w6HRQGDw0N/cMN/8DXYHyX4YQGirifPw+jff6M - fzIdf+oqnIUmicEdGRlFGxkBH/QhFr2LcBZ6iMGjo2MmbmPgEx6yo9u7Bmc0yTwC8PjE - xCTaxMS4aQx0yN5NOKFJ6JFRk2licmpqegZtempqcsJkYjr03iXJFTRkBnlmdm5uHm1u - bnaG6GOjrPfuwFW0aRzkufkFs2URzWJemJ8DfdzUPbhEQ98TRDZbrDb7EprdZrWYiT4B - vZPFGS95FXrevGizO1yuZbd72eVy2G2L5vnuwRX0mGlyenbBYrU7XW7vig9txet2Oe1W - y8Ls9KRprAuSq+jxyem5hUXI7FnxB0KraKGAf8UD2RcX5qYnx42HV6HNENrt9QfDkWgc - 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 - of426plQJerVSCy+lkiiIrS2JZOJtXgssiqqU6McE3pbFlxrvgRo/Fd0uCgOJosmofa3 - LxAMhcPRaKyqRaPhcCgY8KEmuLYq96f2VQ5jE1UHRTEVVB53uNwer8/nDwSDIU0LBgN+ - n8/rITKqkXM1JgywZnXmGgus/AVsUbEJhYtmUXHdal9yUsV1j7emedyowu5csltRhV0t - S0SlgZRPev9/heBq1SSUuLda7fYlh8PpdLrUhhcOx5LdbkX1eRTJ0VERSc83ITbX70EN - G5TvERX2LYtWq9VW1fCLRYuouo+qROOa+jx6KA3eo4HjYQr0RAN6tMACmlnT6DU/bABP - O+BHLXBBJGi8A5WTa1MkF49ymJycmuZHKsxWNX7IwvQUnvKgPORB1IHqiK3C+XEOY3iE - BR4lwc+SmNI08WwJcE1j8vESRqAVyanTuXASHqEhn6EhnqRR+QmseKoG10MihXcmNVkB - qR0WB7p8YskIGj09RNvod8rTTCDze6vkNLA2hiuli+iRKfQV6jX+k+YhLp1Lzd8IopPw - JD0aPr9uE3/F2/jtDUV5/x/48/gL4MOVbyFo9JN/iR/ybe///Fb/h/zgFv9p9SmDvw80 - MNBAzzTwP8rohZUKZW5kc3RyZWFtCmVuZG9iago0NyAwIG9iago0MzMwCmVuZG9iago1 - NSAwIG9iago8PCAvTGVuZ3RoIDU2IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAv - SW1hZ2UgL1dpZHRoIDM4MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQovRGV2aWNlR3Jh - eSAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVh - bQp4Ae3d21dT97YHcCWBkAu5QEhCLiQkEFgECIFg5CZBMHKLYIQSRRCM0oJBlN0oWxRB - VCqKiJUNSsVLRYZYKg5OdfdUx/nXzlzBPfauBNtJf297fh8cPkwmY3zWZP5W8vLbtYtC - AiRAAiRAAiRAAv9tArspDASQUwO/Me7fEVB2JvBvQgD9U49g0x1+m1AYT/nLAkKhUCDg - n8If+0flN9kTRKLEzYgpaIFPdCJRQgI8P+En/i9M/yf5+PgEYBdLJFKpVCaTJVF2IABw - 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 - T1dHe8Bft7+ixMnZ0vWpKv6oFcSwFybA4Cdr9GYbV+Auq6qtbzrcGuzo7Oo+GQqdoqAE - QqGTPSe6OoJtgeYGn7d8T6Ejy2LQpihlYlFMe1g6Ehh8rcGcyTmLPRXemoMN/ubAkbav - ghS0QHtba6DlUGPdgerKUrcz124x6tT82MdY97BzBPH84KsA35LJ5bvce8urqmt9dfWN - Tf5DFKSAv6mpod53YL+3smxPEdBnmNJSk+WbY//56z2/8IUw+DLA1xnMtmxHgcvtKS2v - rPJW19TU1FJQAkC23+utrCjzlBQ587gsC0+vSIKxh5UTy54ffEmSgp98szUrx5HvdBWX - 7PGUlkHKKQgBXqx0r6fEXVRYkMfZbRYj0CvlUtj2wrgtRy0/9zD4okRpFF9vslgz7Vxu - Xr7T6XK5iihIAUArdBbkObjsLJvFZNBt0sO2jzX2PL4A8MXSJLkqRaPTm8wWqy3Lnp3D - cbm5DgpKIDeX47gcuz3TlmE2GdK0ahUsHHF048RYOdHBh60Dky+TK5PVGq3eYAR/S4bV - RtmBgNWaYTGnmwx6nVadopJv0sPG2brt+Y9asHUE8bB2JDD6yuQU4Nel6eEJGE0UtIDR - aNDr03RaTao6WaWQSyWJMPWwcbaz38RPSIzqK1Qq8Fenpmo0WgpeQAPq6pRklUopT5LB - vhHFf4EeBp/HF/KjD/pSWZJcrlAqVZQdCiiVCrmch5eA/ObQbzP10a0D+FF9WPuJYgk8 - AKlMJkui7EAA4ICPd+fhYeb5fRN72X/6bo0ffdCH4YdjFx5ANGIKWuATnUiUAPDRbfNH - 9PyJy+vHCQSbDwB+jvJXBITC6MTz8F8c+k+zD/xRf/4ZQOAxUHYisMnH//sn3f/lH/0L - gJ+h/EWB/wCl/5IACZAACZAACZDAf43A/wOGt0kcCmVuZHN0cmVhbQplbmRvYmoKNTYg - MCBvYmoKMzQ5NgplbmRvYmoKMzcgMCBvYmoKPDwgL0xlbmd0aCAzOCAwIFIgL1R5cGUg - L1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA0MDQgL0hlaWdodCAxMjQgL0Nv - bG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9G - 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 - b2JqCjUwIDAgb2JqCjMzNTQKZW5kb2JqCjYzIDAgb2JqCjw8IC9MZW5ndGggNjQgMCBS - IC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+ - CnN0cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqU - iAZSpsyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcO - APDFBxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp - 5VeTovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYm - WphpQrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfC - KRCvz9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZp - AO7w/mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMS - RAze50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIp - MFezCrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6h - IlLJUrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWb - e7a9dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxI - JdjgnHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJ - AcneKc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl - 6JLlu59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/Dzm - hUudej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/ - rWIfsq+r/8Y76/cc778MFH1wHuQZ/DqU/9H2E9On3uGrn9U/b45UjLp+Yf3S9TVqTHJs - Yjx9QntiffLOlMU35LfH084zdDMN3wmzuNneuYvzkvMTC1mLhkuwpZpl/Ar/ytfVvB82 - a8xrA+uZP203ODbGNkt/EbbktuHbHTsZu057Ynt7v+Nvhxoks8L0UZygrKeWpinHctNl - MNAzxhzaYsazTLBZsLdwKHDexx3mvsbz66g7by+/okCRIEbIV7hHVFIsVXxBUkeqQHpd - Rk82V25GQUYxUqlFhULVSC1JvVOTXEtT+7zOQ91JPRZ9PYMQwxKjfuMdE35TU7MQ85sW - ry2/naKwFrE5aYu3Szr94EyH/bQjyonbWdHFyhXvFuue71Ht2eE1enbVh8yXFS/op0jQ - 97cmugcEBF4Iuhacdi4vpDi0POzR+Zrw5xEvI19deB3VeLHxUmP0q5iXl+uuPI2tiqu4 - WnbtTnx+QmZiSlJc8oWU4FSf6y43bNKM0zUz5DJFsniymXLIc3Zyl/LGbr7LbymoLSy7 - lVuUcDu82PfO6RLDu4qlgvfYyjBl6+UT9/sevPzrfkXOwyuPAiodHhtUyVbzPKF+slkz - Xtv9tPZZ0fOEF0F19vW6L8UamBt2X0297mp83JTdHNni0qr7RqiNpm25vb+jqjO9K7Db - vEeyF9s79/ZN3+3+SCj64gOYgS8fngzGDzl8lPqE/jQ4fO9z6IjeKPPo5JfKr5FjeuOM - 4yMTdycJU7JTu9+apuNmDL9jv7+dvT5nNk83371wbVF7cXepevnsCudK72rUD9EfH9ei - 1wXXe376b9BvVGzqb078CtvCbhVtS2837pjujO6e3V3bC9uPPwIujFBFWqI80ZFkGZh7 - 5HUUA5Sz1HAaRlp+rBKdKb0zQxBj7KFspnLmZyztrENs39jXOeCcFFxMOC5uXh6hI6JH - xXkl+CT4RQUEj/EJcggxCGOEf4nMin4SaxWvlMiTvCzlI212XEaGWWZDdlCuVj5dgaho - rCSojFIeUalVTVXzUtfQYNNY0WzXKtIO0zHTFTwBP/FR75H+NQMXQ0UjRqN545aThSah - puZmIuZo8y8WTy3TrPCnTljzWG/bDNg+tLt22u2Msj2z/bJDu+Ntp3BnKxdRVzLXUbca - 9yQPd09lL0av2bOvvbN88L5aeFb8gl8TIcvfl6gWwBgwHfg8KDHY8ZxkCDJkILQ0LPS8 - fjh7+FzEi8jEC2eiRKN2L/ZcKogmxKhfpr88fqUqNjbO+qrg1Z1rb+OLE0ISDZO4k9aT - O1OKUkOvm9wQSANpQ+mVGQmZ7lnq2YezN3Le5z7KS77pm29QIFhIUfj9VmfRg9vJxcQ7 - ViUKd7lKUaWz9/rLnpcX3096EPqXW4XZQ7VHYpVcj+mqkFUb1QtPJms+13542v+s93nv - i766gfrhl5MNy6/2GrFNvM3KLTatIW/y2praVzt5u+y783q+vBXqi+h//15uoHCQbij+ - E/Vw5ojwaNtXv3GuiQ9TOdOu32Xn6OZXFz8vd602rzX+rNts2GrfGSbF/yD3kXICWgaA - TCwAp44CYF4EQGwOlOosoVxVCoAJNQAW8gBukw3g9rUA5u4OfucPKIegACWgB+yAD0gC - NXASOAB/EAOyQQVoASNgA8YAE4MZwbxh8bD7sG7YCpwZrgx3hSfAn8C/IKgQ8ghPRCbi - DeIXUgTpjMxC9qDIUOqocFQtah19HB2ErkH/IlMlu0zWhWHCOGEeYLbI9clzyRcoNCiy - KJYo9SiLqWBUjlQN1DzUV6jnaMxp6mgFaNOxSGwg9hudHd1ben36JgZVhjpGZcb6Q+qH - WpmMmT4wOzMvsESw0rAWsh1n62B3Zd89nMshzzHEGc6F42rB+XGzcbfwBB3hOzJ0NIlX - h3eP7zl/qIC8wPaxV4JxQmbCnMLzIvWiqWIe4qoSbBIbkoNSz6QLjsfK+MvayxnJqypI - KQoqHVHmUuFU5VTjURfQENdU1Dqhba1zVjfyRLpehX6HwYwRhbHoSQuTCNNSswELtKW8 - ld+pu9Zjtjg7x9O3z8w4SDpGOHW4HHYluLV64DzDvYa9VXxu4yn9ggnjRIuAN0GqwU9D - ZENrziuGv440ujB8ER+NjMm/ohQ7cjUmXixhNOlGitF1yhs96VmZHtlKuUx5P/NHCjuL - Goqfl7wobS57d3+2AvNI5LF19bWaxmeIF3r1aQ2TjUrNma1r7dadL3qOvL3WvzJgO9jw - 6ejnK6NTYxoTuVPLM5qz1+dHl46tEH48Wd/cVN2K3mkm/X5+x58BHAb8QAqoAxPgBAJA - LMgDlaAdjIMdGCvsOMwcFgC7AauGDcK24TxwPbg/PAfeBF9CcCAMEOcR5YjPSDqkNvI8 - shI5i+JFOaJuoj6h2dB26Jvor2T8ZHiyJxiAMcRkY6bJ5cnjyb9QHKdIpPhGqUFZQLlL - 5UD1ipqPOoF6jcaBpoNWnvYelh2bTIekC6dbp/enX2DwZVhkJDKuH4pgQjOlMB9mLmdR - ZOlidWL9yZbELsDeeNiJA3AUcWpzznJdxynivnGn82jxrB8pO+rIy8Lbz5fCf1IAK9B/ - LFvQSUhYaFP4jUiOKF5MS5xDfFPig2SNVI70heMeMqayynLC8pwK9IoYJaC0pbypsqH6 - S21XA6VJo8Wqzacjo3vixGk9IvRWu2P42ujrSQSUA43MgswLLDott06JWDvaZNh2nyY/ - o20f49DiRO580iXTdcxd1CPCs/ssj/c5nx4o+10hTBJ1AsqCsMEh58ZDTcIawqUiSi9w - RWVfYo7OuMx6pSBO4GpVvGbC+yTvFGTqrRvqaZMZiVny2TO5eTctCrCFvUXpxfYlQnd3 - 7r0rr3iQXBHw6Mxjg2qVmuNPJZ9L1Sm81Hl1qhHfHN9a0TbUSdGt2RvT1/3+6IeYoaXh - syOrXxMn5KbWZ9rnHi8+Xmlb+7kpuX1pP/4IQA4YAA6IA01gDQggHpSCVjADo4ZJw87A - 4qCYT0I73gB+Cf4MvoaQQhARVYgNpAoyFtmPwqEIqEY0C5qA7iATIIsjm8WYYp6S85Kn - UaApwil+UBIol6gCqDapo2mwNLdoZWi7sV50GLpyelP6XwwljFaHyA+9ZAphlmZeYali - DWFTZcewvzt8myOQU5eLg2sN18t9nyfhiO9RU145Pm5+Kv5Nge/HhgXfCrUJN4q8FIXS - tHizRKfkgNSY9LIMQpZFTlxeX8FDMVapTLlHZUPtiLqZRrRmjdaCzjFd9xMlet8NxA3D - jFpOHjLxMK0zZ7Tws+yC4pZs88Pu9OlmewmHAida5yiXH25e7qOe1l5vvQ192vC6fi3+ - OsTWQP2g7nPmIUNhjuenI/wjt6JiLzFEF14Wu1IfZ3x1JJ6QCE9KTxFIfXHDNG0q43wW - NrskVynvbb5Hwc6t67f5i1+UmNyduBdcjrmf/dexitpHOpV9VaerJ2q8axef+T9frsPX - Tzc4vOpv1Gp61MLeevHNWLtaR07nYrd6T1LvQB97v927rPfdH+CDEkNnPkZ/ujvc+nl8 - ZPsL3VeeMYlxpQntSYMpo2/G0wYzut/VZuXmROZxC/SLsMXlpc/LbSuPV2/+iFnzXjf5 - eXyDYxO5Of2rc6tiO2XHb9d4j48U/4Pz0v6ZgkLTz8ePiDPU1Nrv/vcuvj5B0Dlqv9BC - Vyq8s/FJqKWHalNAsLk21EJ5Cwy4e+no/cZTrk5aBhA+DNX1ME9NY6ilgv7t0rsTdcwg - zARhzrNO+iYQpoGwtBve0hzCkCZMgxCoQeKwQNjCLUD7b7tPmKfFqd/8SG8/AxKHpJng - 6qb1ew2wLLyPsSFkJ+mXegXq7Z9TIVwPdKD3IhF4ADcgAgyhvaIFWSb2LX/3rfb7Xn/G - D1giwH3fMxjyDADeYAry8XXwukSE9twB4+CKAy7ADwQBH4gXBIji5eIz4tt/OJrQmA9U - /3+vgxEv4Aox/qfavp00l2+le3CWX6iClWf3XM3cH9WDe3L+s2KDv2eH1oD/Y/0/isAL - +q6wf56GnhRAQ7HLsyehBr5CUvO/SqBbCHTWBkDTjxBK9PLwDMSpQ18T3IRxengXUWGc - pLi4PPgXAsVl3AplbmRzdHJlYW0KZW5kb2JqCjY0IDAgb2JqCjM4MjcKZW5kb2JqCjYw - IDAgb2JqClsgL0lDQ0Jhc2VkIDYzIDAgUiBdCmVuZG9iago2NSAwIG9iago8PCAvTGVu - Z3RoIDY2IDAgUiAvTiAzIC9BbHRlcm5hdGUgL0RldmljZVJHQiAvRmlsdGVyIC9GbGF0 - ZURlY29kZSA+PgpzdHJlYW0KeAGtWHk4VV3bX2d0zMeQMRzKkHnKPI8ZM2co8xiO4xhC - SFGUucyEJIl6lIgGUqbMokSJMkVmIjK9+9DT833X973X+8+7rmvt9Vv3+t2/tfa+z9r3 - WRsA7EMnAsEHDgDwxQcSzXQ1cNY2tjiyIYACtAALRAHMySWAoG5iYghR/k1ZHwAw0lC/ - CEkrxyp1IeyFaeVXk6LyQcGy+X/j9LeZhghNCABMGDIwehxgNRJ2PsAWJHwukBAIcTxJ - 2MXTyRXC4RAWJlqYaUK4FMI0Hge4loSdD/AbEg528SD5DgKApse7euEBIJuDsIqrW4AL - NEya19U1wMUXwikQr8/X1w/Sx0IVCLgQiJAvNgHCR0nPBWqhYgvpSpVDOsn/2IIhreo4 - APj8/7HxQhymaQDu8P5jWzXbf1Ywpp4AdynJfTkYlQYAqJG9vVU+SPM6ADupe3tbRXt7 - O7cBQAwD0ODjEkQM3udCC4R1AfCf+gf3/NsDAQUHCjDMDr6KDEEtknlhxincKMeoT9H0 - Y83o2hm0GV8yKTBXswqxFR5m5UjhwuAiuBePuBx9x6fDX3kMJ5gotC7iKNosLiyRJLko - bXz8nixczk7+oSJSyVK5WGVJTVE9WqNVi0rbWCdRt0MPra9uEGr40GjyJKuJoWmYWZn5 - oCXSSvyUnfUlm3u2vXbrZ9jtlR2cHC863XJucBlx3XJn8ZDyNPbyPHvBO8Pnge9r/Ae/ - WcIOkTaAK1AsSCXY4Jx1iGsoPizkfFR4XERyZNqF7Ki8i/mXCqNvxdy6XHglPzY3Lutq - 2rXk+KsJ0YnhSQHJ3inOqdbXjW9opsmlC2fgMhmzyLI2s7/nDOd25dXffJB/syC+MOyW - R5HlbY1i0TtsJeiS5bufSpvvVZRllUfd93xg8pdsBcdDxMPpR12VlY8zqkKrTz9RreGu - hddOPG16Vvw85oVLnXo9V/3Oy6GGJ69SX3s3ajdxNf1s7mkpaQ1/Y9bG1/arvasjvxPf - pdJN1f2xp6TX/61iH7Kvq//GO+v3HO+/DBR9cB7kGfw6lP/R9hPTp97hq5/VP2+OVIy6 - fmH90vU1akxybGI8fUJ7Yn3yzpTFN+S3x9POM3QzDd8Js7jZ3rmL85LzEwtZi4ZLsKWa - ZfwK/8rX1bwfNmvMawPrmT9tNzg2xjZLfxG25Lbh2x07GbtOe2J7e7/jb4caJLPC9FGc - oKynlqYpx3LTZTDQM8Yc2mLGs0ywWbC3cChw3scd5r7G8+uoO28vv6JAkSBGyFe4R1RS - LFV8QVJHqkB6XUZPNlduRkFGMVKpRYVC1UgtSb1Tk1xLU/u8zkPdST0WfT2DEMMSo37j - HRN+U1OzEPObFq8tv52isBaxOWmLt0s6/eBMh/20I8qJ21nRxcoV7xbrnu9R7dnhNXp2 - 1YfMlxUv6KdI0Pe3JroHBAReCLoWnHYuL6Q4tDzs0fma8OcRLyNfXXgd1Xix8VJj9KuY - l5frrjyNrYqruFp27U58fkJmYkpSXPKFlOBUn+suN2zSjNM1M+QyRbJ4splyyHN2cpfy - xm6+y28pqC0su5VblHA7vNj3zukSw7uKpYL32MowZevlE/f7Hrz8635FzsMrjwIqHR4b - VMlW8zyhfrJZM17b/bT2WdHzhBdBdfb1ui/FGpgbdl9Nve5qfNyU3RzZ4tKq+0aojaZt - ub2/o6ozvSuw27xHshfbO/f2Td/t/kgo+uIDmIEvH54Mxg85fJT6hP40OHzvc+iI3ijz - 6OSXyq+RY3rjjOMjE3cnCVOyU7vfmqbjZgy/Y7+/nb0+ZzZPN9+9cG1Re3F3qXr57Arn - Su9q1A/RHx/XotcF13t++m/Qb1Rs6m9O/Arbwm4VbUtvN+6Y7ozunt1d2wvbjz8CLoxQ - RVqiPNGRZBmYe+R1FAOUs9RwGkZafqwSnSm9M0MQY+yhbKZy5mcs7axDbN/Y1zngnBRc - TDgubl4eoSOiR8V5Jfgk+EUFBI/xCXIIMQhjhH+JzIp+EmsVr5TIk7ws5SNtdlxGhllm - Q3ZQrlY+XYGoaKwkqIxSHlGpVU1V81LX0GDTWNFs1yrSDtMx0xU8AT/xUe+R/jUDF0NF - I0ajeeOWk4UmoabmZiLmaPMvFk8t06zwp05Y81hv2wzYPrS7dtrtjLI9s/2yQ7vjbadw - ZysXUVcy11G3GvckD3dPZS9Gr9mzr72zfPC+WnhW/IJfEyHL35eoFsAYMB34PCgx2PGc - ZAgyZCC0NCz0vH44e/hcxIvIxAtnokSjdi/2XCqIJsSoX6a/PH6lKjY2zvqq4NWda2/j - ixNCEg2TuJPWkztTilJDr5vcEEgDaUPplRkJme5Z6tmHszdy3uc+yku+6ZtvUCBYSFH4 - /VZn0YPbycXEO1YlCne5SlGls/f6y56XF99PehD6l1uF2UO1R2KVXI/pqpBVG9ULTyZr - Ptd+eNr/rPd574u+uoH64ZeTDcuv9hqxTbzNyi02rSFv8tqa2lc7ebvsu/N6vrwV6ovo - f/9ebqBwkG4o/hP1cOaI8GjbV79xrokPUznTrt9l5+jmVxc/L3etNq81/qzbbNhq3xkm - xf8g95FyAloGgEwsAKeOAmBeBEBsDpTqLKFcVQqACTUAFvIAbpMN4Pa1AObuDn7nDyiH - oAAloAfsgA9IAjVwEjgAfxADskEFaAEjYAPGABODGcG8YfGw+7Bu2AqcGa4Md4UnwJ/A - vyCoEPIIT0Qm4g3iF1IE6YzMQvagyFDqqHBULWodfRwdhK5B/yJTJbtM1oVhwjhhHmC2 - yPXJc8kXKDQosiiWKPUoi6lgVI5UDdQ81Feo52jMaepoBWjTsUhsIPYbnR3dW3p9+iYG - VYY6RmXG+kPqh1qZjJk+MDszL7BEsNKwFrIdZ+tgd2XfPZzLIc8xxBnOheNqwflxs3G3 - 8AQd4TsydDSJV4d3j+85f6iAvMD2sVeCcUJmwpzC8yL1oqliHuKqEmwSG5KDUs+kC47H - yvjL2ssZyasqSCkKKh1R5lLhVOVU41EX0BDXVNQ6oW2tc1Y38kS6XoV+h8GMEYWx6EkL - kwjTUrMBC7SlvJXfqbvWY7Y4O8fTt8/MOEg6Rjh1uBx2Jbi1euA8w72GvVV8buMp/YIJ - 40SLgDdBqsFPQ2RDa84rhr+ONLowfBEfjYzJv6IUO3I1Jl4sYTTpRorRdcobPelZmR7Z - SrlMeT/zRwo7ixqKn5e8KG0ue3d/tgLzSOSxdfW1msZniBd69WkNk41KzZmta+3WnS96 - jry91r8yYDvY8Ono5yujU2MaE7lTyzOas9fnR5eOrRB+PFnf3FTdit5pJv1+fsefARwG - /EAKqAMT4AQCQCzIA5WgHYyDHRgr7DjMHBYAuwGrhg3CtuE8cD24PzwH3gRfQnAgDBDn - EeWIz0g6pDbyPLISOYviRTmibqI+odnQduib6K9k/GR4sicYgDHEZGOmyeXJ48m/UByn - SKT4RqlBWUC5S+VA9YqajzqBeo3GgaaDVp72HpYdm0yHpAunW6f3p19g8GVYZCQyrh+K - YEIzpTAfZi5nUWTpYnVi/cmWxC7A3njYiQNwFHFqc85yXccp4r5xp/No8awfKTvqyMvC - 28+Xwn9SACvQfyxb0ElIWGhT+I1IjiheTEucQ3xT4oNkjVSO9IXjHjKmsspywvKcCvSK - GCWgtKW8qbKh+kttVwOlSaPFqs2nI6N74sRpPSL0Vrtj+Nro60kElAONzILMCyw6LbdO - iVg72mTYdp8mP6NtH+PQ4kTufNIl03XMXdQjwrP7LI/3OZ8eKPtdIUwSdQLKgrDBIefG - Q03CGsKlIkovcEVlX2KOzrjMeqUgTuBqVbxmwvsk7xRk6q0b6mmTGYlZ8tkzuXk3LQqw - hb1F6cX2JUJ3d+69K694kFwR8OjMY4NqlZrjTyWfS9UpvNR5daoR3xzfWtE21EnRrdkb - 09f9/uiHmKGl4bMjq18TJ+Sm1mfa5x4vPl5pW/u5Kbl9aT/+CEAOGAAOiANNYA0IIB6U - glYwA6OGScPOwOKgmE9CO94Afgn+DL6GkEIQEVWIDaQKMhbZj8KhCKhGNAuagO4gEyCL - I5vFmGKekvOSp1GgKcIpflASKJeoAqg2qaNpsDS3aGVou7FedBi6cnpT+l8MJYxWh8gP - vWQKYZZmXmGpYg1hU2XHsL87fJsjkFOXi4NrDdfLfZ8n4YjvUVNeOT5ufir+TYHvx4YF - 3wq1CTeKvBSF0rR4s0Sn5IDUmPSyDEKWRU5cXl/BQzFWqUy5R2VD7Yi6mUa0Zo3Wgs4x - XfcTJXrfDcQNw4xaTh4y8TCtM2e08LPsguKWbPPD7vTpZnsJhwInWucolx9uXu6jntZe - b70Nfdrwun4t/jrE1kD9oO5z5iFDYY7npyP8I7eiYi8xRBdeFrtSH2d8dSSekAhPSk8R - SH1xwzRtKuN8Fja7JFcp722+R8HOreu3+YtflJjcnbgXXI65n/3XsYraRzqVfVWnqydq - vGsXn/k/X67D1083OLzqb9RqetTC3nrxzVi7WkdO52K3ek9S70Afe7/du6z33R/ggxJD - Zz5Gf7o73Pp5fGT7C91XnjGJcaUJ7UmDKaNvxtMGM7rf1Wbl5kTmcQv0i7DF5aXPy20r - j1dv/ohZ8143+Xl8g2MTuTn9q3OrYjtlx2/XeI+PFP+D89L+mYJC08/Hj4gz1NTa7/73 - Lr4+QdA5ar/QQlcqvLPxSailh2pTQLC5NtRCeQsMuHvp6P3GU65OWgYQPgzV9TBPTWOo - pYL+7dK7E3XMIMwEYc6zTvomEKaBsLQb3tIcwpAmTIMQqEHisEDYwi1A+2+7T5inxanf - /EhvPwMSh6SZ4Oqm9XsNsCy8j7EhZCfpl3oF6u2fUyFcD3Sg9yIReAA3IAIMob2iBVkm - 9i1/9632+15/xg9YIsB93zMY8gwA3mAK8vF18LpEhPbcAePgigMuwA8EAR+IFwSI4uXi - M+Lbfzia0JgPVP9/r4MRL+AKMf6n2r6dNJdvpXtwll+ogpVn91zN3B/Vg3ty/rNig79n - h9aA/2P9P4rAC/qusH+ehp4UQEOxy7MnoQa+QlLzv0qgWwh01gZA048QSvTy8AzEqUNf - E9yEcXp4F1FhnKS4uDz4FwLFZdwKZW5kc3RyZWFtCmVuZG9iago2NiAwIG9iagozODI3 - CmVuZG9iagozNiAwIG9iagpbIC9JQ0NCYXNlZCA2NSAwIFIgXQplbmRvYmoKNjcgMCBv - YmoKPDwgL0xlbmd0aCA2OCAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VSR0IgL0Zp - bHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVh5OFVd219ndMzHkDEcypB5yjyP - GTNnKPMYjuMYQkhRlLnMhCSJepSIBlKmzKJEiTJFZiIyvfvQ0/N91/e91/vPu65r7fVb - 9/rdv7X2vs/a91kbAOxDJwLBBw4A8MUHEs10NXDWNrY4siGAArQAC0QBzMklgKBuYmII - Uf5NWR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHBsvl/4/S3mYYITQgATBgyMHocYDUSdj7A - FiR8LpAQCHE8SdjF08kVwuEQFiZamGlCuBTCNB4HuJaEnQ/wGxIOdvEg+Q4CgKbHu3rh - ASCbg7CKq1uACzRMmtfVNcDFF8IpEK/P19cP0sdCFQi4EIiQLzYBwkdJzwVqoWIL6UqV - QzrJ/9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1239WMKaeAHcpyX05GJUGAKiRvb1VPkjz - OgA7qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguEdQHwn/oH9/zbAwEFBwowzA6+igxBLZJ5 - YcYp3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUeZuVI4cLgIrgXj7gcfcenw195DCeYKLQu - 4ijaLC4skSS5KG18/J4sXM5O/qEiUslSuVhlSU1RPVqjVYtK21gnUbdDD62vbhBq+NBo - 8iSriaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY7ZUdnBwvOt1ybnAZcd1yZ/GQ8jT28jx7 - wTvD54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhrKD4s5HxUeFxEcmTaheyovIv5lwqjb8Xc - ulx4JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141vaKbJpQtn4DIZs8iyNrO/5wznduXV - 33yQf7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WUZZVH3fd8YPKXbAXHQ8TD6UddlZWP - M6pCq08/Ua3hroXXTjxtelb8POaFS516PVf9zsuhhievUl97N2o3cTX9bO5pKWkNf2PW - xtf2q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr9xzvvwwUfXAe5Bn8OpT/0fYT06fe - 4auf1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6UxTfkt8fTzjN0Mw3fCbO42d65i/OS - 8xMLWYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4NsY2S38RtuS24dsdOxm7Tntie3u/ - 42+HGiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNMsFmwt3AocN7HHea+xvPrqDtvL7+i - QJEgRshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9 - Fn09gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W - 657vUe3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6Fpx2Li+kOLQ87NH5mvDnES8jX114 - HdV4sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWy - eLKZcshzdnKX8sZuvstvKagtLLuVW5RwO7zY987pEsO7iqWC99jKMGXr5RP3+x68/Ot+ - Rc7DK48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QXQXX29bovxRqYG3ZfTb3uanzclN0c - 2eLSqvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+ - NDh873PoiN4o8+jkl8qvkWN644zjIxN3JwlTslO735qm42YMv2O/v529Pmc2TzffvXBt - UXtxd6l6+ewK50rvatQP0R8f16LXBdd7fvpv0G9UbOpvTvwK28JuFW1LbzfumO6M7p7d - XdsL248/Ai6MUEVaojzRkWQZmHvkdRQDlLPUcBpGWn6sEp0pvTNDEGPsoWymcuZnLO2s - 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 - uplGtGaN1oLOMV33EyV63w3EDcOMWk4eMvEwrTNntPCz7ILilmzzw+706WZ7CYcCJ1rn - KJcfbl7uo57WXm+9DX3a8Lp+Lf46xNZA/aDuc+YhQ2GO56cj/CO3omIvMUQXXha7Uh9n - fHUknpAIT0pPEUh9ccM0bSrjfBY2uyRXKe9tvkfBzq3rt/mLX5SY3J24F1yOuZ/917GK - 2kc6lX1Vp6snarxrF5/5P1+uw9dPNzi86m/UanrUwt568c1Yu1pHTudit3pPUu9AH3u/ - 3bus990f4IMSQ2c+Rn+6O9z6eXxk+wvdV54xiXGlCe1Jgymjb8bTBjO639Vm5eZE5nEL - 9IuwxeWlz8ttK49Xb/6IWfNeN/l5fINjE7k5/atzq2I7Zcdv13iPjxT/g/PS/pmCQtPP - x4+IM9TU2u/+9y6+PkHQOWq/0EJXKryz8UmopYdqU0CwuTbUQnkLDLh76ej9xlOuTloG - ED4M1fUwT01jqKWC/u3SuxN1zCDMBGHOs076JhCmgbC0G97SHMKQJkyDEKhB4rBA2MIt - QPtvu0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAsvI+xIWQn6Zd6Bertn1MhXA90oPciEXgA - NyACDKG9ogVZJvYtf/et9vtef8YPWCLAfd8zGPIMAN5gCvLxdfC6RIT23AHj4IoDLsAP - BAEfiBcEiOLl4jPi2384mtCYD1T/f6+DES/gCjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf - 1YN7cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noaeFEBDscuzJ6EGvkJS879KoFsIdNYGQNOP - EEr08vAMxKlDXxPchHF6eBdRYZykuLg8+BcCxWXcCmVuZHN0cmVhbQplbmRvYmoKNjgg - MCBvYmoKMzgyNwplbmRvYmoKNDUgMCBvYmoKWyAvSUNDQmFzZWQgNjcgMCBSIF0KZW5k - b2JqCjY5IDAgb2JqCjw8IC9MZW5ndGggNzAgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2 - aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa1YeThVXdtfZ3TM - x5AxHMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqUiAZSpsyiRIkyRWYiMr370NPzfdf3 - vdf7z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcOAPDFBxLNdDVw1ja2OLIhgAK0AAtE - AczJJYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp5VeTovJBwbL5f+P0t5mGCE0IAEwY - MjB6HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYmWphpQrgUwjQeB7iWhJ0P8BsSDnbx - IPkOAoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfCKRCvz9fXD9LHQhUIuBCIkC82AcJH - Sc8FaqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZpAO7w/mNbNdt/VjCmngB3Kcl9ORiV - BgCokb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMSRAze50ILhHUB8J/6B/f82wMBBQcK - MMwOvooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIpMFezCrEVHmblSOHC4CK4F4+4HH3H - p8NfeQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6hIlLJUrlYZUlNUT1ao1WLSttYJ1G3 - Qw+tr24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWbe7a9dutn2O2VHZwcLzrdcm5wGXHd - cmfxkPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxIJdjgnHWIayg+LOR8VHhcRHJk2oXs - qLyL+ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJAcneKc6p1teNb2imyaULZ+AyGbPI - sjazv+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl6JLlu59Km+9VlGWVR933fGDyl2wF - x0PEw+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/DzmhUudej1X/c7LoYYnr1JfezdqN3E1 - /WzuaSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/rWIfsq+r/8Y76/cc778MFH1wHuQZ - /DqU/9H2E9On3uGrn9U/b45UjLp+Yf3S9TVqTHJsYjx9QntiffLOlMU35LfH084zdDMN - 3wmzuNneuYvzkvMTC1mLhkuwpZpl/Ar/ytfVvB82a8xrA+uZP203ODbGNkt/EbbktuHb - HTsZu057Ynt7v+Nvhxoks8L0UZygrKeWpinHctNlMNAzxhzaYsazTLBZsLdwKHDexx3m - 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 - e+no/cZTrk5aBhA+DNX1ME9NY6ilgv7t0rsTdcwgzARhzrNO+iYQpoGwtBve0hzCkCZM - gxCoQeKwQNjCLUD7b7tPmKfFqd/8SG8/AxKHpJng6qb1ew2wLLyPsSFkJ+mXegXq7Z9T - IVwPdKD3IhF4ADcgAgyhvaIFWSb2LX/3rfb7Xn/GD1giwH3fMxjyDADeYAry8XXwukSE - 9twB4+CKAy7ADwQBH4gXBIji5eIz4tt/OJrQmA9U/3+vgxEv4Aox/qfavp00l2+le3CW - X6iClWf3XM3cH9WDe3L+s2KDv2eH1oD/Y/0/isAL+q6wf56GnhRAQ7HLsyehBr5CUvO/ - SqBbCHTWBkDTjxBK9PLwDMSpQ18T3IRxengXUWGcpLi4PPgXAsVl3AplbmRzdHJlYW0K - ZW5kb2JqCjcwIDAgb2JqCjM4MjcKZW5kb2JqCjU3IDAgb2JqClsgL0lDQ0Jhc2VkIDY5 - IDAgUiBdCmVuZG9iago3MSAwIG9iago8PCAvTGVuZ3RoIDcyIDAgUiAvTiAxIC9BbHRl - cm5hdGUgL0RldmljZUdyYXkgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB - hVJPSBRRHP7NNhKEiEGFeIh3CgmVKaysoNp2dVmVbVuV0qIYZ9+6o7Mz05vZNcWTBF2i - PHUPomN07NChm5eiwKxL1yCpIAg8dej7zezqKIRveTvf+/39ft97RG2dpu87KUFUc0OV - K6Wnbk5Ni4MfKUUd1E5YphX46WJxjLHruZK/u9fWZ9LYst7HtXb79j21lWVgIeottrcQ - +iGRZgAfmZ8oZYCzwB2Wr9g+ATxYDqwa8COiAw+auTDT0Zx0pbItkVPmoigqr2I7Sa77 - +bnGvou1iYP+XI9m1o69s+qq0UzUtPdEobwPrkQZz19U9mw1FKcN45xIQxop8q7V3ytM - xxGRKxBKBlI1ZLmfak6ddeB1GLtdupPj+PYQpT7JYKiJtemymR2FfQB2KsvsEPAF6PGy - Yg/ngXth/1tRw5PAJ2E/ZId51q0f9heuU+B7hD014M4UrsXx2oofXi0BQ/dUI2iMc03E - 09c5c6SI7zHUGZj3RjmmCzF3lqoTN4A7YR9ZqmYKsV37ruol7nsCd9PjO9GbOQtcoBxJ - crEV2RTQPAlYFH2LsEkOPD7OHlXgd6iYwBy5idzNKPce1REbZ6NSgVZ6jVfGT+O58cX4 - ZWwYz4B+rHbXe3z/6eMVdde2Pjz5jXrcOa69nRtVYVZxZQvd/8cyhI/ZJzmmwdOhWVhr - 2HbkD5rMTLAMKMR/BT6X+pITVdzV7u24RRLMUD4sbCW6S1RuKdTqPYNKrBwr2AB2cJLE - LFocuFNrujl4d9giem35TVey64b++vZ6+9ryHm3KqCkoE82zRGaUsVuj5N142/1mkRGf - ODq+572KWsn+SUUQP4U5WiryFFX0VlDWxG9nDn4btn5cP6Xn9UH9PAk9rZ/Rr+ijEb4M - dEnPwnNRH6NJ8LBpIeISoIqDM9ROVGONA+Ip8fK0W2SR/Q9AGf1mCmVuZHN0cmVhbQpl - bmRvYmoKNzIgMCBvYmoKNzA0CmVuZG9iagoyNiAwIG9iagpbIC9JQ0NCYXNlZCA3MSAw - IFIgXQplbmRvYmoKNzMgMCBvYmoKPDwgL0xlbmd0aCA3NCAwIFIgL04gMyAvQWx0ZXJu - YXRlIC9EZXZpY2VSR0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVh5 - OFVd219ndMzHkDEcypB5yjyPGTNnKPMYjuMYQkhRlLnMhCSJepSIBlKmzKJEiTJFZiIy - vfvQ0/N91/e91/vPu65r7fVb9/rdv7X2vs/a91kbAOxDJwLBBw4A8MUHEs10NXDWNrY4 - siGAArQAC0QBzMklgKBuYmIIUf5NWR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHBsvl/4/S3 - mYYITQgATBgyMHocYDUSdj7AFiR8LpAQCHE8SdjF08kVwuEQFiZamGlCuBTCNB4HuJaE - nQ/wGxIOdvEg+Q4CgKbHu3rhASCbg7CKq1uACzRMmtfVNcDFF8IpEK/P19cP0sdCFQi4 - EIiQLzYBwkdJzwVqoWIL6UqVQzrJ/9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1239WMKae - AHcpyX05GJUGAKiRvb1VPkjzOgA7qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguEdQHwn/oH - 9/zbAwEFBwowzA6+igxBLZJ5YcYp3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUeZuVI4cLg - IrgXj7gcfcenw195DCeYKLQu4ijaLC4skSS5KG18/J4sXM5O/qEiUslSuVhlSU1RPVqj - VYtK21gnUbdDD62vbhBq+NBo8iSriaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY7ZUdnBwv - Ot1ybnAZcd1yZ/GQ8jT28jx7wTvD54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhrKD4s5HxU - eFxEcmTaheyovIv5lwqjb8Xculx4JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141vaKbJ - pQtn4DIZs8iyNrO/5wznduXV33yQf7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WUZZVH - 3fd8YPKXbAXHQ8TD6UddlZWPM6pCq08/Ua3hroXXTjxtelb8POaFS516PVf9zsuhhiev - Ul97N2o3cTX9bO5pKWkNf2PWxtf2q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr9xzv - vwwUfXAe5Bn8OpT/0fYT06fe4auf1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6UxTfk - t8fTzjN0Mw3fCbO42d65i/OS8xMLWYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4NsY2 - S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNMsFmw - t3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFSqUWF - QtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlpi7dL - Ov3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6Fpx2 - Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJKUlzy - hZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZuvstvKagtLLuVW5RwO7zY987pEsO7 - iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QXQXX2 - 9bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f7f5I - 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 - QmFzZWQgNzMgMCBSIF0KZW5kb2JqCjc1IDAgb2JqCjw8IC9MZW5ndGggNzYgMCBSIC9O - IDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 - cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqUiAZS - psyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcOAPDF - BxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp5VeT - ovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYmWphp - QrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfCKRCv - z9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZpAO7w - /mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMSRAze - 50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIpMFez - CrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6hIlLJ - UrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWbe7a9 - dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxIJdjg - nHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJAcne - Kc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl6JLl - u59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/DzmhUud - ej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/rWIf - 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 - vOpv1Gp61MLeevHNWLtaR07nYrd6T1LvQB97v927rPfdH+CDEkNnPkZ/ujvc+nl8ZPsL - 3VeeMYlxpQntSYMpo2/G0wYzut/VZuXmROZxC/SLsMXlpc/LbSuPV2/+iFnzXjf5eXyD - YxO5Of2rc6tiO2XHb9d4j48U/4Pz0v6ZgkLTz8ePiDPU1Nrv/vcuvj5B0Dlqv9BCVyq8 - s/FJqKWHalNAsLk21EJ5Cwy4e+no/cZTrk5aBhA+DNX1ME9NY6ilgv7t0rsTdcwgzARh - zrNO+iYQpoGwtBve0hzCkCZMgxCoQeKwQNjCLUD7b7tPmKfFqd/8SG8/AxKHpJng6qb1 - ew2wLLyPsSFkJ+mXegXq7Z9TIVwPdKD3IhF4ADcgAgyhvaIFWSb2LX/3rfb7Xn/GD1gi - wH3fMxjyDADeYAry8XXwukSE9twB4+CKAy7ADwQBH4gXBIji5eIz4tt/OJrQmA9U/3+v - gxEv4Aox/qfavp00l2+le3CWX6iClWf3XM3cH9WDe3L+s2KDv2eH1oD/Y/0/isAL+q6w - f56GnhRAQ7HLsyehBr5CUvO/SqBbCHTWBkDTjxBK9PLwDMSpQ18T3IRxengXUWGcpLi4 - PPgXAsVl3AplbmRzdHJlYW0KZW5kb2JqCjc2IDAgb2JqCjM4MjcKZW5kb2JqCjQyIDAg - b2JqClsgL0lDQ0Jhc2VkIDc1IDAgUiBdCmVuZG9iago3NyAwIG9iago8PCAvTGVuZ3Ro - IDc4IDAgUiAvTiAzIC9BbHRlcm5hdGUgL0RldmljZVJHQiAvRmlsdGVyIC9GbGF0ZURl - Y29kZSA+PgpzdHJlYW0KeAGtWHk4VV3bX2d0zMeQMRzKkHnKPI8ZM2co8xiO4xhCSFGU - ucyEJIl6lIgGUqbMokSJMkVmIjK9+9DT833X973X+8+7rmvt9Vv3+t2/tfa+z9r3WRsA - 7EMnAsEHDgDwxQcSzXQ1cNY2tjiyIYACtAALRAHMySWAoG5iYghR/k1ZHwAw0lC/CEkr - xyp1IeyFaeVXk6LyQcGy+X/j9LeZhghNCABMGDIwehxgNRJ2PsAWJHwukBAIcTxJ2MXT - yRXC4RAWJlqYaUK4FMI0Hge4loSdD/AbEg528SD5DgKApse7euEBIJuDsIqrW4ALNEya - 19U1wMUXwikQr8/X1w/Sx0IVCLgQiJAvNgHCR0nPBWqhYgvpSpVDOsn/2IIhreo4APj8 - /7HxQhymaQDu8P5jWzXbf1Ywpp4AdynJfTkYlQYAqJG9vVU+SPM6ADupe3tbRXt7O7cB - QAwD0ODjEkQM3udCC4R1AfCf+gf3/NsDAQUHCjDMDr6KDEEtknlhxincKMeoT9H0Y83o - 2hm0GV8yKTBXswqxFR5m5UjhwuAiuBePuBx9x6fDX3kMJ5gotC7iKNosLiyRJLkobXz8 - nixczk7+oSJSyVK5WGVJTVE9WqNVi0rbWCdRt0MPra9uEGr40GjyJKuJoWmYWZn5oCXS - SvyUnfUlm3u2vXbrZ9jtlR2cHC863XJucBlx3XJn8ZDyNPbyPHvBO8Pnge9r/Ae/WcIO - kTaAK1AsSCXY4Jx1iGsoPizkfFR4XERyZNqF7Ki8i/mXCqNvxdy6XHglPzY3Lutq2rXk - +KsJ0YnhSQHJ3inOqdbXjW9opsmlC2fgMhmzyLI2s7/nDOd25dXffJB/syC+MOyWR5Hl - bY1i0TtsJeiS5bufSpvvVZRllUfd93xg8pdsBcdDxMPpR12VlY8zqkKrTz9RreGuhddO - PG16Vvw85oVLnXo9V/3Oy6GGJ69SX3s3ajdxNf1s7mkpaQ1/Y9bG1/arvasjvxPfpdJN - 1f2xp6TX/61iH7Kvq//GO+v3HO+/DBR9cB7kGfw6lP/R9hPTp97hq5/VP2+OVIy6fmH9 - 0vU1akxybGI8fUJ7Yn3yzpTFN+S3x9POM3QzDd8Js7jZ3rmL85LzEwtZi4ZLsKWaZfwK - /8rX1bwfNmvMawPrmT9tNzg2xjZLfxG25Lbh2x07GbtOe2J7e7/jb4caJLPC9FGcoKyn - lqYpx3LTZTDQM8Yc2mLGs0ywWbC3cChw3scd5r7G8+uoO28vv6JAkSBGyFe4R1RSLFV8 - QVJHqkB6XUZPNlduRkFGMVKpRYVC1UgtSb1Tk1xLU/u8zkPdST0WfT2DEMMSo37jHRN+ - U1OzEPObFq8tv52isBaxOWmLt0s6/eBMh/20I8qJ21nRxcoV7xbrnu9R7dnhNXp21YfM - lxUv6KdI0Pe3JroHBAReCLoWnHYuL6Q4tDzs0fma8OcRLyNfXXgd1Xix8VJj9KuYl5fr - rjyNrYqruFp27U58fkJmYkpSXPKFlOBUn+suN2zSjNM1M+QyRbJ4splyyHN2cpfyxm6+ - y28pqC0su5VblHA7vNj3zukSw7uKpYL32MowZevlE/f7Hrz8635FzsMrjwIqHR4bVMlW - 8zyhfrJZM17b/bT2WdHzhBdBdfb1ui/FGpgbdl9Nve5qfNyU3RzZ4tKq+0aojaZtub2/ - o6ozvSuw27xHshfbO/f2Td/t/kgo+uIDmIEvH54Mxg85fJT6hP40OHzvc+iI3ijz6OSX - yq+RY3rjjOMjE3cnCVOyU7vfmqbjZgy/Y7+/nb0+ZzZPN9+9cG1Re3F3qXr57ArnSu9q - 1A/RHx/XotcF13t++m/Qb1Rs6m9O/Arbwm4VbUtvN+6Y7ozunt1d2wvbjz8CLoxQRVqi - PNGRZBmYe+R1FAOUs9RwGkZafqwSnSm9M0MQY+yhbKZy5mcs7axDbN/Y1zngnBRcTDgu - bl4eoSOiR8V5Jfgk+EUFBI/xCXIIMQhjhH+JzIp+EmsVr5TIk7ws5SNtdlxGhllmQ3ZQ - rlY+XYGoaKwkqIxSHlGpVU1V81LX0GDTWNFs1yrSDtMx0xU8AT/xUe+R/jUDF0NFI0aj - eeOWk4UmoabmZiLmaPMvFk8t06zwp05Y81hv2wzYPrS7dtrtjLI9s/2yQ7vjbadwZysX - UVcy11G3GvckD3dPZS9Gr9mzr72zfPC+WnhW/IJfEyHL35eoFsAYMB34PCgx2PGcZAgy - ZCC0NCz0vH44e/hcxIvIxAtnokSjdi/2XCqIJsSoX6a/PH6lKjY2zvqq4NWda2/jixNC - Eg2TuJPWkztTilJDr5vcEEgDaUPplRkJme5Z6tmHszdy3uc+yku+6ZtvUCBYSFH4/VZn - 0YPbycXEO1YlCne5SlGls/f6y56XF99PehD6l1uF2UO1R2KVXI/pqpBVG9ULTyZrPtd+ - eNr/rPd574u+uoH64ZeTDcuv9hqxTbzNyi02rSFv8tqa2lc7ebvsu/N6vrwV6ovof/9e - bqBwkG4o/hP1cOaI8GjbV79xrokPUznTrt9l5+jmVxc/L3etNq81/qzbbNhq3xkmxf8g - 95FyAloGgEwsAKeOAmBeBEBsDpTqLKFcVQqACTUAFvIAbpMN4Pa1AObuDn7nDyiHoAAl - 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 - JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141vaKbJpQtn4DIZs8iyNrO/5wznduXV33yQ - f7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WUZZVH3fd8YPKXbAXHQ8TD6UddlZWPM6pC - q08/Ua3hroXXTjxtelb8POaFS516PVf9zsuhhievUl97N2o3cTX9bO5pKWkNf2PWxtf2 - q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr9xzvvwwUfXAe5Bn8OpT/0fYT06fe4auf - 1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6UxTfkt8fTzjN0Mw3fCbO42d65i/OS8xML - WYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4NsY2S38RtuS24dsdOxm7Tntie3u/42+H - GiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNMsFmwt3AocN7HHea+xvPrqDtvL7+iQJEg - RshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09 - gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W657v - Ue3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4 - sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZ - cshzdnKX8sZuvstvKagtLLuVW5RwO7zY987pEsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7D - K48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QXQXX29bovxRqYG3ZfTb3uanzclN0c2eLS - qvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh8 - 73PoiN4o8+jkl8qvkWN644zjIxN3JwlTslO735qm42YMv2O/v529Pmc2TzffvXBtUXtx - d6l6+ewK50rvatQP0R8f16LXBdd7fvpv0G9UbOpvTvwK28JuFW1LbzfumO6M7p7dXdsL - 248/Ai6MUEVaojzRkWQZmHvkdRQDlLPUcBpGWn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf - 2Nc54JwUXEw4Lm5eHqEjokfFeSX4JPhFBQSP8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUj - bXZcRoZZZkN2UK5WPl2BqGisJKiMUh5RqVVNVfNS19Bg01jRbNcq0g7TMdMVPAE/8VHv - kf41AxdDRSNGo3njlpOFJqGm5mYi5mjzLxZPLdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9 - skO7422ncGcrF1FXMtdRtxr3JA93T2UvRq/Zs6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd - +DwoMdjxnGQIMmQgtDQs9Lx+OHv4XMSLyMQLZ6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76 - quDVnWtv44sTQhINk7iT1pM7U4pSQ6+b3BBIA2lD6ZUZCZnuWerZh7M3ct7nPspLvumb - b1AgWEhR+P1WZ9GD28nFxDtWJQp3uUpRpbP3+suelxffT3oQ+pdbhdlDtUdilVyP6aqQ - VRvVC08maz7Xfnja/6z3ee+LvrqB+uGXkw3Lr/YasU28zcotNq0hb/LamtpXO3m77Lvz - er68FeqL6H//Xm6gcJBuKP4T9XDmiPBo21e/ca6JD1M5067fZefo5lcXPy93rTavNf6s - 22zYat8ZJsX/IPeRcgJaBoBMLACnjgJgXgRAbA6U6iyhXFUKgAk1ABbyAG6TDeD2tQDm - 7g5+5w8oh6AAJaAH7IAPSAI1cBI4AH8QA7JBBWgBI2ADxgATgxnBvGHxsPuwbtgKnBmu - DHeFJ8CfwL8gqBDyCE9EJuIN4hdSBOmMzEL2oMhQ6qhwVC1qHX0cHYSuQf8iUyW7TNaF - YcI4YR5gtsj1yXPJFyg0KLIolij1KIupYFSOVA3UPNRXqOdozGnqaAVo07FIbCD2G50d - 3Vt6ffomBlWGOkZlxvpD6odamYyZPjA7My+wRLDSsBayHWfrYHdl3z2cyyHPMcQZzoXj - asH5cbNxt/AEHeE7MnQ0iVeHd4/vOX+ogLzA9rFXgnFCZsKcwvMi9aKpYh7iqhJsEhuS - g1LPpAuOx8r4y9rLGcmrKkgpCiodUeZS4VTlVONRF9AQ11TUOqFtrXNWN/JEul6FfofB - jBGFsehJC5MI01KzAQu0pbyV36m71mO2ODvH07fPzDhIOkY4dbgcdiW4tXrgPMO9hr1V - fG7jKf2CCeNEi4A3QarBT0NkQ2vOK4a/jjS6MHwRH42Myb+iFDtyNSZeLGE06UaK0XXK - Gz3pWZke2Uq5THk/80cKO4saip+XvChtLnt3f7YC80jksXX1tZrGZ4gXevVpDZONSs2Z - rWvt1p0veo68vda/MmA72PDp6Ocro1NjGhO5U8szmrPX50eXjq0QfjxZ39xU3YreaSb9 - fn7HnwEcBvxACqgDE+AEAkAsyAOVoB2Mgx0YK+w4zBwWALsBq4YNwrbhPHA9uD88B94E - X0JwIAwQ5xHliM9IOqQ28jyyEjmL4kU5om6iPqHZ0Hbom+ivZPxkeLInGIAxxGRjpsnl - yePJv1Acp0ik+EapQVlAuUvlQPWKmo86gXqNxoGmg1ae9h6WHZtMh6QLp1un96dfYPBl - WGQkMq4fimBCM6UwH2YuZ1Fk6WJ1Yv3JlsQuwN542IkDcBRxanPOcl3HKeK+cafzaPGs - Hyk76sjLwtvPl8J/UgAr0H8sW9BJSFhoU/iNSI4oXkxLnEN8U+KDZI1UjvSF4x4yprLK - csLynAr0ihgloLSlvKmyofpLbVcDpUmjxarNpyOje+LEaT0i9Fa7Y/ja6OtJBJQDjcyC - zAssOi23TolYO9pk2HafJj+jbR/j0OJE7nzSJdN1zF3UI8Kz+yyP9zmfHij7XSFMEnUC - yoKwwSHnxkNNwhrCpSJKL3BFZV9ijs64zHqlIE7galW8ZsL7JO8UZOqtG+ppkxmJWfLZ - M7l5Ny0KsIW9RenF9iVCd3fuvSuveJBcEfDozGODapWa408ln0vVKbzUeXWqEd8c31rR - NtRJ0a3ZG9PX/f7oh5ihpeGzI6tfEyfkptZn2uceLz5eaVv7uSm5fWk//ghADhgADogD - TWANCCAelIJWMAOjhknDzsDioJhPQjveAH4J/gy+hpBCEBFViA2kCjIW2Y/CoQioRjQL - moDuIBMgiyObxZhinpLzkqdRoCnCKX5QEiiXqAKoNqmjabA0t2hlaLuxXnQYunJ6U/pf - DCWMVofID71kCmGWZl5hqWINYVNlx7C/O3ybI5BTl4uDaw3Xy32fJ+GI71FTXjk+bn4q - /k2B78eGBd8KtQk3irwUhdK0eLNEp+SA1Jj0sgxClkVOXF5fwUMxVqlMuUdlQ+2IuplG - tGaN1oLOMV33EyV63w3EDcOMWk4eMvEwrTNntPCz7ILilmzzw+706WZ7CYcCJ1rnKJcf - bl7uo57WXm+9DX3a8Lp+Lf46xNZA/aDuc+YhQ2GO56cj/CO3omIvMUQXXha7Uh9nfHUk - npAIT0pPEUh9ccM0bSrjfBY2uyRXKe9tvkfBzq3rt/mLX5SY3J24F1yOuZ/917GK2kc6 - lX1Vp6snarxrF5/5P1+uw9dPNzi86m/UanrUwt568c1Yu1pHTudit3pPUu9AH3u/3bus - 990f4IMSQ2c+Rn+6O9z6eXxk+wvdV54xiXGlCe1Jgymjb8bTBjO639Vm5eZE5nEL9Iuw - xeWlz8ttK49Xb/6IWfNeN/l5fINjE7k5/atzq2I7Zcdv13iPjxT/g/PS/pmCQtPPx4+I - M9TU2u/+9y6+PkHQOWq/0EJXKryz8UmopYdqU0CwuTbUQnkLDLh76ej9xlOuTloGED4M - 1fUwT01jqKWC/u3SuxN1zCDMBGHOs076JhCmgbC0G97SHMKQJkyDEKhB4rBA2MItQPtv - u0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAsvI+xIWQn6Zd6Bertn1MhXA90oPciEXgANyAC - DKG9ogVZJvYtf/et9vtef8YPWCLAfd8zGPIMAN5gCvLxdfC6RIT23AHj4IoDLsAPBAEf - iBcEiOLl4jPi2384mtCYD1T/f6+DES/gCjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7 - cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noaeFEBDscuzJ6EGvkJS879KoFsIdNYGQNOPEEr0 - 8vAMxKlDXxPchHF6eBdRYZykuLg8+BcCxWXcCmVuZHN0cmVhbQplbmRvYmoKODAgMCBv - YmoKMzgyNwplbmRvYmoKNDggMCBvYmoKWyAvSUNDQmFzZWQgNzkgMCBSIF0KZW5kb2Jq - CjgxIDAgb2JqCjw8IC9MZW5ndGggODIgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNl - UkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYWUTUgUYRjH/7ONBLEG - 0ZcIxdDBJFQmC1IC0/UrU7Zl1UwJYp19d50cZ6eZ3S1FIoTomHWMLlZEh4hO4aFDpzpE - BJl1iaCjRRAFXiK2/zuTu2NUvjAzv3me//t8vcMAVY9SjmNFNGDKzrvJ3ph2enRM2/wa - VahGFFwpw3M6EokBn6mVz/Vr9S0UaVlqlLHW+zZ8q3aZEFA0KndkAz4seTzg45Iv5J08 - NWckGxOpNNkhN7hDyU7yLfLWbIjHQ5wWngFUtVOTMxyXcSI7yC1FIytjPiDrdtq0ye+l - Pe0ZU9Sw38g3OQvauPL9QNseYNOLim3MAx7cA3bXVWz1NcDOEWDxUMX2PenPR9n1yssc - avbDKdEYa/pQKn2vAzbfAH5eL5V+3C6Vft5hDtbx1DIKbtHXsjDlJRDUG+xm/OQa/YuD - nnxVC7DAOY5sAfqvADc/AvsfAtsfA4lqYKgVkctsN7jy4iLnAnTmnGnXzE7ktWZdP6J1 - 8GiF1mcbTQ1ayrI03+VprvCEWxTpJkxZBc7ZX9t4jwp7eJBP9he5JLzu36zMpVNdnCWa - 2NantOjqJjeQ72fMnj5yPa/3GbdnOGDlgJnvGwo4csq24jwXqYnU2OPxk2TGV1QnH5Pz - kDznFQdlTN9+LnUiQa6lPTmZ65eaXdzbPjMxxDOSrFgzE53x3/zGLSRl3n3U3HUs/5tn - bZFnGIUFARM27zY0JNGLGBrhwEUOGXpMKkxapV/QasLD5F+VFhLlXRYVvVjhnhV/z3kU - uFvGP4VYHHMN5Qia/k7/oi/rC/pd/fN8baG+4plzz5rGq2tfGVdmltXIuEGNMr6sKYhv - sNoOei1kaZ3iFfTklfWN4eoy9nxt2aPJHOJqfDXUpQhlasQ448muZfdFssU34edby/av - 6VH7fPZJTSXXsrp4Zin6fDZcDWv/s6tg0rKr8OSNkC48a6HuVQ+qfWqL2gpNPaa2q21q - F9+OqgPlHcOclYkLrNtl9Sn2YGOa3spJV2aL4N/CL4b/pV5hC9c0NPkPTbi5jGkJ3xHc - NnCHlP/DX7MDDd4KZW5kc3RyZWFtCmVuZG9iago4MiAwIG9iago3OTIKZW5kb2JqCjcg - MCBvYmoKWyAvSUNDQmFzZWQgODEgMCBSIF0KZW5kb2JqCjgzIDAgb2JqCjw8IC9MZW5n - dGggODQgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRl - RGVjb2RlID4+CnN0cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjzGI7jGEJI - UZS5zIQkiXqUiAZSpsyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+19r7P2vdZ - GwDsQycCwQcOAPDFBxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkfADDSUL8I - SSvHKnUh7IVp5VeTovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6QEAhxPEnY - xdPJFcLhEBYmWphpQrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4OwiqtbgAs0 - TJrX1TXAxRfCKRCvz9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/YgiGt6jgA - +Pz/sfFCHKZpAO7w/mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7e1tFe3s7 - twFADAPQ4OMSRAze50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwox6hP0fRj - zejaGbQZXzIpMFezCrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwuLJEkuSht - fPyeLFzOTv6hIlLJUrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mhaZhZmfmg - JdJK/JSd9SWbe7a9dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB72v8B79Z - wg6RNoArUCxIJdjgnHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/Njcu62ra - teT4qwnRieFJAcneKc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+zIL4w7JZH - keVtjWLRO2wl6JLlu59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtPP1Gt4a6F - 1048bXpW/DzmhUudej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9qyO/E9+l - 0k3V/bGnpNf/rWIfsq+r/8Y76/cc778MFH1wHuQZ/DqU/9H2E9On3uGrn9U/b45UjLp+ - Yf3S9TVqTHJsYjx9QntiffLOlMU35LfH084zdDMN3wmzuNneuYvzkvMTC1mLhkuwpZpl - /Ar/ytfVvB82a8xrA+uZP203ODbGNkt/EbbktuHbHTsZu057Ynt7v+Nvhxoks8L0UZyg - rKeWpinHctNlMNAzxhzaYsazTLBZsLdwKHDexx3mvsbz66g7by+/okCRIEbIV7hHVFIs - VXxBUkeqQHpdRk82V25GQUYxUqlFhULVSC1JvVOTXEtT+7zOQ91JPRZ9PYMQwxKjfuMd - E35TU7MQ85sWry2/naKwFrE5aYu3Szr94EyH/bQjyonbWdHFyhXvFuue71Ht2eE1enbV - h8yXFS/op0jQ97cmugcEBF4Iuhacdi4vpDi0POzR+Zrw5xEvI19deB3VeLHxUmP0q5iX - l+uuPI2tiqu4WnbtTnx+QmZiSlJc8oWU4FSf6y43bNKM0zUz5DJFsniymXLIc3Zyl/LG - br7LbymoLSy7lVuUcDu82PfO6RLDu4qlgvfYyjBl6+UT9/sevPzrfkXOwyuPAiodHhtU - yVbzPKF+slkzXtv9tPZZ0fOEF0F19vW6L8UamBt2X0297mp83JTdHNni0qr7RqiNpm25 - vb+jqjO9K7DbvEeyF9s79/ZN3+3+SCj64gOYgS8fngzGDzl8lPqE/jQ4fO9z6IjeKPPo - 5JfKr5FjeuOM4yMTdycJU7JTu9+apuNmDL9jv7+dvT5nNk83371wbVF7cXepevnsCudK - 72rUD9EfH9ei1wXXe376b9BvVGzqb078CtvCbhVtS2837pjujO6e3V3bC9uPPwIujFBF - WqI80ZFkGZh75HUUA5Sz1HAaRlp+rBKdKb0zQxBj7KFspnLmZyztrENs39jXOeCcFFxM - OC5uXh6hI6JHxXkl+CT4RQUEj/EJcggxCGOEf4nMin4SaxWvlMiTvCzlI212XEaGWWZD - dlCuVj5dgahorCSojFIeUalVTVXzUtfQYNNY0WzXKtIO0zHTFTwBP/FR75H+NQMXQ0Uj - RqN545aThSahpuZmIuZo8y8WTy3TrPCnTljzWG/bDNg+tLt22u2Msj2z/bJDu+Ntp3Bn - KxdRVzLXUbca9yQPd09lL0av2bOvvbN88L5aeFb8gl8TIcvfl6gWwBgwHfg8KDHY8Zxk - CDJkILQ0LPS8fjh7+FzEi8jEC2eiRKN2L/ZcKogmxKhfpr88fqUqNjbO+qrg1Z1rb+OL - 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 - V2/+iFnzXjf5eXyDYxO5Of2rc6tiO2XHb9d4j48U/4Pz0v6ZgkLTz8ePiDPU1Nrv/vcu - vj5B0Dlqv9BCVyq8s/FJqKWHalNAsLk21EJ5Cwy4e+no/cZTrk5aBhA+DNX1ME9NY6il - gv7t0rsTdcwgzARhzrNO+iYQpoGwtBve0hzCkCZMgxCoQeKwQNjCLUD7b7tPmKfFqd/8 - SG8/AxKHpJng6qb1ew2wLLyPsSFkJ+mXegXq7Z9TIVwPdKD3IhF4ADcgAgyhvaIFWSb2 - LX/3rfb7Xn/GD1giwH3fMxjyDADeYAry8XXwukSE9twB4+CKAy7ADwQBH4gXBIji5eIz - 4tt/OJrQmA9U/3+vgxEv4Aox/qfavp00l2+le3CWX6iClWf3XM3cH9WDe3L+s2KDv2eH - 1oD/Y/0/isAL+q6wf56GnhRAQ7HLsyehBr5CUvO/SqBbCHTWBkDTjxBK9PLwDMSpQ18T - 3IRxengXUWGcpLi4PPgXAsVl3AplbmRzdHJlYW0KZW5kb2JqCjg0IDAgb2JqCjM4MjcK - ZW5kb2JqCjU0IDAgb2JqClsgL0lDQ0Jhc2VkIDgzIDAgUiBdCmVuZG9iagozNCAwIG9i - ago8PCAvTGVuZ3RoIDg1IDAgUiAvRnVuY3Rpb25UeXBlIDAgL0JpdHNQZXJTYW1wbGUg - OCAvU2l6ZSBbIDEzNjUgXSAvRG9tYWluClsgMCAxIF0gL1JhbmdlIFsgMCAxIDAgMSAw - IDEgXSAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAG1wgd74WAAAOB/WHuE - JHbsvYlxP+86rzbV1myNFq3ZxT3ycIQkPol7n3fxK7H4N5lYbMYXydV5Mn70RHy+GZsn - Yj+sx2M/699xnP0Y/h3Dv0ijXzE2P/Ho3sgnftwPPLIc3R/+iIKeRcOrkfCMYmgWOXwa - CZGGQ1OKwWmY6SQc3B0KTqgHJiHq41CAejAwpu4fB0lHQf+BAf+Ium8UWB0GfIf7fUPa - 3qHf+w7e532n+ebzHNHreaM68HqI7oEXuMc9IO973FtdfQ9wt6u/3nO79jp7brAuZ8/l - fGXqeHUBfXE6ANpfnEy7TnvXAdjWdVDvOGyrdlsHqLVj3922W0lt1jZQS9u22bJZdlst - LdDmlnX5ma7F/Az2yWJiajY9AWyaTURj00zfZGwybpiMW7GGib4RazCuGzGahrqRMWao - 06xhBsb6GsbYoK/trRr0YHVVA329rrpe0euOrK3oaeq0FeKjTsuq5lFHU6t54Fr9oCW9 - 16qXNdyr7jWrZY1qU60qc42W1ejdThV6x21JhRKRkoocRUpsF1FkK1xEyRG4ePwCAu9V - FhByWFkAnoeVjBV5eKtSkQeYUyoAQjnlugLKHZpVQMDlWcU6JM/SzEByVmUZiCiXZfam - 5TIOpWk5USZNb03JpJxLUjKiVJKSSm5PXHwrEf/5D28kohvxKV+LRevCa9FpXomEu4WC - K24vhQLqAv4lqxcCPlM+/4LPO/Y5n3c4j3e+fAb4N+8M8F9GWfYACmVuZHN0cmVhbQpl - bmRvYmoKODUgMCBvYmoKNTY0CmVuZG9iagozNSAwIG9iago8PCAvTGVuZ3RoIDg2IDAg - UiAvRnVuY3Rpb25UeXBlIDAgL0JpdHNQZXJTYW1wbGUgOCAvU2l6ZSBbIDEzNjUgXSAv - RG9tYWluClsgMCAxIF0gL1JhbmdlIFsgMCAxIDAgMSAwIDEgXSAvRmlsdGVyIC9GbGF0 - ZURlY29kZSA+PgpzdHJlYW0KeAG1wtdVAgAABMFut1qzKCooBjAj5ixrC3zczRvFVS4x - 8g8jfzHyByO/MfULIz8x8gMj3zHyDVNfMfIFI58x8gkjHzF1gZEPGDnHyHuMvMPUW4y8 - wchrjLzCyEtMnWHjFBsvsPEcS8+w8RQbJ1h6go3H2DjGxhGWHmHjITYeYOkQG/excQ9L - B9i4i4072LiNpVvYuImNG1i6jo1ruPJ/ZCYxegplbmRzdHJlYW0KZW5kb2JqCjg2IDAg - b2JqCjE2MwplbmRvYmoKMzMgMCBvYmoKPDwgL0xlbmd0aCA4NyAwIFIgL0Z1bmN0aW9u - VHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAxMzY1IF0gL0RvbWFpbgpbIDAg - MSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K - c3RyZWFtCngBtcIHe+FgAADgf1h7hCR27L2JcT/vOq821dZsjRat2cU98nCEJD6Je593 - 8Sux+DeZWGzGF8nVeTJ+9ER8vhmbJ2I/rMdjP+vfcZz9GP4dw79Io18xNj/x6N7IJ37c - DzyyHN0f/oiCnkXDq5HwjGJoFjl8GgmRhkNTisFpmOkkHNwdCk6oByYh6uNQgHowMKbu - HwdJR0H/gQH/iLpvFFgdBnyH+31D2t6h3/sO3ud9p/nm8xzR63mjOvB6iO6BF7jHPSDv - e9xbXX0PcLerv95zu/Y6e26wLmfP5Xxl6nh1AX1xOgDaX5xMu0571wHY1nVQ7zhsq3Zb - B6i1Y9/dtltJbdY2UEvbttmyWXZbLS3Q5pZ1+ZmuxfwM9sliYmo2PQFsmk1EY9NM32Rs - Mm6YjFuxhom+EWswrhsxmoa6kTFmqNOsYQbG+hrG2KCv7a0a9GB1VQN9va66XtHrjqyt - 6GnqtBXio07LquZRR1OreeBa/aAlvdeqlzXcq+41q2WNalOtKnONltXo3U4VesdtSYUS - kZKKHEVKbBdRZCtcRMkRuHj8AgLvVRYQclhZAJ6HlYwVeXirUpEHmFMqAEI55boCyh2a - VUDA5VnFOiTP0sxAclZlGYgol2X2puUyDqVpOVEmTW9NyaScS1IyolSSkkpuT1x8KxH/ - +Q9vJKIb8Slfi0XrwmvRaV6JhLuFgituL4UC6gL+JasXAj5TPv+Czzv2OZ93OI93vnwG - +DfvDPBfRln2AAplbmRzdHJlYW0KZW5kb2JqCjg3IDAgb2JqCjU2NAplbmRvYmoKMzIg - MCBvYmoKPDwgL0xlbmd0aCA4OCAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2Ft - cGxlIDggL1NpemUgWyAxMzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAw - IDEgMCAxIF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBtcIHe+FgAADg - f1h7hCR27L2JcT/vOq821dZsjRat2cU98nCEJD6Je5938Sux+DeZWGzGF8nVeTJ+9ER8 - vhmbJ2I/rMdjP+vfcZz9GP4dw79Io18xNj/x6N7IJ37cDzyyHN0f/oiCnkXDq5HwjGJo - Fjl8GgmRhkNTisFpmOkkHNwdCk6oByYh6uNQgHowMKbuHwdJR0H/gQH/iLpvFFgdBnyH - +31D2t6h3/sO3ud9p/nm8xzR63mjOvB6iO6BF7jHPSDve9xbXX0PcLerv95zu/Y6e26w - LmfP5Xxl6nh1AX1xOgDaX5xMu0571wHY1nVQ7zhsq3ZbB6i1Y9/dtltJbdY2UEvbttmy - WXZbLS3Q5pZ1+ZmuxfwM9sliYmo2PQFsmk1EY9NM32RsMm6YjFuxhom+EWswrhsxmoa6 - kTFmqNOsYQbG+hrG2KCv7a0a9GB1VQN9va66XtHrjqyt6GnqtBXio07LquZRR1OreeBa - /aAlvdeqlzXcq+41q2WNalOtKnONltXo3U4VesdtSYUSkZKKHEVKbBdRZCtcRMkRuHj8 - AgLvVRYQclhZAJ6HlYwVeXirUpEHmFMqAEI55boCyh2aVUDA5VnFOiTP0sxAclZlGYgo - l2X2puUyDqVpOVEmTW9NyaScS1IyolSSkkpuT1x8KxH/+Q9vJKIb8Slfi0XrwmvRaV6J - hLuFgituL4UC6gL+JasXAj5TPv+Czzv2OZ93OI93vnwG+DfvDPBfRln2AAplbmRzdHJl - YW0KZW5kb2JqCjg4IDAgb2JqCjU2NAplbmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFn - ZXMgL01lZGlhQm94IFswIDAgODMwIDczM10gL0NvdW50IDEgL0tpZHMgWyAyIDAgUiBd - ID4+CmVuZG9iago4OSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIg - L1ZlcnNpb24gLzEuNCA+PgplbmRvYmoKOTAgMCBvYmoKPDwgL0xlbmd0aCA5MSAwIFIg - L0xlbmd0aDEgMTEzMTIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBvXoL - eFRFlv+p++5HOt2dfj/S3el0dzrvB0mICaSJeQKJkAgkSDAJBAKCAsYoKmwUGCQqishD - cJ1BHQgwSBMidGBkGQdFHNfXKKijrq7oOK75dHd9zAjp/p+6HSI4s677/+abvl1163Xr - nvrVr+qcqltAAEANvcBCaN7S9mU3LplxJ6a8BED083q63ff/aeJjGP4AgF2yYNnCpboP - fvsiAD8VQKleuGTlgo6cB14FSGwGMM/p6myf/5+n9zsA/INYR1EXJihTxEKMf47x1K6l - 3bfdNEP6F4CAFuPdS26a114XnXYC42sxXra0/bZl0l3Kv2D8JMbdN7Yv7bzuxvm3Y/wD - jKcsu+nmbraaewIgjcP4nGUrOpf9es2NeRhvQ/lewTSCF/2pMSjIoR/14oUBGBaL0Tr/ - bz8eBFFSKFXqBA0kanX6JAMYTWaL1Qb2/1tF/3+lEej/9cefBC3/DKTxvWDjcsAFEHsb - 3Tv0Hp0R+4Q/Ddro0th/sqVY1RB1TLS8DE7C/bATDoIA/RhOg7mwHc6QxTBE5sAgnCXJ - kI2c4SACU+ElEou9BgvgSSzfDc/CFjiE+KfBUjBi7kbii92O8RCGO2Bt7HFIhfHwM3gG - SrDWjTAc2xs7jLmNMAP2wX58/nfEyxzikmJPxc6DBNOxzrWY81psauwg6CETKmAapq6F - E8THvhPrAguUonSPws9hF/wGPid3k8FYV6wn9mrsQ2Aw1wFNeK0ig+RD9iD3s9ijsc9i - UUQiDdLxrW2wGZ7A+g/idRLpU0VuIN1kM9nChJi7mUFuHW+OjiAOQajBqxZugnsQgSE4 - Bf8FfyFfMBZWy3azz8UKY/8NKpiCraQt6YQevNbjtRHbdJwIJJdcTaaRVeRhsoX8nkln - ZjDNzK3MbcwnbAM7h13J/p67mRvg7+O3C6ro17HjsdOxN8EMTrgOVsBqbN2z8Cp8Bd8R - FutyEB8pJRVkLl69ZCczRHaRIWYaOUleZfaRfyMfkS/IBYZn1IyRyWC6mc3MfuZZ5mV2 - EbuFfYT9N/ZrbiLP8Lv4jwWf+IdoR3RD9OVYaezD2J9xFpDAgz1TAQ1wPbRja5fBOPgn - bMUBvA5ir52C5+CMfH1EHDAMf0YUcK4gNpJP6vFqINeQBWQReYwcw+uELMs3DHYEo2B0 - jJlxME1MB7OU6WXeZHpZO5vOTmZnswfxeoE9y15gL3A8l8QZuRquDu7jlnI78NrN9XMD - 3Ct8CT+Rb+Bn8r38Bv4+dh7/Gn9WWC1sFAaEL4QvxTRxqniTeB/2zhnk7G+uGBocSUXp - 8+FGmEcqSQdsxd7YRdqhD9k1n9yDeC2DtFgru5qtYXKRDSfgDmTrDlgFG9g5sCv2FrsP - ziFTlmCtvbCHqwAnvw17527IRRaNXqFgejAt4PelelM8bley02G3WS1mk9GQpNdpE9Qq - pUISBZ5jGQKZVd7qNnfY3xbm/N7a2iwa97ZjQvtlCW1hNyZVX1km7KbPtWPWFSVDWHLB - D0qG4iVDYyWJ1l0GZVmZ7iqvO/yvlV53hMye3ozh+yu9Le7wsByul8MPyuEEDHs8+IC7 - ytJV6Q6TNndVuLqnq6+qrTIrkwyFEA5lViadOEKgohWH4er2VV0WvNESVWGbt7IqbPVi - GPNYX1X7/PC06c1VlXaPpwXTMKmxGd+RlbkojHLCver53vn3RkLQ0UZD7XOaw2x7S5hp - o3XpMsJmb2XYfPvHlu+jl0JV912WGWZ81e2dfdXhUNu9CC6NttFY+30Ym9LkxmqZdS3N - YbJuVAgq42KUlIrb6a2icrUtdocV3gpvV9/iNgQXGpsHbCFblbe9siUM05oHrCGrHMnK - HLKsLvVg64eyJmVNovdSj2V1/P7HNfH010/Su2X1qQ/wPqVxDABC3+StQznD7nnyS7wo - 7HjqdY6HvnnjESf8tRBs5iKU5+owg5xhfWHeV9ce7m26JEZXZVy4tsWVAwqrjbahraIF - y7f1aa/CnsLyWq+772vALvQOf35lSvtoiuDTfg00k3b0GFfCpP1SuEcGBlvdZfF20f7t - kfsU415L1WUJGKfQUJnDhnD+lGnNnrC7BRMikJE5JQKKac2HCNnYEiGxdRGodA6BAtjr - 52J2JqXaokp8P0ayMjEh3YOh7Ex3Nba6mnLF3efuq5vf5652dyGZOJ98x4zOvpYcRLCp - GXGCa/GNoRb7WLCzpeUqrCeH1oOPYPG+Fqxh8WgNeJeTckawUG7mFOwV/7Tm6c3h3kp7 - OFTZgr2A9D05rTl8Epnb0oKl8sYkRYlXLbKMypyPMuelY35BvJYmrAOraOnro3U2NXs9 - 4ZN9ffY+Ot7i8QiBHyaERhMiQItgw6sipHcaPos3r8dOE7werwfFaqGYjkNKX2JUBAp/ - HOGiMbnxyWKUtkhGePzfCeGSn4LwVT8J4dIxSa9AuAxlLqUIT/jHITzxCoTLfxzh0Jjc - KOQklDYkI1zxd0L46p+CcOVPQrhqTNIrEK5GmasowjX/OIRrr0C47scRnjwmNwo5BaWd - LCM89e+EcP1PQbjhJyF8zZikVyA8DWW+hiI8/R+HcOMVCDf9OMLXjsmNQs5Aaa+VEZ75 - d0J41k9BuPknIdwyJukVCM9GmVsowtf94xCecxnCaPBW4JLyVVx7sSBCeQSaMiIg5aDy - QydpIwCvoqNxDLPvRoBDBxgW34Vj+ATAzIxjWAuP99y8Ap1HF0BXwW2MXPx3/pnvro5w - 9RcOYykCG6NzmXb+TTDAxJDCoFMkmcxmm+I4eRRteQN5NKQJQS83VWs1mr71LGm0RMT8 - dRkZDV/VD9vesw2/MdxQ1Vn5CZSX5+USRhR0WrMpyZtNAv6Av1BbXJTEzP3nnJrp+ZtX - PlQdHG9StZYe59+MvvLgH6IfRt//8uHoZ+dXL3m4f9Y1JO2Pm4lPlqcS5TGjPElQFFJL - Okgyojzc1MQkKhKAAkVSSFaD8VtP+R2WuCRvDL93mRxJ+uIinTbgZwuSiTmZGLWiwNb8 - PLuaSrFjkj83OLf0WHQuKdp4jniI58uHiembmztXfbU8+tanW6LvyzLMIO8zU5htiL07 - pIQclth4sHJ8hFQc9hyrpW89r/0EcuqH83KTPEbPDPJNVMlso+tf3BKIAXdzrBOfTXya - VAPDQ5m2DHKwqK7QY8Qs6OzE7QZojL0rr1IScf1ZBu+FxqfnEqVWZVc7AgW12kWKxVqx - RNKrFaw9X0xVOLVqZ2kGkx0sPVrKlOan+/RakZccgRSzI0L6Ql6z0yUGnNkqxlmoKhPL - yhwGMZjen2qbaA86JicGxlsnTPw12YaLsyGyFUZh+2q4QftN/fmRU/qSHOzBYXoN60t0 - enNJq05fkj2cPUzwrjOX5OVevTKUVlRsTAFi9ZGiRA9Yku0eMLkNCGEKFDMesDnNHmL0 - oAcZGRlEW4Z+xl133QWtpDXVVJBfXDSBaEgiEUTBSIqKiwrH+b0poiB6J5KCfFzm6AxY - CF+hId4UpA+9+QvHFRUnEc2Khutbtnq68pd25DWRwYlG9Zrb7y/1KPv5b594pucWs0+d - rEvP9LemmxTFL9+55Zlj2/pemZ1Zt3uT0SFoEhw5C8kSKdOSNadpanrT8ztra7ePbHOk - sOw6tVDhDdUufvqeLU8mkfO4jQM9sfc5H/8s6CAZloWyd4t7HOccbIqUmIzdCGYnL+qU - yU6VyhCQbG5btjabBEFndbnXe55plUEtqx85f56iOgyIJv51Jbo4eha9SVCaBIOf6JXo - GUWznyQpkv0IFqE4QWtSgY5CodcZGBkBozc1DpJgNJhNBT0HS59se+Ev37xz+7X5JbuZ - BZs23X/HkL/mWf7Zkf+onx4djn4VjYZLvfUbVn16Yu/7R17bNvcQ8pEBXJmzr3INQHeV - 9oRy9ljJdku/tM/CTpZ0Ow0saxCcNjHBaVDZRbvdrA3oCRtgdDanMmC2OpwRIh72rFg1 - yhhsWVn9cEkJsmSUMRjQIrWRHuPAKvnURqUfNElabKUuUStaMcYD6yGE4ViVKcEPiXr0 - FBbBTzgieLDlMlUoWbQ4TChjKF/AZMZJhNLDGGdFAaUDU6iFApE5+5H5oHbF6l9Nzr3n - oWVrrAeTvzz++ndE/4aDawifm7emf+kvdr274dY3nyMFn+C2wlU8YjA+9g47jP2qwjX3 - raH8Yk2NZpZmD7fXzvskA5Po1ILkdIpJSsZpVvHZSdnaoE5vc6kCNmuya71nRcXlzccO - /mHf2iwOhRIIsaiwbQ70wMr4QWmX/NhAuXexVXpKb5n0ghHMJrOuQOctpM2CwnH6gm8e - 2rVq1+7b79lL+ppyJxx4vPxXNx2OfvfF++T6T8+d+d1vX32RKR6XPIVxfjdxy7xmkvXd - Z2QWziG1sXc4G+50OHBXzEfUoZXbpEdse1wsr2ESeYNRo080GkLqkEEK2sgU1RH2NHme - PW1/S3pbcdb1lvdT86de1WndaT0zR+I9qYk7TM7UEkEUTR6nQ1Q6TSqfuM2xx3EUxwDn - MyX6HLxVqRZ1mkCiM8DbAqnZYsBq9Qfe8OyOkx+5L1P/jZESfQlOIyV4y2mNzywYKhsp - 0w5jqsyWavByPIvbSITnBJdfp9Vrk7QGLSeofSn2VD+4weknyU6FWfSDyqjxkwSN1+bB - JB49yYK8StCiR6ea+Fwjkyc9I/0usrwVlrdSCuFl9CTjkCouKkYC4VwjINo6JBFB9eRN - EUTCDJ4dX6TXXvyCf3Db/dfmGg6J1+Q1rpzU+EL0M2L5d+JSpU0+cGc/T7xczQ0zpi+Z - /PgTz7UW1ZRuyp7m0BIv7qUxpCLqv6X67sN95F0cbjj/47Yq6q/XcfetPpQhOgWlkyWJ - hhJTgqBXWlGVaRJ0QbNe1CdqXBpGc9FgtVgvehaujlNspLXklAzUJbWKE0pZOarZvFzU - agX5JrORjgvBWGD06vAqLCh82ls+qEs1O6yqRvfA4MCWLXzFuDkM8yRDZjy18eJ89tGN - /SgXCxOipeynyBUXZOHu69FQfZGhTqpTNEstinvUe+39zr2B3RlDdlVIYk0pQc0pZQqq - FE4IOq1KvVOZmC1mZ/MONtuUnRXkbblqTSBhoj/gsObkXjZAvhouoQwYOf819vMlnVI+ - LHd7vN8zvWm2ZJUu1af1e5P9fkizoadTaTyQqFEn+JwpfhKwB3GeUOs9cu+iIomrkrgu - oSOnsEBnEAVPij9QgF1Mu1fWFqm0Z0FWKvKsgSqGMHfOLSjcXbYseubA55qjCYEJa14J - +dmi7aueil4g4jFS+eQ/naj2bb7z2Wsyo69xFRO9V6+/mP9Szzs7f1kbKHto5nuN074l - TpJAsqO7Tg5cv+PpZw7OW8tkyf28FkGlc4oJmkKZOGoks2iWAlwg6RbxFklKSmCSjAA6 - pyAa1cqEoNJmIcYgmKxmS4QIhz0d8TmFdu+ousBuRm1RQugAkZUB6si4YvTqxsl0Neq8 - awdDBbPu/lNT1lBy3vplRwZx8n93uqfkiZbHRqYzT/QUN+84O/IC5SGDO9xASkftx6KQ - Q/yYQ3IKrFKBHETeBkUWJ2zFvu8lOTVSdmqMduWybYNTlA6ZtvYo/rj0C2f5Z16S296L - bf8z1q1CjTK/hSFXScTK4AAzC7P4hfxK4TZxPT/EnmHfYZU8L0iSqGCZtczDSEqWKdEr - FByPG5nCUj2iJom4pckLColHFaFEm5cVlKKgFGwJCkYZBJVVnTDg6RgiprjVQgErszZo - P7FAeVlZeVk5tVYIuvX12RnSKu1vuPXZloxWfpX2pFYqk8rQLKXTwQpUrqRAgUNW1Hl7 - D5CXP4kuIIc+iQ5sO8A/c3E/OR29aaSDcfRFb5TbtwGxmyBjFwxhL2IrEDTEDFi0Ai+D - DDtvzPzFN3g3DA5S41quA/EXfFwN+GFdqFSURI2QaJbMGnNiQArgFFprnalaqFJ7fUqb - 02tVMpzZ53GanQmCCILd4WOTlGn4Tl3QECFkwBZERUzQFCXZPhwc1kBahCRcTqLz2q+G - vxoZFcZcVl6OahrnWoQGB+IlRhlHGWW+ZHEhseg0iLy6jGEDoXEty3sbMlPLHu98qyH9 - +A31ix85agsuW7BnkMvZfk3qhPLU6plNj167caSY+fSGaRt3j2xiji/Nn/LYK5R5Mu/Y - YZxnrGhxzA3lHRVOCwwnGISAoUfoFnmDmjFYtGhJgWBRKW2izQbqoMLmINmWoBWsdjRn - rxgecZUSn02wXcPfDxGCFpPxsqbQMYJzvIZge8ja/VP3dZ2flnnUmbs6FJw8Pss+SPag - /HMbfz7rcTpWOsrmJ5gqCpcvGnkFhcX5sTT2NudBO0mN33ys8GCoYLu0VfuI6Zdcv7Rb - u9cUkV6QznEfa/5kUF8lCU6LqHbqVVbRajUygUSbXREwWm32CFGgtTSqDWXr+ntLSVZ7 - mWDm/KokBWouHeMnohlDfAKGlAa1H4gWPcmExhGrQU/WbdSjRlGqvnB0FkCLSI9ajPGg - 5SAbRB+sy5167Jdbtz6BH9YuRr99L3qR6P8odJPE3VvnPnxxYP959p3o52gejkSfIhkX - 0QgPUZuoJzqD82HTNZAC3aHMvdIeM5MmuR06jeA0iomCxulQpWiYgMWWqkRL1xNMSbR6 - U/+mpSubujqZZ2gIOkx24G1+zg92bBhvQo9YNX5gzXKb5GZRe5dat/E+k+1bUhDnJ37s - oHoalwA6L/P8Hl/1seNVPvSj2QeLQtfdcSR6tHvHysbc0sGVv3+9d86h4/N33DlrN3to - Y11aWfRP2MbHt15fmFw38h7VxTiOmYdwDOrgmpA/wPoTitkajtNIWkaj0CnUAYnSUKeU - bEmE2nxg1SdFSBUOrLg6ptY8TjVowdSXnxo5RS0aOp7i87NMvTF9jGN/v/HJG3iLU2vX - 3vMQDpWhop0Me4JlDq4Y2U7HRUXsHHuEm4K6N4dkhx4Yr9jOb9U/Ythu3J4upKX6AkWe - ak9Nak1gZuqswILUhf6V6pUJKzU93u7Ubl+3f3dyf2YSi6YQn8VlJ4HNaDc7LMYsQ3Za - omqR5PcV+RhfSoKSy0iyPO9wJomcM3tHhipHVGi0jAg5nhyby2KyBMwT0/xiIM2Wp3EF - tBMhkG3NzRsYs99wConr7xIthmhzS3LQxyFHjTi6OqRTynKZyVNJFuM3+mx+j8blAYVf - 9BA2E9eXfDqGnHpMsxssHuJOTPGAJ0WTIAWUHuL3KZQki/OAEEQvWefwEKsJPdmMkxW9 - 7MkUuUR8XA7glkTceA74c6jphstCqhlFb9yMo/RxEWrtGZA4/gD5QvJV9s/fPiFw8wMb - JnX/Yei/bria2cf7Jz6yYFFVWsOtz1Ysevv9L06L5CiZNjt31qzrqlLR8k1Jr7tr+683 - zu6akF/TEKpOtyY5czKrHn7g1bd/wfwFuWSOfcEo+Nk4OzQ+nZCtPKkhEVIe8nGmEjMr - aJQ6G07X+HU1CEaNMZF1sQx70WS12tC2G109/cC2y6GT9EjZsHbkPO4iFFA9q6PrvEtr - YH8hNe/6j+zf7zfmJSQbXFcHVs/etImfHX1z80jV+CQVYTYqpLsWMs9tRn3DQG/sI/Z9 - HM9mlHBu6KqI4QUDo0iSDNYkqyFNuJU9h8oWeI0ShAQlj3OXRbRYcEmWrQyqVTYbCVJh - X79kDdTTyYvSH7s/bseVl1FCUOqTVhIXFA0suogplu1q7BWdj4y35a75daVvcB/jHbdw - 88dNWeQglzNS0jiurX/2PzOaC689NiH92kcaNzBv2ej4VOHE+xmXA2iPhLIryHOEgYXQ - xXSxC4X13D38HuhnJPxqzlRxk/mfcRv409wLvFSXdnOaKMlTrWw2T2lcGYktG8SFhJuL - kDVHWXapniEM7tasCSULaGXgm3iBYwnhGVZgAU0PpUQ76yBzjFArae1hclCwWhu+stSP - fPDBiFVWo9S+KDeX6Uc1qIjmhbbhfL0Yv2VMmb4y5GOCepblIKgXBFzHXFE5GjMHefi+ - 3pKSkZKSH9TMi9oM/KOJgkuW1uVJClKABsq7JJlkPBddcjJ6C5dzcTvbdeE1RIjg2QXg - d2FITdyh1TXcPgV2P6kW61Tr2T5pnfJF5hT7vHhGel55RqVaIC6WOpWLVD3iSqlHuVK1 - TuxTKWlZpoa9FW7j2VlppjRcmXKlpJR7gDzACQqOsCoGDTE1nkqRlCpWVGoQI5EXdkos - d0rJKE6pgOxUWxMo5miAUbDkRsX9saah8YGo4Q4SRUjNIzYinn7Qq9Uqfr02A//YXYMK - /JatjJB7Q0l63BoQBY6nBQVRgcdhsGfvDWn0HG4YqLHZ8qOEWnfrtatOWXhq3qGd95wc - WL9Ke2oshZp6y5cvR2vPzhTYKZYqhPPcy6+9+PofBqNnjr/z++PR3yGkg+zUi0NszYXX - 2AkXf4uA4tgxRuvktRHV/C+Gbuwz3mPZY2GpPTteX6tv1i8Ub2VvFe8zbIdt/HbjNtM2 - cz/0m7S1MMVYYz5j5Cr553lmPb8bdpM9fL+ZT03jLUazCe1to1qV6JQ01FAw2RFQygmz - 0XJQ/YAJ7YU34gs/pF79ecsVQMaHHUKcb82xoKWLRCwhCF1IbzSCybRUbzZbeEIouS3r - kZMIDb1JeEcU8nKXo9nbSgoElhEZeVIspAvhouKJpBiRYVnPaf+ajopHex/1B5Nz0rX5 - OVp+oiba/RJxES5nYXRT9POnogsGBenJBMFjkR5O5RqQindTOwnXt+xt8vrWjmvJ9lCR - /WMrfL/OdeJC16VTerCt9uSgxfVXy12353XPwtEdlbEp8SxuDoxar2jU03mRLnrLh8n/ - vO71FRYYRZx+/mr9yyQN4u+vV8Gul146feGsbJujh6fCzi07c31i2degk+T48+WH6L45 - xO/RUsGHu9C44YwkjP/wLgSjQTweRv7ceXFYtWksZ7QAnurQQwVTgpTaBxvRVaKbwd2M - q5qboRFdDwdQivfx6GqxnAPvE9CtJadhLeb14n2DsA/DmIaOlu3BOjZgHq3XjPFeDKvw - 3JUe70Z02B94xmYc3ImnTt6FL8kxJpXZzerZd7k0/H7gxhMvJ/gPhDlCRMwVHxc/lV5Q - WBQrlJLyiGqq6im5BU48p8PCDfjVgAEtXq0A4qdKNdAFI52l9aPtFDAPKibPrKmty6jt - XNLT2b1oXjuWYOLNxz1v3Nj+Gz8npqHpgpa1Fr9qVEIVVMtnoepgsnziqQGukc9kNeI5 - qxkwE2ZBM7TAbJgDJ+PfPerw20c5ukJ0GRmTLIjTbngQ3S/QsbCI3Asr0W1A9wg6biy0 - F2ND5N4BTgodIyvBRiaHVJzrWoPVZVGqXK/jUmPwMdfblo+OEyskwIfEOpAAiklK8gvy - c5gPLvJL3FW7HU9tpZEdh4NLXG2YtReWoetFx8o+IXsHkvNdJ0gm+DiCz/ghmSNHXH/M - y3J9nBdhyIDr2UCEw9tvkjEWSnSddD7m+hfnQtcJdPvjWfuCWOKIa69ziWtzcoTsGHA9 - RBd8A65N8dstTnz0iGtpcKtrfp6cP3VrhNk/4CrB/JkhlatovMdV6DzvyglEJILxLOdU - V3rev7pS8UEs5sZKfSGdy+Hc7LoKs5KdVYGr0B0n+8hOSCc7B3yTXccwiM09XBccvzVC - 7jhcm5bni5DbQ0W1aVuDtQFfcKrLF6wOBDA88wVxrXidOEnMFzPw4BQaf6JdNEh6SStp - JLWklHBZHyG/Gih3CcfJfihHWPYflgQJdfJTmMgdJwfkxANHJU5iJJAMkdgHeOiTAC53 - 9w8izQhg4Iggh4QIOYDfqmjSgZALKU+AkzO0yDwi0w+JyhCJQUKFyf0RAdaZesot5fqJ - upLqyv/Ja5NzLvmyufm3PQtxhrfiGYnwPmcLHkfBQMzZcqko7tv9L7/uW7BAZ0UG1XyH - e5YtXiAfr/FWdbbhKZvwvT143Km3w+0+tHjZ6Nkhf1vHvC56vqO9M7zM21kZXuytdB/q - kZ+jyZdlL6DZPd7KQ7Cg6trmQwtCnZUDPaGeKnrM6HBHxYrWK961YexdKyr+xrsqaGUr - 6Ls65Od+8K5Wmt1B39VK39VK39UR6pDfRSGoWtRUcXM3shOP4OARmLSmcN302c140qyl - MkJ203M5t8D/A/LNF+sKZW5kc3RyZWFtCmVuZG9iago5MSAwIG9iago3NTY3CmVuZG9i - ago5MiAwIG9iago8PCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL0FzY2VudCA3NzAgL0Nh - cEhlaWdodCA3MjcgL0Rlc2NlbnQgLTIzMCAvRmxhZ3MgMzIKL0ZvbnRCQm94IFstOTUx - IC00ODEgMTQ0NSAxMTIyXSAvRm9udE5hbWUgL0JKVkdISStIZWx2ZXRpY2EgL0l0YWxp - Y0FuZ2xlIDAKL1N0ZW1WIDk4IC9NYXhXaWR0aCAxNTAwIC9TdGVtSCA4NSAvWEhlaWdo - dCA1MzEgL0ZvbnRGaWxlMiA5MCAwIFIgPj4KZW5kb2JqCjkzIDAgb2JqClsgMjc4IDAg - MCAwIDAgMCAwIDAgMzMzIDMzMyAwIDAgMCAzMzMgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAKMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCA1NTYgMCA1NTYgNTU2CjUwMCA1NTYgNTU2 - IDI3OCA1NTYgNTU2IDIyMiAwIDUwMCAyMjIgODMzIDU1NiA1NTYgNTU2IDAgMzMzIDUw - MCAyNzggNTU2IDUwMAo3MjIgMCA1MDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwCjAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAKMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCA1MDAgXQplbmRvYmoKMjcgMCBvYmoKPDwgL1R5cGUg - L0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvQkpWR0hJK0hlbHZldGlj - YSAvRm9udERlc2NyaXB0b3IKOTIgMCBSIC9XaWR0aHMgOTMgMCBSIC9GaXJzdENoYXIg - MzIgL0xhc3RDaGFyIDIyMiAvRW5jb2RpbmcgL01hY1JvbWFuRW5jb2RpbmcKPj4KZW5k - b2JqCjEgMCBvYmoKPDwgL1RpdGxlIChVbnRpdGxlZCkgL0F1dGhvciAoQXJ2aWQgTm9y - YmVyZykgL0NyZWF0b3IgKE9tbmlHcmFmZmxlKSAvUHJvZHVjZXIKKE1hYyBPUyBYIDEw - LjUuNyBRdWFydHogUERGQ29udGV4dCkgL0NyZWF0aW9uRGF0ZSAoRDoyMDA5MDUyNTAy - MjA1NVowMCcwMCcpCi9Nb2REYXRlIChEOjIwMDkwNTI1MDIyMDU1WjAwJzAwJykgPj4K - ZW5kb2JqCnhyZWYKMCA5NAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwOTQ5NjYgMDAw - MDAgbiAKMDAwMDAwMTU4MSAwMDAwMCBuIAowMDAwMDg2MjcxIDAwMDAwIG4gCjAwMDAw - MDAwMjIgMDAwMDAgbiAKMDAwMDAwMTU2MSAwMDAwMCBuIAowMDAwMDAxNjg1IDAwMDAw - IG4gCjAwMDAwNzk2NjAgMDAwMDAgbiAKMDAwMDAwNzQwNyAwMDAwMCBuIAowMDAwMDA4 - MzY3IDAwMDAwIG4gCjAwMDAwMDU4MzMgMDAwMDAgbiAKMDAwMDAwNjY1NSAwMDAwMCBu - IAowMDAwMDA1MjI3IDAwMDAwIG4gCjAwMDAwMDU4MTMgMDAwMDAgbiAKMDAwMDAwMzUx - MCAwMDAwMCBuIAowMDAwMDA0NTIwIDAwMDAwIG4gCjAwMDAwMDg5OTIgMDAwMDAgbiAK - MDAwMDAxMDA3MyAwMDAwMCBuIAowMDAwMDA2Njc1IDAwMDAwIG4gCjAwMDAwMDczODcg - MDAwMDAgbiAKMDAwMDAwMjYyOCAwMDAwMCBuIAowMDAwMDAzNDkwIDAwMDAwIG4gCjAw - MDAwMDgzODYgMDAwMDAgbiAKMDAwMDAwODk3MiAwMDAwMCBuIAowMDAwMDA0NTQwIDAw - MDAwIG4gCjAwMDAwMDUyMDcgMDAwMDAgbiAKMDAwMDA2Mjc1NiAwMDAwMCBuIAowMDAw - MDk0NzkxIDAwMDAwIG4gCjAwMDAwMDIwMDUgMDAwMDAgbiAKMDAwMDAwMjQ3NSAwMDAw - MCBuIAowMDAwMDAyMTYyIDAwMDAwIG4gCjAwMDAwMDIzMTkgMDAwMDAgbiAKMDAwMDA4 - NTUyNCAwMDAwMCBuIAowMDAwMDg0Nzc3IDAwMDAwIG4gCjAwMDAwODM2ODQgMDAwMDAg - biAKMDAwMDA4NDQzMSAwMDAwMCBuIAowMDAwMDUzOTE1IDAwMDAwIG4gCjAwMDAwMzg4 - ODAgMDAwMDAgbiAKMDAwMDA0MjQwNiAwMDAwMCBuIAowMDAwMDY2NzQ0IDAwMDAwIG4g - CjAwMDAwMTgyODYgMDAwMDAgbiAKMDAwMDAyMjA1MiAwMDAwMCBuIAowMDAwMDcwNzMy - IDAwMDAwIG4gCjAwMDAwMjIwNzMgMDAwMDAgbiAKMDAwMDAyNjA2NiAwMDAwMCBuIAow - MDAwMDU3OTAzIDAwMDAwIG4gCjAwMDAwMzA2NjQgMDAwMDAgbiAKMDAwMDAzNTE2OCAw - MDAwMCBuIAowMDAwMDc4NzA4IDAwMDAwIG4gCjAwMDAwNDI0MjcgMDAwMDAgbiAKMDAw - MDA0NTk1NSAwMDAwMCBuIAowMDAwMDc0NzIwIDAwMDAwIG4gCjAwMDAwMTAwOTMgMDAw - MDAgbiAKMDAwMDAxNDQwMiAwMDAwMCBuIAowMDAwMDgzNjQ3IDAwMDAwIG4gCjAwMDAw - MzUxODkgMDAwMDAgbiAKMDAwMDAzODg1OSAwMDAwMCBuIAowMDAwMDYxODkxIDAwMDAw - IG4gCjAwMDAwMjYwODcgMDAwMDAgbiAKMDAwMDAzMDY0MyAwMDAwMCBuIAowMDAwMDQ5 - OTI3IDAwMDAwIG4gCjAwMDAwMTQ0MjMgMDAwMDAgbiAKMDAwMDAxODI2NSAwMDAwMCBu - IAowMDAwMDQ1OTc2IDAwMDAwIG4gCjAwMDAwNDk5MDYgMDAwMDAgbiAKMDAwMDA0OTk2 - NCAwMDAwMCBuIAowMDAwMDUzODk0IDAwMDAwIG4gCjAwMDAwNTM5NTIgMDAwMDAgbiAK - MDAwMDA1Nzg4MiAwMDAwMCBuIAowMDAwMDU3OTQwIDAwMDAwIG4gCjAwMDAwNjE4NzAg - MDAwMDAgbiAKMDAwMDA2MTkyOCAwMDAwMCBuIAowMDAwMDYyNzM2IDAwMDAwIG4gCjAw - MDAwNjI3OTMgMDAwMDAgbiAKMDAwMDA2NjcyMyAwMDAwMCBuIAowMDAwMDY2NzgxIDAw - MDAwIG4gCjAwMDAwNzA3MTEgMDAwMDAgbiAKMDAwMDA3MDc2OSAwMDAwMCBuIAowMDAw - MDc0Njk5IDAwMDAwIG4gCjAwMDAwNzQ3NTcgMDAwMDAgbiAKMDAwMDA3ODY4NyAwMDAw - MCBuIAowMDAwMDc4NzQ1IDAwMDAwIG4gCjAwMDAwNzk2NDAgMDAwMDAgbiAKMDAwMDA3 - OTY5NiAwMDAwMCBuIAowMDAwMDgzNjI2IDAwMDAwIG4gCjAwMDAwODQ0MTEgMDAwMDAg - biAKMDAwMDA4NDc1NyAwMDAwMCBuIAowMDAwMDg1NTA0IDAwMDAwIG4gCjAwMDAwODYy - NTEgMDAwMDAgbiAKMDAwMDA4NjM1NCAwMDAwMCBuIAowMDAwMDg2NDE4IDAwMDAwIG4g - CjAwMDAwOTQwNzYgMDAwMDAgbiAKMDAwMDA5NDA5NyAwMDAwMCBuIAowMDAwMDk0MzMz - IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgOTQgL1Jvb3QgODkgMCBSIC9JbmZvIDEg - MCBSIC9JRCBbIDxmZmVmM2YxZDVmY2QyMzFjMWY5NDVjMjYwMTQ5MGE0Nz4KPGZmZWYz - ZjFkNWZjZDIzMWMxZjk0NWMyNjAxNDkwYTQ3PiBdID4+CnN0YXJ0eHJlZgo5NTE3Mwol - JUVPRgoxIDAgb2JqCjw8L0F1dGhvciAoQXJ2aWQgTm9yYmVyZykvQ3JlYXRpb25EYXRl - IChEOjIwMDkwNTI1MDIwMDAwWikvQ3JlYXRvciAoT21uaUdyYWZmbGUgNS4xLjEpL01v - ZERhdGUgKEQ6MjAwOTA1MjUwMjIwMDBaKS9Qcm9kdWNlciAoTWFjIE9TIFggMTAuNS43 - IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAocmVhZF9kaXNrX2J1ZmZlcnMuZ3JhZmZs - ZSk+PgplbmRvYmoKeHJlZgoxIDEKMDAwMDA5NzIxMSAwMDAwMCBuIAp0cmFpbGVyCjw8 - L0lEIFs8ZmZlZjNmMWQ1ZmNkMjMxYzFmOTQ1YzI2MDE0OTBhNDc+IDxmZmVmM2YxZDVm - Y2QyMzFjMWY5NDVjMjYwMTQ5MGE0Nz5dIC9JbmZvIDEgMCBSIC9QcmV2IDk1MTczIC9S - b290IDg5IDAgUiAvU2l6ZSA5ND4+CnN0YXJ0eHJlZgo5NzQyMgolJUVPRgo= - - QuickLookThumbnail - - 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 - FjoXtRkrGqAcGR2QHwggfSHvI2Mk1CZGJ7IpICqNK/YtYC7FMCwxjjLvNE41qTcCOFs5 - sDsEPFU9pD7wQDtBg0LHRAxFTkaNR8tJBkpBS3lMsE3lTxlQTFF6UqpT2FUEVjBXW1iC - Wala0Fv2XRtePV9hYIFhoWLAY95k+2YYZzNoTmloan9rlmytbcJu12/rcP1yD3MfdC91 - PnZLd1h4ZHluenh7gHyIfY9+lH+ZgJyBnoKgg6GEoYWghp6Hm4iYiZSKj4uJjIONfI5z - j2uQYpFYkk6TQ5Q4lSyWIJcUmAeY+ZnqmtybzZy+na2enZ+LoHqhZ6JVo0KkLqUapgam - 8afbqMWpr6qYq4CsaK1QrjevHbADsOixzbKys5a0ebVctj+3IbgCuOS5xLqku4S8Y71B - vh++/b/ZwLbBkcJrw0XEH8T3xc/Gpsd8yFLJJsn6ys3LnsxuzT7ODM7az6bQcdE70gTS - zdOT1FjVHdXg1qLXYtgi2OHZntpa2xbb0NyJ3UHd+d6v32TgGeDN4YDiMuLk45XkReT2 - 5aXmVOcE57PoYukR6cDqcOsg69Dsge0y7eTumO9M8AHwt/Fu8ify4fOc9Fn1F/XW9pf3 - Wvgd+OL5qPpw+zj8AvzL/Zb+Yf8v//8AAAFbAvMEWwXYB1YI0gpIC74NNA6rECERlhMS - FIQV+xduGOQaVBvGHToerCAcIYoi+iRmJdAnOyiiKggrbSzPLjIvkTDuMkYznzT2Nkk3 - mzjpOjY7fjzGPgo/TUCOQcxDCURDRXtGsEflSRhKSUt4TKZN0079UCdRUVJ4U51UwlXn - VwpYLVlPWm5bjlytXcte6GAFYSFiO2NWZG5lh2aeZ7Voy2nfavNsB20ZbipvO3BKcVhy - ZnNzdH91inaUd5x4pHmrerF7t3y7fb9+wX/CgMOBw4LDg8GEv4W9hrmHtIiviaqKpYue - jJeNkI6Ij3+QdpFskmOTWZROlUOWOJctmCKZFpoJmv2b8JzindSexp+4oKqhm6KMo3yk - baVcpkynO6gqqRqqCKr2q+Ss0q2/rqyvmrCGsXOyX7NLtDe1IrYNtvm347jOubi6oruM - vHW9Xr5Hvy/AF8D+webCzcOzxJnFfsZjx0fIK8kOyfHK08u0zJXNdM5TzzLQENDs0cjS - o9N+1FjVMNYI1uDXttiL2V/aM9sG29jcqd153knfF9/l4LLhfuJJ4xTj3uSn5XDmN+b+ - 58ToiOlM6hDq0euS7FLtEe3O7orvRe/+8LXxavIe8tDzgPQu9Nr1g/Yq9s/3cfgS+K/5 - Svnk+nr7EPui/DT8w/1R/d/+av71/3r//wAAAQMCLAM/BFUFaAZ+B5YIqQm9CtYL6w0C - DhQPJxA8EVESYhN1FIcVmRarF7oYyhnZGucb9h0CHgofFiAfISUiKyMuJDAlMSYxJy4o - KikmKh0rEywILPot6y7bL8cwsjGcMoUzbDRSNTY2Fzb4N9g4uDmVOnI7TjwqPQU93j64 - P5FAaUFCQhpC8kPKRKJFekZSRypIAkjaSbFKiktjTDtNFE3tTsZPoFB5UVNSLVMGU+BU - uVWUVm5XSFgiWPxZ1lqwW4pcZV09Xhde8V/KYKNhfGJWYy5kB2TfZbdmjmdmaD1pFGnr - asJrmGxubURuGW7ub8RwmHFtckBzFHPodLt1jXZgdzJ4BHjWeah6eXtKfBt87H28fo1/ - XYAugP6Bz4Kfg3CEQYUSheOGtIeHiFiJKon9itGLpIx4jUyOIY72j8yQopF5klCTKJQA - lNmVspaMl2aYQZkcmfia1ZuxnI+dbZ5LnyqgCqDqocyiraOPpHKlVqY6px+oBajrqdOq - u6ukrI6tea5lr1KwQLEvsh+zEbQEtPi17rblt92417nTutC70LzSvda+3L/lwPHB/8MQ - xCXFPcZZx3jInMnEyvPMJc1ezpzP4NEr0n3T1dU41qDYEdmL2w3cl94s38nhb+Mf5Nfm - muhk6jfsEe3z793xzfPF9cP3vfnA+8r93P//AABuZGluAAAAAAAABjYAAJcQAABXJAAA - U6IAAIeaAAAoVQAAFqgAAFANAABUOQACWZkAAl64AAFmZgADAQAAAgAAAAIABQALABIA - GwAmADIAQABPAF8AcQCEAJkArwDGAN4A+AETAS8BTQFrAYsBrAHOAfICFwI9AmQCjAK2 - AuADDAM5A2gDlwPIA/oELQRiBJgEzwUHBUEFfAW4BfYGNQZ1BrcG+gc/B4UHzQgWCGEI - rQj7CUoJmwnuCkIKmArwC0kLpAwBDGAMwA0iDYYN7A5TDr0PKA+VEAQQdBDnEVsR0RJK - EsQTPxO9FD0UvhVBFccWThbXF2IX7hh9GQ4ZoBo1GssbZBv+HJsdOR3ZHnwfIB/GIG8h - GiHGInUjJiPZJI4lRiX/JrsneSg5KPwpwSqIK1IsHiztLb4ukS9nMD8xGjH4MtgzujSf - NYc2cTdeOE05PzozOyo8Iz0fPh4/HkAiQShCMEM7REhFWEZqR35IlUmvSstL6U0KTi5P - VFB9UahS1lQGVTlWb1enWOJaIFtgXKNd6V8yYH1hy2McZHBlx2cgaHxp3Gs+bKNuC292 - cONyVHPIdT92uXg3ebd7O3zCfk1/3IFugwSEnoY8h96JhIsvjN+Ok5BNkguTz5WYl2eZ - PJsWnPee3qDMosCku6a8qMWq1KzrrwixLbNYtYq3w7oDvEm+lMDlwzzFl8f1yljMvM8j - 0YrT8tZa2MDbJN2G3+biQuSb5vHpQ+uS7d/wKfJw9Lb2+fk/+4P9xf//AAAAAgAFAAsA - EwAcACcAMwBBAFAAYQBzAIcAnACyAMoA4wD9ARkBNQFTAXMBkwG1AdgB/AIiAkkCcQKa - AsQC8AMdA0sDewOrA90EEQRFBHsEsgTrBSUFYAWdBdsGGgZbBp0G4QcnB24HtggACEwI - mQjoCTkJiwngCjUKjQrnC0ILnwv+DF8MwQ0mDYwN9Q5fDssPOQ+pEBsQjxEEEXwR9hJx - Eu4TbRPvFHIU9hV9FgYWkBccF6sYOxjNGWEZ9xqOGygbwxxhHQAdoh5FHusfkiA7IOch - lSJEIvYjqiRgJRgl0yaQJ08oECjTKZkqYSssK/ksyC2aLm4vRDAdMPkx1zK3M5o0fzVn - NlE3PTgsOR46EjsIPAA8+z35Pvg/+kD+QgVDDkQZRSZGNkdISFxJckqKS6VMwk3hTwNQ - J1FNUnVTn1TMVftXLVhgWZZazlwJXUVehF/GYQliT2OXZOJmLmd9aM5qImt3bM9uKm+G - cOVyRnOqdRB2eHfjeVB6wHwzfah/H4CZgheDloUZhp+IKIm0i0OM1Y5rkASRoZNBlOWW - jZg5membnJ1UnxCg0KKVpF6mK6f9qdSrr62Pr3OxXbNLtT63NbkyuzO9Or9GwVfDbsWL - x6/J2cwKzkPQhNLO1STXhNnw3Gre8+GN5Djm9unK7LPvs/LJ9ff5MvyC//8AAAADAAkA - EwAgADAAQwBYAHAAiwCoAMcA6QENATQBXQGIAbUB5QIXAksCggK6AvUDMwNyA7QD+AQ+ - BIcE0wUgBXAFwwYYBnAGywcoB4gH6whRCLkJJQmUCgYKfAr0C3AL8AxzDPkNgw4QDqEP - Ng/OEGkRCBGrElES+hOmFFYVCRW/FngXNBfyGLQZeBo/Gwgb1ByjHXQeRx8dH/Ug0CGs - IowjbSRRJTgmIScMJ/ko6incKtErySzDLb8uvy/AMMUxzDLWM+I08TYDNxg4MDlKOmc7 - hzyqPc8++EAjQVJCg0O3RO5GKUdmSKZJ6kswTHpNxk8VUGdRvVMUVG9VzVctWI9Z9Ftc - XMVeMV+fYQ9igGP0ZWlm32hYadFrTGzJbkdvxnFGcsd0SnXOd1N42Xpge+h9cX77gIeC - E4OghS+GvohOid6LcI0CjpWQKJG8k1GU5pZ7mBCZpps8nNGeZ5/9oZKjJ6S8plCn5Kl2 - qwisma4pr7ixRrLStFy15LdruO+6cbvxvW6+6MBfwdPDRMSxxhvHgcjiykDLmszvzkHP - jtDW0hvTW9SY1c7XAtgx2V3ag9un3Mfd49784BHhIuIx4zzkReVL5k3nTuhL6UfqQOs4 - 7C3tIO4S7wHv8fDd8cnys/Od9IT1a/ZQ9zX4HfkE+ej6zPuv/JD9b/5O/yb//wAAZGVz - YwAAAAAAAAAKQ29sb3IgTENEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1sdWMA - AAAAAAAADwAAAAxuYk5PAAAAEgAAAMRzdlNFAAAAEAAAANZmaUZJAAAAEAAAAOZkYURL - AAAAHAAAAPZ6aENOAAAADAAAARJmckZSAAAAFgAAAR5qYUpQAAAADgAAATRlblVTAAAA - EgAAAUJwdEJSAAAAGAAAAVRlc0VTAAAAEgAAAWx6aFRXAAAADgAAAX5rb0tSAAAADAAA - AYxkZURFAAAAEAAAAZhubE5MAAAAFgAAAahpdElUAAAAFAAAAb4ARgBhAHIAZwBlAC0A - TABDAEQARgDkAHIAZwAtAEwAQwBEAFYA5AByAGkALQBMAEMARABMAEMARAAtAGYAYQBy - AHYAZQBzAGsA5gByAG1faYJyACAATABDAEQATABDAEQAIABjAG8AdQBsAGUAdQByMKsw - 6TD8ACAATABDAEQAQwBvAGwAbwByACAATABDAEQATABDAEQAIABjAG8AbABvAHIAaQBk - AG8ATABDAEQAIABjAG8AbABvAHJfaYJybbJmdphveTpWaM7st+wAIABMAEMARABGAGEA - cgBiAC0ATABDAEQASwBsAGUAdQByAGUAbgAtAEwAQwBEAEwAQwBEACAAYwBvAGwAbwBy - AGkAAG1tb2QAAAAAAAAGEAAAnF4AAAAAwCagAAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAA - AENvcHlyaWdodCBBcHBsZSwgSW5jLiwgMjAwOQA= - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - FitInWindow - - Frame - {{28, 8}, {990, 770}} - ListView - - OutlineWidth - 142 - RightSidebar - - Sidebar - - SidebarWidth - 138 - VisibleRegion - {{-131.785, 1.10725}, {1096.18, 731.893}} - Zoom - 0.90313780307769775 - ZoomValues - - - Canvas 1 - 0.0 - 1 - - - - saveQuickLookFiles - YES - - diff --git a/libtorrent_utp/docs/read_disk_buffers.png b/libtorrent_utp/docs/read_disk_buffers.png deleted file mode 100644 index 119cb97f272ac630311112fc6e625483dfc7a4fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50638 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 - - - - - -libtorrent manual - - - - - - - -
-
- - -
-

libtorrent manual

- --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
-
-

Table of contents

- -
-
-

running and building tests

-

Some of the tests of libtorrent are not self contained. For instance, in -order to test the http_connection class in libtorrent, the test requires -lighty. This document outlines the requirements of the tests as well as -describes how to set up your environment to be able to run them.

-
-
-

lighty

-

Download lighty. I've tested with lighttpd-1.4.19. If libtorrent is built -with SSL support (which it is by default), lighty needs SSL support as well.

-

To build lighty with SSL support do:

-
-./configure --with-openssl
-
-

Followed by:

-
-sudo make install
-
-

Make sure you have SSL support in lighty by running:

-
-lighttpd -V
-
-

Which gives you a list of all enabled features.

-
-
-

delegate

-

Delegate can act as many different proxies, which makes it a convenient -tool to use to test libtorrent's support for SOCKS4, SOCKS5, HTTPS and -HTTP proxies.

-

You can download prebuilt binaries for the most common platforms on -deletate's download page. Make sure to name the executable delegated -and put it in a place where a shell can pick it up, in its PATH. For -instance /bin.

-
-
-

OpenSSL

-

In order to create an SSL certificate for lighty, openssl is used. More -specifically, the following command is issued by the test to create the -certificate file:

-
-echo -e "AU\ntest province\ntest city\ntest company\ntest department\n\
-        tester\ntest@test.com" | openssl req -new -x509 -keyout server.pem \
-        -out server.pem -days 365 -nodes
-
-

This will write server.pem which is referenced in the lighty -confiuration file.

-

OpenSSL comes installed with most Linux and BSD distros, including Mac OS X. -You can download it from the openssl homepage.

-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/running_tests.rst b/libtorrent_utp/docs/running_tests.rst deleted file mode 100644 index 550ecd886..000000000 --- a/libtorrent_utp/docs/running_tests.rst +++ /dev/null @@ -1,75 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -running and building tests -========================== - -Some of the tests of libtorrent are not self contained. For instance, in -order to test the ``http_connection`` class in libtorrent, the test requires -lighty_. This document outlines the requirements of the tests as well as -describes how to set up your environment to be able to run them. - -.. _lighty: http://www.lighttpd.net - -lighty -====== - -Download lighty_. I've tested with ``lighttpd-1.4.19``. If libtorrent is built -with SSL support (which it is by default), lighty needs SSL support as well. - -To build lighty with SSL support do:: - - ./configure --with-openssl - -Followed by:: - - sudo make install - -Make sure you have SSL support in lighty by running:: - - lighttpd -V - -Which gives you a list of all enabled features. - -delegate -======== - -Delegate_ can act as many different proxies, which makes it a convenient -tool to use to test libtorrent's support for SOCKS4, SOCKS5, HTTPS and -HTTP proxies. - -.. _Delegate: http://www.delegate.org - -You can download prebuilt binaries for the most common platforms on -`deletate's download page`_. Make sure to name the executable ``delegated`` -and put it in a place where a shell can pick it up, in its ``PATH``. For -instance ``/bin``. - -.. _`deletate's download page`: http://www.delegate.org/delegate/download/ - -OpenSSL -======= - -In order to create an SSL certificate for lighty_, openssl is used. More -specifically, the following command is issued by the test to create the -certificate file:: - - echo -e "AU\ntest province\ntest city\ntest company\ntest department\n\ - tester\ntest@test.com" | openssl req -new -x509 -keyout server.pem \ - -out server.pem -days 365 -nodes - -This will write ``server.pem`` which is referenced in the lighty -confiuration file. - -OpenSSL comes installed with most Linux and BSD distros, including Mac OS X. -You can download it from `the openssl homepage`_. - -.. _`the openssl homepage`: http://www.openssl.org/ - diff --git a/libtorrent_utp/docs/session_stats_peers.png b/libtorrent_utp/docs/session_stats_peers.png deleted file mode 100644 index e134f310f3b8f04a15c4408702cd0739b0740e23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3902 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 - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 137.11.0.108132 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {576, 733}} - Class - SolidGraphic - ID - 2 - Style - - fill - - GradientColor - - w - 0.666667 - - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - CanvasOrigin - {0, 0} - CanvasSize - {576, 733} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2009-05-24 18:38:49 -0700 - Creator - Arvid Norberg - DisplayScale - 1.000 cm = 1.000 cm - FileType - flat - GraphDocumentVersion - 6 - GraphicsList - - - Bounds - {{113, 177.5}, {103, 28}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Font - Helvetica - Size - 36 - - ID - 45 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 storage_interface\ -customization point} - VerticalPad - 0 - - Wrap - NO - - - Class - LineGraphic - ID - 43 - Points - - {6.4195, 204.5} - {179.999, 204.5} - - Style - - stroke - - HeadArrow - 0 - Pattern - 1 - TailArrow - 0 - - - - - Bounds - {{63.5, 67.5}, {53, 40}} - Class - ShapedGraphic - Head - - ID - 1 - - ID - 32 - Rotation - 90 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 31 - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{6.4195, 6.5}, {167.161, 54}} - Class - ShapedGraphic - ID - 31 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 disk_io_thread -\fs24 \ -manages piece cache} - - - - Bounds - {{58.75, 180.25}, {62.5, 40}} - Class - ShapedGraphic - Head - - ID - 3 - - ID - 30 - Rotation - 90 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 1 - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{6.4195, 232}, {167.161, 72}} - Class - ShapedGraphic - ID - 3 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 storage -\fs24 \ -maps slots to file and offset\ -reads and writes to and from disk} - - - - Bounds - {{6.4195, 114.5}, {167.161, 54}} - Class - ShapedGraphic - ID - 1 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 piece_manager -\fs24 \ -maps pieces to slots} - - - - GridInfo - - SnapsToGrid - YES - - GuidesLocked - NO - GuidesVisible - YES - HPages - 1 - ImageCounter - 3 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - AutoLayout - 2 - LineLength - 0.4643835723400116 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2009-05-24 19:23:30 -0700 - Modifier - Arvid Norberg - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - OutlineStyle - Basic - PageBreaks - NO - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {612, 792} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - QuickLookPreview - - JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGlVk2P2zYQvetXzDE5mOZQJCVeu2mB - 5NTUBnoIgoWglWMllrWRlBbIj81v6QxJfdnebRax1vAsRT5q3rx51Fd4D19B0mUyC1ma - QlfB33AGKaz0H6hhe9cjlD2gv/oSNlKYeHeOIkBCAAfY/ll1ZfU4fCtO0NW0BTreRALt - stHC5FK7HIx2wkDZwPZtg/Cm9Q8zznR6MVOjTsI8Nc6zygPmBowDnecRJx3vjzirHa3J - hIlIepwZkTKPZKWNSCbc3/5VnYqh/qe6a09tVzfV0NUlJ2WFRmekAmMpI0CbCbRISQUK - EN4RX58De3c7/7ASdndMhv9nwz9MOSU2YSFeY+2IF6qB4hpIIP4lFWWBQtQah4HJ3/aA - eYTPYWOVkPSUjkb2TbL9A4Wk3fcHePVYV2V13xTn4lPVvYb9Z/h9H/iPq+ESNvC9QQUb - o0VqMq0ATbKn8i1wm+KxBw/ew9BCf2qHfoZ/TlbJRIJWbqIzUyzJwzLfa9ZUSo8S+fcL - kp/gTFuvvQvGUikkSYfEh5o4C7klgbN+aDuia05nXYQF4IKrTAnjMmuZ9zWcp8oTxEz9 - eBFsJogkS6rHywKcKijOD9AeXoRnUqGMybNrvENfDS+C2mRWyDRDvKGOrioeev98/3b1 - UHmJ8NMeuraB53dJuFALXpG0ZJ1TQGIZiY3afqj7LzMYO1xOLWI1NIDSR8kpRIrbhmKk - 0Riz7GUYzubRzAnHn2naMWImjfdLy05G60Aj2JTi6KNXRihJWPwnfX+lSpDtsLPtjpj0 - R+rAJwx5tJOrDmJrETZ30igyWCFVhpr8NXnSa4gLtC5ywVHgQuHMxRgTFyqlYaKLuBhH - Jy7GAc9FwIx9tz4vpsa23AxTp45OGU6G/3NGu1rJ/K56PLnpiyQQWkb8XnS5yYVJnTHe - F1f+xdq5r9v74chafZ3cNsYV7kKUhEvKl2Rf5lKUwWqjN0JZlMdqRo8aJX5iXTgKdbGp - rxApNERUE4s8RBUJI1RQCqhGvg4Bg89wr0mLlCqr0uif06TVJh6Ru6OCX5ZkMkuSyH6i - yJwBjpljTJxeAEJn+sDJhM7GkLYfoDX+NyTNq1kPH0DT9ZG2eqAvXze25BeASZNK8olL - zhBkbSAMnNbyunHsoj/2jeb+iApbKEFbkVqVwia9FEI8Re7r81B1h6K8fZ48j26Q0GVK - vTSfKtH8ym+0QVN/p3eW9gyPLW0zeWHy/j+HvTh4CmVuZHN0cmVhbQplbmRvYmoKNSAw - IG9iago5MjEKZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAzIDAg - UiAvUmVzb3VyY2VzIDYgMCBSIC9Db250ZW50cyA0IDAgUiAvTWVkaWFCb3ggWzAgMCA1 - NzYgNzMzXQo+PgplbmRvYmoKNiAwIG9iago8PCAvUHJvY1NldCBbIC9QREYgL1RleHQg - L0ltYWdlQiAvSW1hZ2VDIC9JbWFnZUkgXSAvQ29sb3JTcGFjZSA8PCAvQ3MyIDE4IDAg - UgovQ3MxIDcgMCBSID4+IC9Gb250IDw8IC9GMS4wIDE5IDAgUiA+PiAvWE9iamVjdCA8 - PCAvSW0yIDEwIDAgUiAvSW00IDE0IDAgUgovSW01IDE2IDAgUiAvSW0xIDggMCBSIC9J - bTMgMTIgMCBSID4+IC9TaGFkaW5nIDw8IC9TaDEgMjAgMCBSIC9TaDIgMjEgMCBSCj4+ - ID4+CmVuZG9iagoyMCAwIG9iago8PCAvQ29sb3JTcGFjZSA3IDAgUiAvU2hhZGluZ1R5 - cGUgMiAvQ29vcmRzIFsgMzEuNzUgLTIwLjUgMzEuNzUgMjAuNTAwMDEgXQovRG9tYWlu - IFsgMCAxIF0gL0V4dGVuZCBbIGZhbHNlIGZhbHNlIF0gL0Z1bmN0aW9uIDIyIDAgUiA+ - PgplbmRvYmoKMjEgMCBvYmoKPDwgL0NvbG9yU3BhY2UgNyAwIFIgL1NoYWRpbmdUeXBl - IDIgL0Nvb3JkcyBbIDI3IC0yMC41IDI3IDIwLjUwMDAxIF0gL0RvbWFpbgpbIDAgMSBd - IC9FeHRlbmQgWyBmYWxzZSBmYWxzZSBdIC9GdW5jdGlvbiAyMyAwIFIgPj4KZW5kb2Jq - CjEwIDAgb2JqCjw8IC9MZW5ndGggMTEgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBl - IC9JbWFnZSAvV2lkdGggMzgwIC9IZWlnaHQgMTg4IC9Db2xvclNwYWNlCjI0IDAgUiAv - U01hc2sgMjUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNv - ZGUgPj4Kc3RyZWFtCngB7dABDQAAAMKg909tDjeIQGHAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBv4GBkVd - AAEKZW5kc3RyZWFtCmVuZG9iagoxMSAwIG9iago5NTgKZW5kb2JqCjE0IDAgb2JqCjw8 - IC9MZW5ndGggMTUgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lk - dGggMzgwIC9IZWlnaHQgMTUyIC9Db2xvclNwYWNlCjI3IDAgUiAvU01hc2sgMjggMCBS - IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFt - CngB7dAxAQAAAMKg9U9tDQ+IQGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgy8Dwyk/gABCmVuZHN0cmVhbQplbmRvYmoKMTUgMCBvYmoKNzc5CmVuZG9i - agoxNiAwIG9iago8PCAvTGVuZ3RoIDE3IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlw - ZSAvSW1hZ2UgL1dpZHRoIDEyNCAvSGVpZ2h0IDE1MCAvQ29sb3JTcGFjZQozMCAwIFIg - L1NNYXNrIDMxIDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVj - b2RlID4+CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQlPiEBhwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGPgMDNn4AAEKZW5kc3RyZWFtCmVuZG9iagoxNyAwIG9i - agoyNjcKZW5kb2JqCjggMCBvYmoKPDwgL0xlbmd0aCA5IDAgUiAvVHlwZSAvWE9iamVj - dCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDM4MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFj - ZQoyNyAwIFIgL1NNYXNrIDMzIDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIg - L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0PiEBhwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMvA8MpP4AAQplbmRzdHJlYW0KZW5k - b2JqCjkgMCBvYmoKNzc5CmVuZG9iagoxMiAwIG9iago8PCAvTGVuZ3RoIDEzIDAgUiAv - VHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDEyNCAvSGVpZ2h0IDE3 - MCAvQ29sb3JTcGFjZQozNSAwIFIgL1NNYXNrIDM2IDAgUiAvQml0c1BlckNvbXBvbmVu - dCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QgQAAAADDoPlT3+AE - hVBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABA29g9wgAAQplbmRzdHJlYW0KZW5kb2JqCjEzIDAg - b2JqCjI5OQplbmRvYmoKMzEgMCBvYmoKPDwgL0xlbmd0aCAzMiAwIFIgL1R5cGUgL1hP - YmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAxMjQgL0hlaWdodCAxNTAgL0NvbG9y - U3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0 - ZURlY29kZSA+PgpzdHJlYW0KeAHtm9dWYt0ShTuYExJEyTkLiAQREBGRZMQc29B2v/8j - nFm1NpuNgiLQZ/wXrIseijQfFVbYNWt9+zYaIw+MPPDf8MD3Hsewv62E/cHjZ8ch/ia9 - cXh8+kB8NCPHxBhvG9KL/Aa8kd4/HLog43PHxgg4QWPyzeAX6a9jY3jj0OhsswAzc2pq - arrDwMv4PhMTAs/0gU0nNJNhK0FnZmZpzLUNfmlmBn/GF5hg48nxA8Il9DiTgZ2bn59f - WFC9GwsL+MPc7OyMRIfjB4W30FPTBAZ1Ua1WazQarWLgV7y4qFItEB50mD44nB0+Pj45 - OT0zC/CiGkzd0tKSXq9flgd+wUs6rVYD/sI8bJ+cHB9n+ABeF7EeR6Bn5xZUAOuW9Msr - BoPRaDQpBn41GFaW9eBr1KqFOTJdsrx/OLHHJPSiRrsErtFktlistjfDarGYTUbwl7Sa - RRmOkA/AJvTE5DT8vUhkowlYh8PpcrnbhsvldDhsVovJSPRF+H2aY/6jfzabDfTM3IJa - u7RiNAPscnu8Pr8/0Db8fp/X43YBbzauLGnVC3OI+cQY5Xq/hn//zh4HGlYbTBa70+31 - BYKh1XAk2jYi4dVQMODzup12i8lAls/NcMgHYMPl7HGgV8w2h9sXCIWja7H1eCKRVIxE - Ir4eW4uGQwGf22Ezw/JF9joZ3rfdP34i0WbmVZqlFZPN6fGDHIsnU+nNzUwmK49MZnMz - nUrGY6D7PU6baWVJo5onw/tnC5dPzyLYy0aLwxMIRWOJVDqTzeXz2wXF2M7nc9lMOpWI - RUMBj8NiXEbIkW6YZ/06ndgTUzNzZLbZ7vaHouvJdCaXL+wUd0t7ilHaLe4U8rlMOrke - Dfnddnhdo0LEkW0DsMcnpmbnF2G21eljdDZfKJbKlWqtVpdHrVatlEvFQj7LcJ/TCsMR - 8amJ8f7ZFG5yuQ7RdgfCsWQ6l98plav1/YPDI8U4PNivV8ulnXwunYyFA25EXCec3nfA - saiBTS43WBzeUDSxkc0X9yr1g8Pjk0bjVB6Nxsnx4UG9slfMZzcS0ZDXYTGQ0xHwwdgI - t1ZvtLp84VgqA6urdZBPz84vFOP87BT0ehWWZ1KxsM9lNeq1CPhAbKzlMxRucnk0ns4V - SpX64cnp+cXl1bViXF1enJ+eHNYrpUIuHY+S0yngYI/1O8G//6A0n0e4zQ5PcC2ZyRfL - jL68vrm9U4zbm+tLhpeL+UxyLehxmBFwzPCJwdhINRHuWCpbKFUPjk/PL69v7+4ffsnj - 4f7uluDHB9VSIZuKiYAj2YbDdvpW19O5nXLt8ITQ979+PT7J4/HXr3uCnxzWyju59Pqq - z4lkG5yNKYY0t7r84Xg6X6zsH51eEPrx6flFHs9PjwS/OD3arxTz6XjY77Ii0bGyDebz - 6VmVRm+0ufyRxGZ+t3pwcnZ5c//w+PTy8lseLy9Pjw/3N5dnJwfV3fxmIuJ32Yx6jWpI - bHcgkshsl2oHJ+dXMPvp5ferYvx+eYLhV+cnB7XSdiYRQaIPlR1NZgql2mHj4vru4fH5 - 5fX1jzxeX1+eHx/uri8ah7VSIZOkSfaP2X+l8eefsMWSKi0tyWxhr06pdscu/9NE//37 - 55WcDrtPj+p7hSzbjcVFLKp9Hh6+j9hYzkc+H+WamGejOTZaW0Zran8PoqO9hB4NRnvJ - aC8Z7SWjc+rofM7FntFzyZc209EeOtpD/0/P/lzKFSVNuycYRWmvt7pDKhr02JtFzX6K - uRAjuXI/K5U0Q2upXG/sXGotJBU1qbCICvpX6vf0bkmVa1WwYxsoK6LecnnTrd5yc4l6 - CwqLG1JRU1SxWaiiz+thetPbhMiPIjJEORYNXFTS3CqW94/BFuW1d7UeFPdOj/fLxS0q - aqKKzdIBaybQ4kkU/hRPb+HuAoj8kIjmVGrWSgJSSfOYyoqdz2tUWDyWipoB1kzUpJmg - jt1jK4BEpu6CCUKzRGR1eINRlBVFSVMqK763+1YUNVFYjAa9DjKc9EESpIUY/4kg3kQT - maVI0gPNdpd/NYbjOcqKrZJmBzaKmlRYzCZjq36X3SzpgxCkiS51QXSNuoxmlR+yM+mB - JqvTE4zERao1zqmKTOXUDuzr84ZItngk6HFaIdGROEmCNAuzogWjG5zY6G0gd5PWrgKa - RDm3f3WNqsgVpBqXUzuxuaCKZKtQJXlt1e8miQ5wFWnxPXQCKNDoL1BB+wXa7iJlTIgG - VMHmUu47u6mYK6rYJB1AovO57AxXq9CHoIB3sZvNFlazzE+yM9DeIJSxTcyw+mHj/IrS - /Pm9z59Rxb65Om8c1jHLNqGSBb0EJ0FaagSgHoju6SaZTR0G0NrVWv0KZGeXNxBegzK2 - XaqyyyncXdgkHRzvV0vbUMnWwgGCm1b0WjVr8Z90IRBbyPzIMiU6DmWMJaKzS6qec+W+ - LdeokEwBxywjw6GSxdvhrMULwzs7XbAxrZFmb9BbQiK6INGgG5ukAxhOYtHWGzglHBve - rRGAXS66GyjNZIfHU5tbhd1K/aghJCJ2efscg90UcBKLMM0qu4WtTYXlFHPRAdG170Q2 - W5rXFofLFwyvweFAkyh3JklE0GkgWYjnQPoXwsFv0mtYLGKvA86WB30uB2KuaELoYrjE - nobOr9YtG8w2TjMJXYMod3GFCSYy7S1bGI5pxl6vlZtwJJzNbFjWwXB0X3QPeJONaGuW - lk1YU4Tkndsulqv7TY+TMkYiUbvdzGbDyev71XJxG3o0TXOsMaZl6kEQfQCf2M1sXkox - u+IbkvjLHhdms8uVbOF0RPwB4uQFeZ0l4Q1KdrG4fpFtc2JmxxLp3PZumTx+fnnDSS6Z - /YYtG37Dwii8DsMTEOK9zd6LD+3+xtNb0dfhDUawoDWFZyRay2xSx5rJhh8RA2Rb0/Cm - HI3lLRKU2c1J1n1+o8GBDgy6ZWxf0mJKovcRVnLsYFhXYDaHW4aTREdsmmaQJjHPzk4w - zzDJxdKKDY1yjVovqA2gy9mJk416aeSmElrIyW5kGrNZg5XZbDmhW2ye42fINpLhia1s - OfmoyaiV6ItavcGCrhLsnRD7aV0Rdr9hA87ot2yyexctALyX2i0Gvejz6T7Fvn0j9k/h - dG7koc0zTv0VJHuz9tzuc8Fts5t8foF417CoZzfitJWKNh/h8u4+Zzg3MLU5fQsNFpTn - zZVFTO8WGD8pcg0bKa/pOxzuXl0uDOfjqdTBJBmOSUYrKuv9bcnW5MupJsJNe9kuNlKF - 2dzd9GFPGR8eeDfBSQ0RRwsTz3BsYryuSUsqG06zTBpAy1MM05vWNWr4oNmN5iaKNjU3 - iSzvkuaYeFK2KadZZJ0277229bwd3oYW6zm6bDKpdZrc8gT7YDEXM146NdHzCDdQ2bqk - mxIuo3lyv000bqtqreXdzZZTneY4eR1Hc5HripC3cp3dzmjluqIMNg7pLY9/kORNy/nc - NDVNjyStSZ5VwFuTnLgYYklryzPF1MbDyTSWtA8TTVpnRchpZe0V/imaVtNe0M18+xwu - Yi5luNi6xZllO9tude/or8CxqcBo2kM+cPhX0F+AY0t7GS66JzhlO7cVPbd2TpHh/Tu8 - Pds/SDgcGkEHGSclsWkPB92L5TjDPKKX6/GRD2nigQSL+KBWk+2fTLWL6xuphQ3Nazfi - fEj7xzDQn8HprI4GOoy7WzqTS1vXcNCfwvF8dHOLIbcLDs3qT9yOXa1xxg2TaJM8axwf - 0Jl4SA7/INtxiMJzSu3giBpFMdAeegR0kc4KVOrg4xkXmHpdSAXs7b+dEk6c4PbQpnqE - Blm0xR7hKYS7YuPRIaI7xpyOj/SYVKlxYzDagWsV6omlE9LwrO4Sc8D5qXivUqVu6Gpl - Tzx0DhvdyXI6TKQ20RSNPvByea+0ixZoPOnTI+ewYt2M/fuY+0IRuQ9d7juP/AN0B8up - GhGLo/8+l9vayqHjPRVHSYvqaTggDSHDm1Z3iDnXI1bR/Z/cSGNsJNHpvyrqC0NHv7Pc - 3Lz1QNctcMFCuuFAx8IhW/3Gcjq9mqwOl9ePWyZ0xwQ3S/xel8Nq4hMpP3b1djZTuvaj - nxUJx6Vli03ccgkGxY0WW6uYNNhq1ulLyHCc21EIgul8u8fjETd5TAaUc/Dk87WzWSdQ - p9eacHpiUev0dKvJZrM7HHYbbDau6KmKhXLx8K2mL9MG1+r0y3SbCwM3t5b1OlE5/Edo - GY7HJbrGJm6xrayIG2uiTo1HzeGmWSsAkuXS9T2+vafT8U096aLev0O3LKdri81bi+KG - onxBsWsdqWVCnz+R5T/HJTkFtzUXMOhmphBEBr6d+PG3YjiV9+maqrikCnBLCOpWPfv4 - U3v8K+B4RmblblLczaWruEJ9I+Gxx4/p721cmSAdC66H+eACjPQe/CpqD9+H4Ig6jOdb - 0aQ3ynpnD/99sLcALuHxDcToTeYdDCv9b6aTTi0N8ftQPrqXDxG41r+9/J/hvofYw/3E - 0aeNPDDywMgDg3vgf1KX+AYKZW5kc3RyZWFtCmVuZG9iagozMiAwIG9iagozMzY1CmVu - ZG9iagoyOCAwIG9iago8PCAvTGVuZ3RoIDI5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3Vi - dHlwZSAvSW1hZ2UgL1dpZHRoIDM4MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQovRGV2 - aWNlR3JheSAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+ - CnN0cmVhbQp4Ae3d21dT97YHcCWBkAu5QEhCLiQkEFgECIFg5CZBMHKLYIQSRRCM0oJB - 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 - YmoKMjkgMCBvYmoKMzQ5NgplbmRvYmoKMjUgMCBvYmoKPDwgL0xlbmd0aCAyNiAwIFIg - L1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAzODAgL0hlaWdodCAx - ODggL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt2P07m+m2B/CWEJGIhEjIi0RCeCRE - iKbeKkrVa6kytEppWjM0qrUnZVerVNtMtapMbdQU7Xi5aA0uu0xP6zr/2lkPPdc+u9LO - LHP/dPb6/tDLD8tKr8+zrPvOc+QIhQRIgARIgARIgAT+0wSOUhgIIKcGPjHgXwmkHE7g - X4QA+qcewb47fJpAEET5ywICgSAwkH8Kf+y/J7/PHiwUhuxHREELfKYTCoOD4fkJPvN/ - Y/o/ywcFBQO7KDRULBZLJJIwyiEEAA74xKGikBAh8O9N/zdWD08fCJsG4IFdEiaVhstk - csohBWSycKlUGibh/YF/b/a/tvf36GHmefkwabhcHhGpUERFKZUqCl5AqYxSKCIj5HIZ - zw/6+5vnK/j81PNDz8vLwF2pio5RqzVarY6CFtBqNWp1TLQKnkCEPFwqDg0J2R99v/h7 - 9LDoxRKQB3hQ1+kNhjijiXIIAaMxzqCP1WnU0SpFpFwaBqMP+HDf8XfeHuV3vVAEQy+P - VEarwd1oSjAnJnFccrKFghJITuY4LslsjjfF6XWaGJVCHr6PD/cdP/gw9kAfIg4LlytU - ap3BGG/mkq0pNpvdbk+nIAUALc2WarVwiQkmg04THRUhk/KTDweuX/tAOGZD9+g1emNC - kiXFZs/IPObMyobkUBACvFjWcWemIz0t1cqZTQZtzD6+kN86B5fOURj7kFCJVK6I1uhN - iZZUu8OZlZOX7yooLCwsoqAEgOyky5WXm+3MTLdZuQSDDvBh7cDK9zP4sO1h7MVAr9IY - 4rkUu+N4Tn5BUXFJaXlF5RkKUqCyoqKstPjUSVde9rF0W7I5jseXSkTCr9nDxpEBvT6e - s2U4c12Fp8sqq2rO1X3XQEEL1NfV1lSfKS85VZCX5QB8gzZaIZfC4AsOLnxYOfzYRyjV - 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 - ZLNBG63gx97PuoedExjED74c8A3xXIrdcTwnv6CouKS0vKLyDAUpUFlRUVZafOqkKy/7 - WDrQx+lioiKk+2P/5fWeX/gCGHwJ4Edr9KZES6rd4czKyct3FRQWFhZRUAJAdtLlysvN - dmam26xcgoGnDw+DsYeV48+eH/zQsHB+8vXGhCRLis2ekXnMmZUNyaEgBHixrOPOTEd6 - WqqVM5sMWqCXScWw7QUBB45afu5h8IUh4j18tc5gjDdzydYUm81ut6dTkAKAlmZLtVq4 - xASTQaeJ3qeHbe9v7Hn8QMAXicOk8khltFqnNxhNCebEJI5LTrZQUALJyRzHJZnN8aY4 - vU4To1LIYeGI9jaOn5WzN/iwdWDyJVJZhEKpUmu04G+IM5oohxAwGuMM+lidRh2tUkTK - pfv0sHEObnv+qxZsncAgWDuhMPqyiEjgj45RwxPQ6ihoAa1Wo1bHRKuUUYoIebhUHBoC - Uw8b52v2+/jBIXv64XI5+CuiopRKFQUvoAR1RWSEXC6Thklg3wiDvkEPg8/jC/jRB32x - JEwqDZfJ5JRDCshk4VIpDx8K8vtD/5Wp39s6gL+nD2s/RBQKD0AskUjCKIcQADjg4915 - eJh5ft/4X/af363xow/6MPxw7MID2IuIghb4TCcUBgP83rb5I3r+xOX1AwID9x8A/B7l - rwgIBHsTz8N/c+g/zz7w7/nzzwACj4FyGIF9Pv7fP+n+v/57fwHwO5S/KPB/QOlHEiAB - EiABEiABEviPEfgfA0+JGQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjM4NjUKZW5k - b2JqCjM2IDAgb2JqCjw8IC9MZW5ndGggMzcgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0 - eXBlIC9JbWFnZSAvV2lkdGggMTI0IC9IZWlnaHQgMTcwIC9Db2xvclNwYWNlCi9EZXZp - Y2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K - c3RyZWFtCngB7ZxnUyLdFoUnmBMSRCXnKCASREBERJIRcxjHMOH//4S79j5N0ygoAlN1 - 36o+H6YUGR52OHGv01++qE31gOqB/w8PfO2zjfrbSthv3L53beJv0htHx6cPxEczcky0 - 8Y4mvchvwBvp/aOhCzI+d2yMgBPUJl81fpH+OjaGN46MzjYLMDOnpqamuzS8jO8zMSHw - TB/adEIzGbYSdGZmltpcR+OXZmbwZ3yBCTaeHD8kXEKPMxnYufn5+YUFzZu2sIA/zM3O - zkh0OH5YeBs9NU1gUBe1Wq1Op9MrGn7Fi4sazQLhQYfpw8PZ4ePjk5PTM7MAL2rBNCwt - LRmNxmW54Re8ZNDrdeAvzMP2ycnxcYYP4XUR63EEenZuQQOwYcm4vLK6ajKZzIqGX1dX - V5aN4Ou0moU5Ml2yfHA4scck9KJOvwSuyWyxWm32V81mtVrMJvCX9LpFGY6QD8Em9MTk - NPy9SGSTGVin0+V2ezqa2+1yOu02q9lE9EX4fZpj/m1wNpsN9Mzcgla/tGKyAOz2eH3+ - QCDY0QIBv8/rcQNvMa0s6bULc4j5xBjl+qCGf/3KHgcaVq+arQ6Xx+cPhsJrkWiso0Uj - a+FQ0O/zuBxW8ypZPjfDIR+CDZezx4FesdidHn8wHImtxzcSyWRK0ZLJxEZ8PRYJB/0e - p90CyxfZ62T4wHZ/+45Em5nX6JZWzHaXNwByPJFKZ7a2stmc3LLZra1MOpWIgx7wuuzm - lSWdZp4MH5wtXD49i2Avm6xObzAciyfTmWwuXyjsFBVtp1DI57KZdDIeCwe9TqtpGSFH - uqGfDep0Yk9MzcyR2RaHJxCObaQy2XyhuFvaK+8rWnmvtFss5LOZ1EYsHPA44HWdBhFH - tg3BHp+Ymp1fhNk2l5/RuUKxVK5Ua/V6Q271eq1aKZeKhRzD/S4bDEfEpybGB2dTuMnl - BkTbE4zEU5l8YbdcqTUODo+OFe3o8KBRq5R3C/lMKh4JehBxg3D6wAHHoAY2uXzV6vSF - Y8nNXKG0X20cHp2cNptncms2T0+ODhvV/VIht5mMhX1O6yo5HQEfjo1w640mm9sfiaez - sLrWAPns/OJS0S7Oz0Bv1GB5Nh2P+N02k1GPgA/Fxlg+Q+Eml8cSmXyxXG0cnZ5dXF5d - 3yja9dXlxdnpUaNaLuYziRg5nQIO9tigHfzrN0rzeYTb4vSG1lPZQqnC6Kub27t7Rbu7 - vblieKVUyKbWQ16nBQFHD58Yjo1UE+GOp3PFcu3w5Ozi6ubu/sfDT7k9/Li/I/jJYa1c - zKXjIuBIttGwXf61jUx+t1I/OiX0j58/H5/k9vjz5w+Cnx7VK7v5zMaa34VkG56NLoY0 - t7kDkUSmUKoeHJ9dEvrx6flFbs9PjwS/PDs+qJYKmUQk4LYh0TGyDefz6VmNzmiyuwPR - 5FZhr3Z4en51++Ph8enl5ZfcXl6eHh9+3F6dnx7W9gpbyWjAbTcZdZoRsT3BaDK7U64f - nl5cw+ynl1+/Fe3XyxMMv744PayXd7LJKBJ9pOxYKlss14+alzf3D4/PL79//5Hb798v - z48P9zeXzaN6uZhNUSf7x+y/UvvzT9hiSJWGllSuuN+gVLtnl/9pof/+/fObnA67z44b - +8Uc243BRQyqAy4evqpsDOeqz9VcE/1M7WPq2KKOqYNtRNW5hLYG6lyiziXqXKKuU9X1 - OR/2qPuST02m6hyqzqHq3l8981D3oeo+VN2HfmrqlN+szqHqHKrOoeoc+g/mUC5/izKw - wxuKoRzaX60mHQt5Ha1C8CAFcAi4WO0wK5WBw+vpfH/sfHo9LBWCqRgL1cFnNA/0bknJ - 1K76xzdRikWN6uq2V43q9go1KhRjN6VCsKj8s7iHPk8eqnv/QG8TwkgU3iFkYqGFm8rA - 26XKwQnYoiT5pj6GgujZyUGltE2FYFT+WW7BOhPoF0lI9yGe3sKKTAgjIauZ02hZXxKU - ysAnVIrtfsZFxdgTqRAcZJ2JlnQmqP33KZ+UyKTInCA0y2psTl8ohlKsKANLpdi3dt+J - QjCKsbGQz0mGk6aKRHxCwPiBiLCFJjLLt0hDZXG4A2txHGmiFNsuA3dhoxBMxdhcKr4W - cDsskqYKIj6iS8rRnsGW0ayMhFSPNFRmm8sbiiZEqjUvqPJOJegu7JuLpki2RDTkddkg - ayJBF4n4WMwmZKu94MSGHpTcTfpEDdAkZPIE1tap8l5FqnEJuhubi9BItipV39fXAh6S - NQGuIf1iH+pJBRqaTA30ckA73KQmEkILqvpz+fuN3VQAF5V/kltA1uR3Oxiu1UC7qYD3 - sJvNFlazNJKkekD7QlATbaGHNY6aF9eU5s9vff6Myv/t9UXzqIFetgVlUchHcBLxSeJJ - 0o32TjfJbFJlQp+o1RtXINVz+4KRdaiJdso1djmFuweb5BYnB7XyDpRF65Egwc0rRr2W - 9YsfKDeJLaSRyDIlOgE1Ectqzq9IccBqh45co+I7BRy9jAyHsijRCWf9ojC8u9MFG90a - afYKvS1kNZcktOjFJrkFDCeBzfYrOCUcG95LPMkuF4pQSjPZ4Yn01nZxr9o4bgpZDbu8 - s4/Bbgo4CWzQzap7xe0theUUc6Ea7anVlc2W+rXV6faHIutwONAkZDqXZDXQtkDmoVy3 - gA2ns8CGvQ44Wx7yu52IuUK42cNwiT0NbaTWsLxqsXOaSeg6hEyX1+hgItNes4Xh6Gbs - 9XqlBUfC2S2rywYYDsVq74C32Ii2bmnZjDFFyATzO6VK7aDlcVITkbCm025ms+Hk9YNa - pbQDDR91c4wx5mXSbQrt5Ad2M5uHUvSuxKYkmGOPC7PZ5Uo2FC5wOiL+AEHXJXmdZXSb - lOxicP0k2+5Cz44nM/mdvQp5/OLqlpNcMvsVWzb8lsVk8DoMT0K86GvpVd+1+wt3b4UW - 1heKYkBrifWQaG2zSVHUSjb8iBgoDG9J+DC8RUMyu9XJevdviEJpwWBYxvQlDaYkFDzG - SI4ZDOMKzOZwy3CSNRGbuhnkXOhn56foZ+jkYmjFhEa5RnJVkk72WDtxspH+WBbi0kBO - diPTmM26NZnNlhO6zeY+fo5sI+kisZUy3feE2e1EX9QbV61Q4mLuhECSxhVh9ys24Ix+ - zSa79yCb5LnUYV01Cm107y725Quxvwuns/iZJs8EaVJJKoiZ+7XPBbfDbvL5JeJdx6Ce - 20zQVCqk0cLlvX3OcBZ9dzh9G6JUyvPWyCK6dxuMnxS5homUx/RdDne/LheG8/JUUn1L - hqOT0YjKGsmOZGvx5VQT4aa5bA8TqcJsVoS/q8PnxQPPJlipIeKQfXMPxyTG45o0pLLh - 1MukBrTcxdC9aVwjkSz1bgjCKdokCBdZ3iPN0fGkbFN2s+gGTd77HeN5J7wDLcZzKJOz - 6Q3q3HIHe2cwFz1eWjXRfoRF5/Ye6aaEy2ju3K8TjaXo7bG8t9lyqlMfJ69jaS5yXRHy - 9vjCbme0clxRBhuL9LbH30nyluW8bpqapi1Ju5PnFPB2JycumhjSOvJM0bWxOZnGkPZu - oknjrAg5jaz9wj9E02jaD7qVbx/DRcylDBdTt1iz7OQ6re4f/Rk4JhUYTXPIOw7/DPoT - cExpL6NF9wWnbGcp9nN75hQZPrjDO7P9nYTDohF0kLFSEpP2aND9WI41zCP074+PvEgT - GxIM4sNaTbZ/0NUub24l2T8E/7difUjzxyjQH8FprY5LB2j3d7Qml6au0aA/hGN/dHuH - Jl+xGJnVH7gds1rznC+Z4GrJefPkkNbEI3L4O9mORRT2KfXDY7pcg4YrNcdAl2itQEcd - vDzjA6Z+B1IBe/1vt4QTK7h9XO05xqUiXCU6xi6EbxIlYiNEd405LR9pm1St82UqXKGq - V+keEa2QRmd1j5gDzrvi/WqNbpDVqvti0zlqdDfLaTGR3sJFMtydq1T2y3u4NoadPm05 - RxXrVuzfxtwfjsp39+S7etF/gO5iOZ1GxBO4s5jPb2/ncUswncCRFp2nYYE0ggxvWd0l - 5nwesYYbk6nNDNpmCrcj18T5wsjRbyy3tG6K0hVVXEqVboXSsnDEVr+ynFavZpvT7Qvg - Zi7dy8Vt3IDP7bSZeUXK267+1mZK1773syLh+GjZahc3g0MhcQvY3j5MGm406/YlZDjW - 7TgIgul8I9rrFbefzas4zsHO53Nrs26gbq+14LRj0RqMdBPcbnc4nQ47bDatGOkUC8fF - o7eavkwHXG8wLtMNeDTcdl82GsTJ4T9Cy3Bsl+jqv7j5v7IibvmLc2psNUebZu0ASJZL - jzzgJx4YDPx0A+nhBv8O3bacHvXQetKDeKqD/FCHnudIbRMG/Iks/87VK5RT8ISLBTR6 - moUoiAz9RIf3vxXD6XifHu0hHuwBcLsQ1Ov07P1P7fOvgGOPzJW7SfE8E3p8iai+UeGx - z48Z7G18MkF1LLge5oMLMNJ7+Md39PF9CI6ow3h+kgzVG+V6Zx//fbi3AC7h8Q1E66/M - OxxW+t9Mpzq11MTvI/nofj5E4Nr/9vN/RvseYo/2E9VPUz2gekD1wH/bA/8DnbcyYgpl - bmRzdHJlYW0KZW5kb2JqCjM3IDAgb2JqCjM0MDgKZW5kb2JqCjMzIDAgb2JqCjw8IC9M - ZW5ndGggMzQgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGgg - MzgwIC9IZWlnaHQgMTUyIC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29t - 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 - 8lcEhMLoxPPwXxz6T7MP/FF//hlA4DFQdiKwycf/+yfd/+Uf/QuAn6H8RYH/AKX/kgAJ - kAAJkAAJkMB/jcD/A4a3SRwKZW5kc3RyZWFtCmVuZG9iagozNCAwIG9iagozNDk2CmVu - ZG9iagozOCAwIG9iago8PCAvTGVuZ3RoIDM5IDAgUiAvTiAzIC9BbHRlcm5hdGUgL0Rl - dmljZVJHQiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGtWHk4VV3bX2d0 - zMeQMRzKkHnKPI8ZM2co8xiO4xhCSFGUucyEJIl6lIgGUqbMokSJMkVmIjK9+9DT833X - 973X+8+7rmvt9Vv3+t2/tfa+z9r3WRsA7EMnAsEHDgDwxQcSzXQ1cNY2tjiyIYACtAAL - RAHMySWAoG5iYghR/k1ZHwAw0lC/CEkrxyp1IeyFaeVXk6LyQcGy+X/j9LeZhghNCABM - GDIwehxgNRJ2PsAWJHwukBAIcTxJ2MXTyRXC4RAWJlqYaUK4FMI0Hge4loSdD/AbEg52 - 8SD5DgKApse7euEBIJuDsIqrW4ALNEya19U1wMUXwikQr8/X1w/Sx0IVCLgQiJAvNgHC - R0nPBWqhYgvpSpVDOsn/2IIhreo4APj8/7HxQhymaQDu8P5jWzXbf1Ywpp4AdynJfTkY - lQYAqJG9vVU+SPM6ADupe3tbRXt7O7cBQAwD0ODjEkQM3udCC4R1AfCf+gf3/NsDAQUH - CjDMDr6KDEEtknlhxincKMeoT9H0Y83o2hm0GV8yKTBXswqxFR5m5UjhwuAiuBePuBx9 - x6fDX3kMJ5gotC7iKNosLiyRJLkobXz8nixczk7+oSJSyVK5WGVJTVE9WqNVi0rbWCdR - t0MPra9uEGr40GjyJKuJoWmYWZn5oCXSSvyUnfUlm3u2vXbrZ9jtlR2cHC863XJucBlx - 3XJn8ZDyNPbyPHvBO8Pnge9r/Ae/WcIOkTaAK1AsSCXY4Jx1iGsoPizkfFR4XERyZNqF - 7Ki8i/mXCqNvxdy6XHglPzY3Lutq2rXk+KsJ0YnhSQHJ3inOqdbXjW9opsmlC2fgMhmz - yLI2s7/nDOd25dXffJB/syC+MOyWR5HlbY1i0TtsJeiS5bufSpvvVZRllUfd93xg8pds - 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 - Yi8xRBdeFrtSH2d8dSSekAhPSk8RSH1xwzRtKuN8Fja7JFcp722+R8HOreu3+YtflJjc - nbgXXI65n/3XsYraRzqVfVWnqydqvGsXn/k/X67D1083OLzqb9RqetTC3nrxzVi7WkdO - 52K3ek9S70Afe7/du6z33R/ggxJDZz5Gf7o73Pp5fGT7C91XnjGJcaUJ7UmDKaNvxtMG - M7rf1Wbl5kTmcQv0i7DF5aXPy20rj1dv/ohZ8143+Xl8g2MTuTn9q3OrYjtlx2/XeI+P - FP+D89L+mYJC08/Hj4gz1NTa7/73Lr4+QdA5ar/QQlcqvLPxSailh2pTQLC5NtRCeQsM - uHvp6P3GU65OWgYQPgzV9TBPTWOopYL+7dK7E3XMIMwEYc6zTvomEKaBsLQb3tIcwpAm - TIMQqEHisEDYwi1A+2+7T5inxanf/EhvPwMSh6SZ4Oqm9XsNsCy8j7EhZCfpl3oF6u2f - UyFcD3Sg9yIReAA3IAIMob2iBVkm9i1/9632+15/xg9YIsB93zMY8gwA3mAK8vF18LpE - hPbcAePgigMuwA8EAR+IFwSI4uXiM+Lbfzia0JgPVP9/r4MRL+AKMf6n2r6dNJdvpXtw - ll+ogpVn91zN3B/Vg3ty/rNig79nh9aA/2P9P4rAC/qusH+ehp4UQEOxy7MnoQa+QlLz - v0qgWwh01gZA048QSvTy8AzEqUNfE9yEcXp4F1FhnKS4uDz4FwLFZdwKZW5kc3RyZWFt - CmVuZG9iagozOSAwIG9iagozODI3CmVuZG9iagoyNCAwIG9iagpbIC9JQ0NCYXNlZCAz - OCAwIFIgXQplbmRvYmoKNDAgMCBvYmoKPDwgL0xlbmd0aCA0MSAwIFIgL04gMyAvQWx0 - ZXJuYXRlIC9EZXZpY2VSR0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB - rVh5OFVd219ndMzHkDEcypB5yjyPGTNnKPMYjuMYQkhRlLnMhCSJepSIBlKmzKJEiTJF - ZiIyvfvQ0/N91/e91/vPu65r7fVb9/rdv7X2vs/a91kbAOxDJwLBBw4A8MUHEs10NXDW - NrY4siGAArQAC0QBzMklgKBuYmIIUf5NWR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHBsvl/ - 4/S3mYYITQgATBgyMHocYDUSdj7AFiR8LpAQCHE8SdjF08kVwuEQFiZamGlCuBTCNB4H - uJaEnQ/wGxIOdvEg+Q4CgKbHu3rhASCbg7CKq1uACzRMmtfVNcDFF8IpEK/P19cP0sdC - FQi4EIiQLzYBwkdJzwVqoWIL6UqVQzrJ/9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1239W - MKaeAHcpyX05GJUGAKiRvb1VPkjzOgA7qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguEdQHw - n/oH9/zbAwEFBwowzA6+igxBLZJ5YcYp3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUeZuVI - 4cLgIrgXj7gcfcenw195DCeYKLQu4ijaLC4skSS5KG18/J4sXM5O/qEiUslSuVhlSU1R - PVqjVYtK21gnUbdDD62vbhBq+NBo8iSriaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY7ZUd - nBwvOt1ybnAZcd1yZ/GQ8jT28jx7wTvD54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhrKD4s - 5HxUeFxEcmTaheyovIv5lwqjb8Xculx4JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141v - aKbJpQtn4DIZs8iyNrO/5wznduXV33yQf7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WU - ZZVH3fd8YPKXbAXHQ8TD6UddlZWPM6pCq08/Ua3hroXXTjxtelb8POaFS516PVf9zsuh - hievUl97N2o3cTX9bO5pKWkNf2PWxtf2q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr - 9xzvvwwUfXAe5Bn8OpT/0fYT06fe4auf1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6U - xTfkt8fTzjN0Mw3fCbO42d65i/OS8xMLWYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4 - NsY2S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNM - sFmwt3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFS - qUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlp - i7dLOv3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6 - Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJK - UlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZuvstvKagtLLuVW5RwO7zY987p - EsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QX - QXX29bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f - 7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh873PoiN4o8+jkl8qvkWN644zjIxN3JwlTslO7 - 35qm42YMv2O/v529Pmc2TzffvXBtUXtxd6l6+ewK50rvatQP0R8f16LXBdd7fvpv0G9U - bOpvTvwK28JuFW1LbzfumO6M7p7dXdsL248/Ai6MUEVaojzRkWQZmHvkdRQDlLPUcBpG - Wn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf2Nc54JwUXEw4Lm5eHqEjokfFeSX4JPhFBQSP - 8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUjbXZcRoZZZkN2UK5WPl2BqGisJKiMUh5RqVVN - VfNS19Bg01jRbNcq0g7TMdMVPAE/8VHvkf41AxdDRSNGo3njlpOFJqGm5mYi5mjzLxZP - LdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9skO7422ncGcrF1FXMtdRtxr3JA93T2UvRq/Z - s6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd+DwoMdjxnGQIMmQgtDQs9Lx+OHv4XMSLyMQL - Z6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76quDVnWtv44sTQhINk7iT1pM7U4pSQ6+b3BBI - A2lD6ZUZCZnuWerZh7M3ct7nPspLvumbb1AgWEhR+P1WZ9GD28nFxDtWJQp3uUpRpbP3 - +suelxffT3oQ+pdbhdlDtUdilVyP6aqQVRvVC08maz7Xfnja/6z3ee+LvrqB+uGXkw3L - r/YasU28zcotNq0hb/LamtpXO3m77Lvzer68FeqL6H//Xm6gcJBuKP4T9XDmiPBo21e/ - ca6JD1M5067fZefo5lcXPy93rTavNf6s22zYat8ZJsX/IPeRcgJaBoBMLACnjgJgXgRA - bA6U6iyhXFUKgAk1ABbyAG6TDeD2tQDm7g5+5w8oh6AAJaAH7IAPSAI1cBI4AH8QA7JB - BWgBI2ADxgATgxnBvGHxsPuwbtgKnBmuDHeFJ8CfwL8gqBDyCE9EJuIN4hdSBOmMzEL2 - oMhQ6qhwVC1qHX0cHYSuQf8iUyW7TNaFYcI4YR5gtsj1yXPJFyg0KLIolij1KIupYFSO - VA3UPNRXqOdozGnqaAVo07FIbCD2G50d3Vt6ffomBlWGOkZlxvpD6odamYyZPjA7My+w - RLDSsBayHWfrYHdl3z2cyyHPMcQZzoXjasH5cbNxt/AEHeE7MnQ0iVeHd4/vOX+ogLzA - 9rFXgnFCZsKcwvMi9aKpYh7iqhJsEhuSg1LPpAuOx8r4y9rLGcmrKkgpCiodUeZS4VTl - VONRF9AQ11TUOqFtrXNWN/JEul6FfofBjBGFsehJC5MI01KzAQu0pbyV36m71mO2ODvH - 07fPzDhIOkY4dbgcdiW4tXrgPMO9hr1VfG7jKf2CCeNEi4A3QarBT0NkQ2vOK4a/jjS6 - MHwRH42Myb+iFDtyNSZeLGE06UaK0XXKGz3pWZke2Uq5THk/80cKO4saip+XvChtLnt3 - f7YC80jksXX1tZrGZ4gXevVpDZONSs2ZrWvt1p0veo68vda/MmA72PDp6Ocro1NjGhO5 - U8szmrPX50eXjq0QfjxZ39xU3YreaSb9fn7HnwEcBvxACqgDE+AEAkAsyAOVoB2Mgx0Y - K+w4zBwWALsBq4YNwrbhPHA9uD88B94EX0JwIAwQ5xHliM9IOqQ28jyyEjmL4kU5om6i - PqHZ0Hbom+ivZPxkeLInGIAxxGRjpsnlyePJv1Acp0ik+EapQVlAuUvlQPWKmo86gXqN - xoGmg1ae9h6WHZtMh6QLp1un96dfYPBlWGQkMq4fimBCM6UwH2YuZ1Fk6WJ1Yv3JlsQu - wN542IkDcBRxanPOcl3HKeK+cafzaPGsHyk76sjLwtvPl8J/UgAr0H8sW9BJSFhoU/iN - SI4oXkxLnEN8U+KDZI1UjvSF4x4yprLKcsLynAr0ihgloLSlvKmyofpLbVcDpUmjxarN - pyOje+LEaT0i9Fa7Y/ja6OtJBJQDjcyCzAssOi23TolYO9pk2HafJj+jbR/j0OJE7nzS - JdN1zF3UI8Kz+yyP9zmfHij7XSFMEnUCyoKwwSHnxkNNwhrCpSJKL3BFZV9ijs64zHql - IE7galW8ZsL7JO8UZOqtG+ppkxmJWfLZM7l5Ny0KsIW9RenF9iVCd3fuvSuveJBcEfDo - zGODapWa408ln0vVKbzUeXWqEd8c31rRNtRJ0a3ZG9PX/f7oh5ihpeGzI6tfEyfkptZn - 2uceLz5eaVv7uSm5fWk//ghADhgADogDTWANCCAelIJWMAOjhknDzsDioJhPQjveAH4J - /gy+hpBCEBFViA2kCjIW2Y/CoQioRjQLmoDuIBMgiyObxZhinpLzkqdRoCnCKX5QEiiX - qAKoNqmjabA0t2hlaLuxXnQYunJ6U/pfDCWMVofID71kCmGWZl5hqWINYVNlx7C/O3yb - I5BTl4uDaw3Xy32fJ+GI71FTXjk+bn4q/k2B78eGBd8KtQk3irwUhdK0eLNEp+SA1Jj0 - sgxClkVOXF5fwUMxVqlMuUdlQ+2IuplGtGaN1oLOMV33EyV63w3EDcOMWk4eMvEwrTNn - tPCz7ILilmzzw+706WZ7CYcCJ1rnKJcfbl7uo57WXm+9DX3a8Lp+Lf46xNZA/aDuc+Yh - Q2GO56cj/CO3omIvMUQXXha7Uh9nfHUknpAIT0pPEUh9ccM0bSrjfBY2uyRXKe9tvkfB - zq3rt/mLX5SY3J24F1yOuZ/917GK2kc6lX1Vp6snarxrF5/5P1+uw9dPNzi86m/UanrU - wt568c1Yu1pHTudit3pPUu9AH3u/3bus990f4IMSQ2c+Rn+6O9z6eXxk+wvdV54xiXGl - Ce1Jgymjb8bTBjO639Vm5eZE5nEL9IuwxeWlz8ttK49Xb/6IWfNeN/l5fINjE7k5/atz - q2I7Zcdv13iPjxT/g/PS/pmCQtPPx4+IM9TU2u/+9y6+PkHQOWq/0EJXKryz8UmopYdq - U0CwuTbUQnkLDLh76ej9xlOuTloGED4M1fUwT01jqKWC/u3SuxN1zCDMBGHOs076JhCm - gbC0G97SHMKQJkyDEKhB4rBA2MItQPtvu0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAsvI+x - IWQn6Zd6Bertn1MhXA90oPciEXgANyACDKG9ogVZJvYtf/et9vtef8YPWCLAfd8zGPIM - AN5gCvLxdfC6RIT23AHj4IoDLsAPBAEfiBcEiOLl4jPi2384mtCYD1T/f6+DES/gCjH+ - p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noaeFEBD - scuzJ6EGvkJS879KoFsIdNYGQNOPEEr08vAMxKlDXxPchHF6eBdRYZykuLg8+BcCxWXc - CmVuZHN0cmVhbQplbmRvYmoKNDEgMCBvYmoKMzgyNwplbmRvYmoKMzAgMCBvYmoKWyAv - SUNDQmFzZWQgNDAgMCBSIF0KZW5kb2JqCjQyIDAgb2JqCjw8IC9MZW5ndGggNDMgMCBS - IC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+ - CnN0cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqU - iAZSpsyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcO - APDFBxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp - 5VeTovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYm - WphpQrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfC - KRCvz9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZp - AO7w/mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMS - RAze50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIp - MFezCrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6h - IlLJUrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWb - e7a9dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxI - JdjgnHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJ - AcneKc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl - 6JLlu59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/Dzm - hUudej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/ - rWIfsq+r/8Y76/cc778MFH1wHuQZ/DqU/9H2E9On3uGrn9U/b45UjLp+Yf3S9TVqTHJs - Yjx9QntiffLOlMU35LfH084zdDMN3wmzuNneuYvzkvMTC1mLhkuwpZpl/Ar/ytfVvB82 - a8xrA+uZP203ODbGNkt/EbbktuHbHTsZu057Ynt7v+Nvhxoks8L0UZygrKeWpinHctNl - MNAzxhzaYsazTLBZsLdwKHDexx3mvsbz66g7by+/okCRIEbIV7hHVFIsVXxBUkeqQHpd - Rk82V25GQUYxUqlFhULVSC1JvVOTXEtT+7zOQ91JPRZ9PYMQwxKjfuMdE35TU7MQ85sW - ry2/naKwFrE5aYu3Szr94EyH/bQjyonbWdHFyhXvFuue71Ht2eE1enbVh8yXFS/op0jQ - 97cmugcEBF4Iuhacdi4vpDi0POzR+Zrw5xEvI19deB3VeLHxUmP0q5iXl+uuPI2tiqu4 - WnbtTnx+QmZiSlJc8oWU4FSf6y43bNKM0zUz5DJFsniymXLIc3Zyl/LGbr7LbymoLSy7 - lVuUcDu82PfO6RLDu4qlgvfYyjBl6+UT9/sevPzrfkXOwyuPAiodHhtUyVbzPKF+slkz - Xtv9tPZZ0fOEF0F19vW6L8UamBt2X0297mp83JTdHNni0qr7RqiNpm25vb+jqjO9K7Db - vEeyF9s79/ZN3+3+SCj64gOYgS8fngzGDzl8lPqE/jQ4fO9z6IjeKPPo5JfKr5FjeuOM - 4yMTdycJU7JTu9+apuNmDL9jv7+dvT5nNk83371wbVF7cXepevnsCudK72rUD9EfH9ei - 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 - Cv/K19W8HzZrzGsD65k/bTc4NsY2S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCs - p5amKcdy02Uw0DPGHNpixrNMsFmwt3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixV - fEFSR6pAel1GTzZXbkZBRjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0T - flNTsxDzmxavLb+dorAWsTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWH - zJcVL+inSND3tya6BwQEXgi6Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX - 6648ja2Kq7hadu1OfH5CZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZu - vstvKagtLLuVW5RwO7zY987pEsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJ - VvM8oX6yWTNe2/209lnR84QXQXX29bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9 - v6OqM70rsNu8R7IX2zv39k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh873PoiN4o8+jk - l8qvkWN644zjIxN3JwlTslO735qm42YMv2O/v529Pmc2TzffvXBtUXtxd6l6+ewK50rv - atQP0R8f16LXBdd7fvpv0G9UbOpvTvwK28JuFW1LbzfumO6M7p7dXdsL248/Ai6MUEVa - ojzRkWQZmHvkdRQDlLPUcBpGWn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf2Nc54JwUXEw4 - Lm5eHqEjokfFeSX4JPhFBQSP8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUjbXZcRoZZZkN2 - UK5WPl2BqGisJKiMUh5RqVVNVfNS19Bg01jRbNcq0g7TMdMVPAE/8VHvkf41AxdDRSNG - o3njlpOFJqGm5mYi5mjzLxZPLdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9skO7422ncGcr - F1FXMtdRtxr3JA93T2UvRq/Zs6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd+DwoMdjxnGQI - MmQgtDQs9Lx+OHv4XMSLyMQLZ6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76quDVnWtv44sT - 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 - b/6IWfNeN/l5fINjE7k5/atzq2I7Zcdv13iPjxT/g/PS/pmCQtPPx4+IM9TU2u/+9y6+ - PkHQOWq/0EJXKryz8UmopYdqU0CwuTbUQnkLDLh76ej9xlOuTloGED4M1fUwT01jqKWC - /u3SuxN1zCDMBGHOs076JhCmgbC0G97SHMKQJkyDEKhB4rBA2MItQPtvu0+Yp8Wp3/xI - bz8DEoekmeDqpvV7DbAsvI+xIWQn6Zd6Bertn1MhXA90oPciEXgANyACDKG9ogVZJvYt - f/et9vtef8YPWCLAfd8zGPIMAN5gCvLxdfC6RIT23AHj4IoDLsAPBAEfiBcEiOLl4jPi - 2384mtCYD1T/f6+DES/gCjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7cv6zYoO/Z4fW - gP9j/T+KwAv6rrB/noaeFEBDscuzJ6EGvkJS879KoFsIdNYGQNOPEEr08vAMxKlDXxPc - hHF6eBdRYZykuLg8+BcCxWXcCmVuZHN0cmVhbQplbmRvYmoKNDcgMCBvYmoKMzgyNwpl - bmRvYmoKMjcgMCBvYmoKWyAvSUNDQmFzZWQgNDYgMCBSIF0KZW5kb2JqCjQ4IDAgb2Jq - Cjw8IC9MZW5ndGggNDkgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0 - ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYWUTUgUYRjH/7ONBLEG0ZcIxdDBJFQm - C1IC0/UrU7Zl1UwJYp19d50cZ6eZ3S1FIoTomHWMLlZEh4hO4aFDpzpEBJl1iaCjRRAF - XiK2/zuTu2NUvjAzv3me//t8vcMAVY9SjmNFNGDKzrvJ3ph2enRM2/waVahGFFwpw3M6 - EokBn6mVz/Vr9S0UaVlqlLHW+zZ8q3aZEFA0KndkAz4seTzg45Iv5J08NWckGxOpNNkh - N7hDyU7yLfLWbIjHQ5wWngFUtVOTMxyXcSI7yC1FIytjPiDrdtq0ye+lPe0ZU9Sw38g3 - OQvauPL9QNseYNOLim3MAx7cA3bXVWz1NcDOEWDxUMX2PenPR9n1ysscavbDKdEYa/pQ - Kn2vAzbfAH5eL5V+3C6Vft5hDtbx1DIKbtHXsjDlJRDUG+xm/OQa/YuDnnxVC7DAOY5s - AfqvADc/AvsfAtsfA4lqYKgVkctsN7jy4iLnAnTmnGnXzE7ktWZdP6J18GiF1mcbTQ1a - yrI03+VprvCEWxTpJkxZBc7ZX9t4jwp7eJBP9he5JLzu36zMpVNdnCWa2NantOjqJjeQ - 72fMnj5yPa/3GbdnOGDlgJnvGwo4csq24jwXqYnU2OPxk2TGV1QnH5PzkDznFQdlTN9+ - LnUiQa6lPTmZ65eaXdzbPjMxxDOSrFgzE53x3/zGLSRl3n3U3HUs/5tnbZFnGIUFARM2 - 7zY0JNGLGBrhwEUOGXpMKkxapV/QasLD5F+VFhLlXRYVvVjhnhV/z3kUuFvGP4VYHHMN - 5Qia/k7/oi/rC/pd/fN8baG+4plzz5rGq2tfGVdmltXIuEGNMr6sKYhvsNoOei1kaZ3i - FfTklfWN4eoy9nxt2aPJHOJqfDXUpQhlasQ448muZfdFssU34edby/av6VH7fPZJTSXX - srp4Zin6fDZcDWv/s6tg0rKr8OSNkC48a6HuVQ+qfWqL2gpNPaa2q21qF9+OqgPlHcOc - lYkLrNtl9Sn2YGOa3spJV2aL4N/CL4b/pV5hC9c0NPkPTbi5jGkJ3xHcNnCHlP/DX7MD - Dd4KZW5kc3RyZWFtCmVuZG9iago0OSAwIG9iago3OTIKZW5kb2JqCjcgMCBvYmoKWyAv - SUNDQmFzZWQgNDggMCBSIF0KZW5kb2JqCjIyIDAgb2JqCjw8IC9MZW5ndGggNTAgMCBS - IC9GdW5jdGlvblR5cGUgMCAvQml0c1BlclNhbXBsZSA4IC9TaXplIFsgMTM2NSBdIC9E - b21haW4KWyAwIDEgXSAvUmFuZ2UgWyAwIDEgMCAxIDAgMSBdIC9GaWx0ZXIgL0ZsYXRl - RGVjb2RlID4+CnN0cmVhbQp4AbXCB3vhYAAA4H9Ye4Qkduy9iXE/7zqvNtXWbI0WrdnF - PfJwhCQ+iXufd/Ersfg3mVhsxhfJ1XkyfvREfL4ZmydiP6zHYz/r33Gc/Rj+HcO/SKNf - MTY/8ejeyCd+3A88shzdH/6Igp5Fw6uR8IxiaBY5fBoJkYZDU4rBaZjpJBzcHQpOqAcm - IerjUIB6MDCm7h8HSUdB/4EB/4i6bxRYHQZ8h/t9Q9reod/7Dt7nfaf55vMc0et5ozrw - eojugRe4xz0g73vcW119D3C3q7/ec7v2OntusC5nz+V8Zep4dQF9cToA2l+cTLtOe9cB - 2NZ1UO84bKt2WweotWPf3bZbSW3WNlBL27bZsll2Wy0t0OaWdfmZrsX8DPbJYmJqNj0B - bJpNRGPTTN9kbDJumIxbsYaJvhFrMK4bMZqGupExZqjTrGEGxvoaxtigr+2tGvRgdVUD - fb2uul7R646srehp6rQV4qNOy6rmUUdTq3ngWv2gJb3Xqpc13KvuNatljWpTrSpzjZbV - 6N1OFXrHbUmFEpGSihxFSmwXUWQrXETJEbh4/AIC71UWEHJYWQCeh5WMFXl4q1KRB5hT - KgBCOeW6AsodmlVAwOVZxTokz9LMQHJWZRmIKJdl9qblMg6laTlRJk1vTcmknEtSMqJU - kpJKbk9cfCsR//kPbySiG/EpX4tF68Jr0WleiYS7hYIrbi+FAuoC/iWrFwI+Uz7/gs87 - 9jmfdziPd758Bvg37wzwX0ZZ9gAKZW5kc3RyZWFtCmVuZG9iago1MCAwIG9iago1NjQK - ZW5kb2JqCjIzIDAgb2JqCjw8IC9MZW5ndGggNTEgMCBSIC9GdW5jdGlvblR5cGUgMCAv - Qml0c1BlclNhbXBsZSA4IC9TaXplIFsgMTM2NSBdIC9Eb21haW4KWyAwIDEgXSAvUmFu - Z2UgWyAwIDEgMCAxIDAgMSBdIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4 - AbXCB3vhYAAA4H9Ye4Qkduy9iXE/7zqvNtXWbI0WrdnFPfJwhCQ+iXufd/Ersfg3mVhs - xhfJ1XkyfvREfL4ZmydiP6zHYz/r33Gc/Rj+HcO/SKNfMTY/8ejeyCd+3A88shzdH/6I - gp5Fw6uR8IxiaBY5fBoJkYZDU4rBaZjpJBzcHQpOqAcmIerjUIB6MDCm7h8HSUdB/4EB - /4i6bxRYHQZ8h/t9Q9reod/7Dt7nfaf55vMc0et5ozrweojugRe4xz0g73vcW119D3C3 - q7/ec7v2OntusC5nz+V8Zep4dQF9cToA2l+cTLtOe9cB2NZ1UO84bKt2WweotWPf3bZb - SW3WNlBL27bZsll2Wy0t0OaWdfmZrsX8DPbJYmJqNj0BbJpNRGPTTN9kbDJumIxbsYaJ - vhFrMK4bMZqGupExZqjTrGEGxvoaxtigr+2tGvRgdVUDfb2uul7R646srehp6rQV4qNO - y6rmUUdTq3ngWv2gJb3Xqpc13KvuNatljWpTrSpzjZbV6N1OFXrHbUmFEpGSihxFSmwX - UWQrXETJEbh4/AIC71UWEHJYWQCeh5WMFXl4q1KRB5hTKgBCOeW6AsodmlVAwOVZxTok - z9LMQHJWZRmIKJdl9qblMg6laTlRJk1vTcmknEtSMqJUkpJKbk9cfCsR//kPbySiG/Ep - X4tF68Jr0WleiYS7hYIrbi+FAuoC/iWrFwI+Uz7/gs879jmfdziPd758Bvg37wzwX0ZZ - 9gAKZW5kc3RyZWFtCmVuZG9iago1MSAwIG9iago1NjQKZW5kb2JqCjMgMCBvYmoKPDwg - L1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDU3NiA3MzNdIC9Db3VudCAxIC9LaWRz - IFsgMiAwIFIgXSA+PgplbmRvYmoKNTIgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1Bh - Z2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5kb2JqCjUzIDAgb2JqCjw8IC9MZW5n - dGggNTQgMCBSIC9MZW5ndGgxIDEwMjg4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 - cmVhbQp4Ab1aeXxU1fU/9+2zZDIzmcnsmZlMZiaTfSEJQ0IyhqwsMSQCCRJMAoGAUBFj - NCo0KhSJiguCCP3Z4sIqMoQIExB/aFHEWvfiUtvaitb203xsfz9trSQzv3PfhAhd/PGH - H+fN3e+775zvPefcc+97QABADf3AQmjRyo5VzY0VtVjzKgDRL+rtcd37x/JHMf8RALti - yaqlK3Uf/ewVAH4mgFK9dEXfkp0FqocAElsAkvq7uzoW//X0U3MAUt/DMYq7sUKZKuYB - eLRYTute2XPzwnNiMZYnYbl9xXWLOsrSS3AsD44BBSs7bl4l3ab8B5ZfxrLrBx0ruxqX - 3bYey59hOXXVdTf0sPPYMziUGstNq1Z3rXr2zh/kY7kd6Xsd6whe9IftRJBz3xrFO39r - l29rZBA1jhdESaEElTpBk6gFnT7JAEaA5G+77ztqM13GOPxJ0PInIJ3vByuXC06A2PsY - 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 - PsXtsyx6E+uxzrNxv/GJa3mzQ2vT3vUgqspw8Q6GfY5lDq4e20b1ojL2LnuEm4Frby7J - Cd03WbGN36p/xLDNuC1DSE/z+ovdNe7atFr/3LR5/iVpS3196r6EPk2vpyetx9vj25Wy - JyuJRVeIz+ZyksBqtJnsZmO2ISc9UbVM8nmLvYw3NUHJZSaZX7I7kkTOkbM9U5UrKjRa - RoRcd67VaU42+03l6T7Rn27N1zj92nLw51jy8gcn/Dc0IfH1O6jFHGU3mIsxqhydY7qT - oiblelmSZ5Jsxmf0Wn1ujdMNCp/oJmwW7sX4DMw59FhnM5jdxJWY6gZ3qiZB8ivdxOdV - KEk25wYhgFGKzu4mlmSMZDdO3gDIkSwiFwQft1pJ8jIvi0sudd1wC0VXRtETd+Oo+DgJ - 9fYMKDg+P/lc8lbtWbxtqv+G+zZe0fOr4f+5dhqzj/eVP7JkWXV6w00vVC57/zefnxbJ - UdI4P2/evKur09DzTc2ov33bs5vmd08tqG0I1WRYkhy5WdUP3ffG+z9l/oGyZIp9zij4 - +Wgdmp5JyFGe1JAIqQh5ueSgiRU0Sp0VzTW+OQuAUWNMZJ0sw44mWyxW9O3Gd09jl/p2 - uXHnbkQ7dk5ePKhHR/Xgwn7RV0Tduz1H9u/3GfMTUgzOaf618x94gJ8f/eXmserJSSrC - bFJIty9lXtyM6w0D/bGP2d+gPpuQwoWhKRHDywZGkSQZLEkWQ7pwE/suLrbAa5QgJCh5 - tF1m0WzGLVmOMqBWWa0kQIl964I3MIsaLyr+OP1xP66ijAoEFX3SRuKEooNFNzElsl+N - s6LzksnWvDufrfIO7WM8k5Zu/qQ5mxzkcseCTZPa98z/L0Zz/s1Hp2Zc9UjTRuY9K9VP - Pb7n38nlgpq4QmtruX0KZInUiPWqDeyAtF75CnOKfUk8I72kPKNSLRGXS13KZapesU/q - Vfap1osDKiXty9SyN8HNPDsvPTkdd1tcKSnl7iP3cYKCI6yKQedCzYMgKVWsqNTgJIm8 - sENiuVNKRnFKBWSH2pJA3W90Kr4wzxoLBif+Fnk9RUcDF1RTGUp+24zZfSE1H9AjxxDQ - q9UqfoM2E/8zmvqGFPjuTRkhd4eS9LjdFQWOpx0FUYGv7iVar9FzuAlWI9vyrYR6LBu0 - a06ZeeqyoO/yopzZsEZ7aqKGui/XX389ejA2ptBGColHhT7Mu6+9+cpbvxqKnjn+wdvH - oz/nckeH2Jmjw2zt+TfZqaM/Q0BRHvD7B3YI5UEBSlgRqiwR6mAetJB5wlLoJkuFm3gF - 4QUhgFNAlEoi4GaMMEH0vkAUgkiyUuTLRauanQ4WlXpwYlGTTT5ugCtwNxeUI2oR0U5s - QGbWvEjpLSFuPBQibqObTGJuHRtiy8c2MgOj/eT1TSzs3DyGM16P6y3uk9ib5X2SDfck - HaFi2ycW+Ga/5MANk1OndOOE2VICZue/bJtc7rfcS8d35hOqdRbJGveC0DmkziLdPFWM - kP+8f/IWFRpFFON/2UcxSUP4+9fdlPPVV0+fPyv7eBjhRyUDr9Vek1j2JegkufxSxaEn - aSaeRksFL3IJOA94jib/MBUC0QB+QkK+6hodUT0w0TLeAcy8HiqZIHrDN8ihiQMoxfxk - DHVYb8d0KoZ15DSsw7Z+TDcK+zCPdRho315mH2zENjqOCcv9+B2GHsuTMCD2KB+ToA7f - qj+E781fYZPZ5zgNV8Y9xb3GT+d3CSnC/cJZ8YgkSO0KjWKh4iTeQek34/t6Fq7F00MG - tHi1AYifKdX4nQZtpRod51LANpjeMqOypSazrmtFb1fPskUd2IPBgD88S8QDw3/zM2Md - i+eaVVAjfwtRD9PlLx4a4Er5m4wm/M5iDsylogzz8eknAT8Qoeee9RgqMBRhyMy8woyY - 7IL7MfwUAwvLyN3Qh2EjhkcwcBO5vVgaJncPclLoGOkDK5keUnHOqwwWp1mpcr6F7ujQ - o873zR8fJxZIgN8Ry2ACKK5Qkp+Sn8BicJIn8eTlFvxqI51sPxxY4WzHpr2wCkM/BlaO - Cdk7mFLgfI5kgZcjeI8PUjhyxPmH/GznJ/kRhgw6X/BHOEyeT8FSKNF50vGo878dS53P - Ydgfb9oXwB5HnHsdK5ybUyJk+6DzQbopGHQ+EE9udOCtR5wrA1udi/Pl9plbI8z+QWcQ - 2+eGVM7iyW5nkeOcM9cfkQiWsx0znRn5v3Cm4Y3YzYWDekM6p92x2TkFm1Ic1f4pGI6T - fWQHZJAdg97pzmOYRXYP1wcmb42QWw/Xped7I+SWUHFd+tZAnd8bmOn0Bmr8fszPfVlc - J14tXiEWiJn44QQ6CKJNNEh6SStpJLWklHDrFyFPDVY4heNkP1QgLPsPo7zxEfI0VnLH - yQG58sBRiZMYCSRDJPbREJUx3BLtH0LxIoCZI4KcEyLkAJ5V06oDISeKOAFObtCixBFZ - 7FAwGSIxKFBhcm9EgPXJvRXmCn25LlhT9Z+idrnlQiy7JP8+MhNHeCu+Iw3vc7Ti62jM - xBytF7ri2c7/8+u5ETt0VWbSleRw76rlS+TX657qrnZ8yx6+uxc/d+jvdLkOLV81/u2A - r71zUTd9v9vRFV7l6aoKL/dUuQ71yvfR6oual9DmXk/VIVhSfVXLoSWhrqrB3lBvNf3M - 4HBn5eq2S561ceJZqyv/zbMq6WCr6bM65fv+6VlttLmTPquNPquNPqsz1Ck/i0JQvay5 - 8oYelE58BY+vwNObw/Wz57fglyatVRGyi76XvxH+DxXtA6oKZW5kc3RyZWFtCmVuZG9i - ago1NCAwIG9iago2NzYyCmVuZG9iago1NSAwIG9iago8PCAvVHlwZSAvRm9udERlc2Ny - aXB0b3IgL0FzY2VudCA3NzAgL0NhcEhlaWdodCA3MjcgL0Rlc2NlbnQgLTIzMCAvRmxh - Z3MgMzIKL0ZvbnRCQm94IFstOTUxIC00ODEgMTQ0NSAxMTIyXSAvRm9udE5hbWUgL0pY - S0JYRitIZWx2ZXRpY2EgL0l0YWxpY0FuZ2xlIDAKL1N0ZW1WIDk4IC9NYXhXaWR0aCAx - NTAwIC9TdGVtSCA4NSAvWEhlaWdodCA1MzEgL0ZvbnRGaWxlMiA1MyAwIFIgPj4KZW5k - b2JqCjU2IDAgb2JqClsgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCA1NTYgMCA1NTYg - MCA1MDAgNTU2CjU1NiAyNzggNTU2IDU1NiAyMjIgMCA1MDAgMjIyIDgzMyA1NTYgNTU2 - IDU1NiAwIDMzMyA1MDAgMjc4IDU1NiAwIDcyMiAwIDAKNTAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMAowIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwCjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDUwMCBdCmVuZG9iagoxOSAwIG9i - ago8PCAvVHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9KWEtC - WEYrSGVsdmV0aWNhIC9Gb250RGVzY3JpcHRvcgo1NSAwIFIgL1dpZHRocyA1NiAwIFIg - L0ZpcnN0Q2hhciAzMiAvTGFzdENoYXIgMjIyIC9FbmNvZGluZyAvTWFjUm9tYW5FbmNv - ZGluZwo+PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9y - IChBcnZpZCBOb3JiZXJnKSAvQ3JlYXRvciAoT21uaUdyYWZmbGUpIC9Qcm9kdWNlcgoo - TWFjIE9TIFggMTAuNS43IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRpb25EYXRlIChE - OjIwMDkwNTI1MDIyMzMzWjAwJzAwJykKL01vZERhdGUgKEQ6MjAwOTA1MjUwMjIzMzNa - MDAnMDAnKSA+PgplbmRvYmoKeHJlZgowIDU3CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAw - MDA1MTUyNSAwMDAwMCBuIAowMDAwMDAxMDM2IDAwMDAwIG4gCjAwMDAwNDM2NDUgMDAw - MDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxMDE3IDAwMDAwIG4gCjAwMDAw - MDExNDAgMDAwMDAgbiAKMDAwMDA0MjExNSAwMDAwMCBuIAowMDAwMDA0Mjk3IDAwMDAw - IG4gCjAwMDAwMDUyNTcgMDAwMDAgbiAKMDAwMDAwMTY4NCAwMDAwMCBuIAowMDAwMDAy - ODI1IDAwMDAwIG4gCjAwMDAwMDUyNzYgMDAwMDAgbiAKMDAwMDAwNTc1OCAwMDAwMCBu - IAowMDAwMDAyODQ1IDAwMDAwIG4gCjAwMDAwMDM4MDcgMDAwMDAgbiAKMDAwMDAwMzgy - NyAwMDAwMCBuIAowMDAwMDA0Mjc3IDAwMDAwIG4gCjAwMDAwMzcxNzUgMDAwMDAgbiAK - MDAwMDA1MTM1MCAwMDAwMCBuIAowMDAwMDAxMzg4IDAwMDAwIG4gCjAwMDAwMDE1Mzkg - MDAwMDAgbiAKMDAwMDA0MjE1MSAwMDAwMCBuIAowMDAwMDQyODk4IDAwMDAwIG4gCjAw - MDAwMjgzMzQgMDAwMDAgbiAKMDAwMDAxMzAyOSAwMDAwMCBuIAowMDAwMDE3MDY4IDAw - MDAwIG4gCjAwMDAwNDExNjMgMDAwMDAgbiAKMDAwMDAwOTMzOCAwMDAwMCBuIAowMDAw - MDEzMDA4IDAwMDAwIG4gCjAwMDAwMzIzMjIgMDAwMDAgbiAKMDAwMDAwNTc3OCAwMDAw - MCBuIAowMDAwMDA5MzE3IDAwMDAwIG4gCjAwMDAwMjA2OTIgMDAwMDAgbiAKMDAwMDAy - NDM2MiAwMDAwMCBuIAowMDAwMDM2MzEwIDAwMDAwIG4gCjAwMDAwMTcwODkgMDAwMDAg - biAKMDAwMDAyMDY3MSAwMDAwMCBuIAowMDAwMDI0MzgzIDAwMDAwIG4gCjAwMDAwMjgz - MTMgMDAwMDAgbiAKMDAwMDAyODM3MSAwMDAwMCBuIAowMDAwMDMyMzAxIDAwMDAwIG4g - CjAwMDAwMzIzNTkgMDAwMDAgbiAKMDAwMDAzNjI4OSAwMDAwMCBuIAowMDAwMDM2MzQ3 - IDAwMDAwIG4gCjAwMDAwMzcxNTUgMDAwMDAgbiAKMDAwMDAzNzIxMiAwMDAwMCBuIAow - MDAwMDQxMTQyIDAwMDAwIG4gCjAwMDAwNDEyMDAgMDAwMDAgbiAKMDAwMDA0MjA5NSAw - MDAwMCBuIAowMDAwMDQyODc4IDAwMDAwIG4gCjAwMDAwNDM2MjUgMDAwMDAgbiAKMDAw - MDA0MzcyOCAwMDAwMCBuIAowMDAwMDQzNzkyIDAwMDAwIG4gCjAwMDAwNTA2NDUgMDAw - MDAgbiAKMDAwMDA1MDY2NiAwMDAwMCBuIAowMDAwMDUwOTAyIDAwMDAwIG4gCnRyYWls - ZXIKPDwgL1NpemUgNTcgL1Jvb3QgNTIgMCBSIC9JbmZvIDEgMCBSIC9JRCBbIDwzODUz - YzMzYzE5YWFhMzMxNDMzNDNmYmUyZDRkYzQ1Mz4KPDM4NTNjMzNjMTlhYWEzMzE0MzM0 - M2ZiZTJkNGRjNDUzPiBdID4+CnN0YXJ0eHJlZgo1MTczMgolJUVPRgoxIDAgb2JqCjw8 - L0F1dGhvciAoQXJ2aWQgTm9yYmVyZykvQ3JlYXRpb25EYXRlIChEOjIwMDkwNTI1MDEz - ODAwWikvQ3JlYXRvciAoT21uaUdyYWZmbGUgNS4xLjEpL01vZERhdGUgKEQ6MjAwOTA1 - MjUwMjIzMDBaKS9Qcm9kdWNlciAoTWFjIE9TIFggMTAuNS43IFF1YXJ0eiBQREZDb250 - ZXh0KS9UaXRsZSAoc3RvcmFnZS5ncmFmZmxlKT4+CmVuZG9iagp4cmVmCjEgMQowMDAw - MDUzMDMwIDAwMDAwIG4gCnRyYWlsZXIKPDwvSUQgWzwzODUzYzMzYzE5YWFhMzMxNDMz - NDNmYmUyZDRkYzQ1Mz4gPDM4NTNjMzNjMTlhYWEzMzE0MzM0M2ZiZTJkNGRjNDUzPl0g - L0luZm8gMSAwIFIgL1ByZXYgNTE3MzIgL1Jvb3QgNTIgMCBSIC9TaXplIDU3Pj4Kc3Rh - cnR4cmVmCjUzMjMxCiUlRU9GCg== - - QuickLookThumbnail - - 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 - UgABAAAAARbPc2YzMgAAAAAAAQxCAAAF3v//8yYAAAeSAAD9kf//+6L///2jAAAD3AAA - wGxjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAGN1cnYAAAAAAAAAAQHNAAB2 - Y2d0AAAAAAAAAAAAAwEAAAIAAAFbAvMEeAXvB2kI4wplC+INVw7TEFIRyhNFFL8WOhe1 - GSsaoBwZHZAfCCB9Ie8jYyTUJkYnsikgKo0r9i1gLsUwLDGOMu80TjWpNwI4WzmwOwQ8 - VT2kPvBAO0GDQsdEDEVORo1Hy0kGSkFLeUywTeVPGVBMUXpSqlPYVQRWMFdbWIJZqVrQ - W/ZdG149X2FggWGhYsBj3mT7ZhhnM2hOaWhqf2uWbK1twm7Xb+tw/XIPcx90L3U+dkt3 - WHhkeW56eHuAfIh9j36Uf5mAnIGegqCDoYShhaCGnoebiJiJlIqPi4mMg418jnOPa5Bi - kViSTpNDlDiVLJYglxSYB5j5meqa3JvNnL6drZ6dn4ugeqFnolWjQqQupRqmBqbxp9uo - xamvqpirgKxorVCuN68dsAOw6LHNsrKzlrR5tVy2P7chuAK45LnEuqS7hLxjvUG+H779 - v9nAtsGRwmvDRcQfxPfFz8amx3zIUskmyfrKzcuezG7NPs4MztrPptBx0TvSBNLN05PU - WNUd1eDWotdi2CLY4dme2lrbFtvQ3IndQd353q/fZOAZ4M3hgOIy4uTjleRF5PblpeZU - 5wTns+hi6RHpwOpw6yDr0OyB7TLt5O6Y70zwAfC38W7yJ/Lh85z0WfUX9db2l/da+B34 - 4vmo+nD7OPwC/Mv9lv5h/y///wAAAVsC8wRbBdgHVgjSCkgLvg00DqsQIRGWExIUhBX7 - F24Y5BpUG8YdOh6sIBwhiiL6JGYl0Cc7KKIqCCttLM8uMi+RMO4yRjOfNPY2STebOOk6 - Njt+PMY+Cj9NQI5BzEMJRENFe0awR+VJGEpJS3hMpk3TTv1QJ1FRUnhTnVTCVedXClgt - WU9abluOXK1dy17oYAVhIWI7Y1ZkbmWHZp5ntWjLad9q82wHbRluKm87cEpxWHJmc3N0 - f3WKdpR3nHikeat6sXu3fLt9v37Bf8KAw4HDgsODwYS/hb2GuYe0iK+Jqoqli56Ml42Q - joiPf5B2kWySY5NZlE6VQ5Y4ly2YIpkWmgma/ZvwnOKd1J7Gn7igqqGbooyjfKRtpVym - TKc7qCqpGqoIqvar5KzSrb+urK+asIaxc7Jfs0u0N7Uitg22+bfjuM65uLqiu4y8db1e - vke/L8AXwP7B5sLNw7PEmcV+xmPHR8gryQ7J8crTy7TMlc10zlPPMtAQ0OzRyNKj037U - WNUw1gjW4Ne22IvZX9oz2wbb2Nyp3XneSd8X3+XgsuF+4knjFOPe5KflcOY35v7nxOiI - 6UzqEOrR65LsUu0R7c7uiu9F7/7wtfFq8h7y0POA9C702vWD9ir2z/dx+BL4r/lK+eT6 - evsQ+6L8NPzD/VH93/5q/vX/ev//AAABAwIsAz8EVQVoBn4HlgipCb0K1gvrDQIOFA8n - EDwRURJiE3UUhxWZFqsXuhjKGdka5xv2HQIeCh8WIB8hJSIrIy4kMCUxJjEnLigqKSYq - HSsTLAgs+i3rLtsvxzCyMZwyhTNsNFI1NjYXNvg32Di4OZU6cjtOPCo9BT3ePrg/kUBp - QUJCGkLyQ8pEokV6RlJHKkgCSNpJsUqKS2NMO00UTe1Oxk+gUHlRU1ItUwZT4FS5VZRW - bldIWCJY/FnWWrBbilxlXT1eF17xX8pgo2F8YlZjLmQHZN9lt2aOZ2ZoPWkUaetqwmuY - bG5tRG4Zbu5vxHCYcW1yQHMUc+h0u3WNdmB3MngEeNZ5qHp5e0p8G3zsfbx+jX9dgC6A - /oHPgp+DcIRBhRKF44a0h4eIWIkqif2K0YukjHiNTI4hjvaPzJCikXmSUJMolACU2ZWy - loyXZphBmRyZ+JrVm7Gcj51tnkufKqAKoOqhzKKto4+kcqVWpjqnH6gFqOup06q7q6Ss - jq15rmWvUrBAsS+yH7MRtAS0+LXutuW33bjXudO60LvQvNK91r7cv+XA8cH/wxDEJcU9 - xlnHeMicycTK88wlzV7OnM/g0SvSfdPV1TjWoNgR2YvbDdyX3izfyeFv4x/k1+aa6GTq - N+wR7fPv3fHN88X1w/e9+cD7yv3c//8AAG5kaW4AAAAAAAAGNgAAlxAAAFckAABTogAA - h5oAAChVAAAWqAAAUA0AAFQ5AAJZmQACXrgAAWZmAAMBAAACAAAAAgAFAAsAEgAbACYA - MgBAAE8AXwBxAIQAmQCvAMYA3gD4ARMBLwFNAWsBiwGsAc4B8gIXAj0CZAKMArYC4AMM - AzkDaAOXA8gD+gQtBGIEmATPBQcFQQV8BbgF9gY1BnUGtwb6Bz8HhQfNCBYIYQitCPsJ - SgmbCe4KQgqYCvALSQukDAEMYAzADSINhg3sDlMOvQ8oD5UQBBB0EOcRWxHREkoSxBM/ - E70UPRS+FUEVxxZOFtcXYhfuGH0ZDhmgGjUayxtkG/4cmx05HdkefB8gH8YgbyEaIcYi - dSMmI9kkjiVGJf8muyd5KDko/CnBKogrUiweLO0tvi6RL2cwPzEaMfgy2DO6NJ81hzZx - N144TTk/OjM7KjwjPR8+Hj8eQCJBKEIwQztESEVYRmpHfkiVSa9Ky0vpTQpOLk9UUH1R - qFLWVAZVOVZvV6dY4logW2Bco13pXzJgfWHLYxxkcGXHZyBofGncaz5so24Lb3Zw43JU - c8h1P3a5eDd5t3s7fMJ+TX/cgW6DBISehjyH3omEiy+M346TkE2SC5PPlZiXZ5k8mxac - 957eoMyiwKS7pryoxarUrOuvCLEts1i1irfDugO8Sb6UwOXDPMWXx/XKWMy8zyPRitPy - 1lrYwNsk3Ybf5uJC5Jvm8elD65Lt3/Ap8nD0tvb5+T/7g/3F//8AAAACAAUACwATABwA - JwAzAEEAUABhAHMAhwCcALIAygDjAP0BGQE1AVMBcwGTAbUB2AH8AiICSQJxApoCxALw - Ax0DSwN7A6sD3QQRBEUEewSyBOsFJQVgBZ0F2wYaBlsGnQbhBycHbge2CAAITAiZCOgJ - OQmLCeAKNQqNCucLQgufC/4MXwzBDSYNjA31Dl8Oyw85D6kQGxCPEQQRfBH2EnES7hNt - E+8UchT2FX0WBhaQFxwXqxg7GM0ZYRn3Go4bKBvDHGEdAB2iHkUe6x+SIDsg5yGVIkQi - 9iOqJGAlGCXTJpAnTygQKNMpmSphKywr+SzILZoubi9EMB0w+THXMrczmjR/NWc2UTc9 - OCw5HjoSOwg8ADz7Pfk++D/6QP5CBUMORBlFJkY2R0hIXElySopLpUzCTeFPA1AnUU1S - dVOfVMxV+1ctWGBZllrOXAldRV6EX8ZhCWJPY5dk4mYuZ31ozmoia3dsz24qb4Zw5XJG - c6p1EHZ4d+N5UHrAfDN9qH8fgJmCF4OWhRmGn4goibSLQ4zVjmuQBJGhk0GU5ZaNmDmZ - 6ZucnVSfEKDQopWkXqYrp/2p1KuvrY+vc7Fds0u1Prc1uTK7M706v0bBV8NuxYvHr8nZ - zArOQ9CE0s7VJNeE2fDcat7z4Y3kOOb26crss++z8sn19/ky/IL//wAAAAMACQATACAA - MABDAFgAcACLAKgAxwDpAQ0BNAFdAYgBtQHlAhcCSwKCAroC9QMzA3IDtAP4BD4EhwTT - BSAFcAXDBhgGcAbLBygHiAfrCFEIuQklCZQKBgp8CvQLcAvwDHMM+Q2DDhAOoQ82D84Q - aREIEasSURL6E6YUVhUJFb8WeBc0F/IYtBl4Gj8bCBvUHKMddB5HHx0f9SDQIawijCNt - JFElOCYhJwwn+SjqKdwq0SvJLMMtvy6/L8AwxTHMMtYz4jTxNgM3GDgwOUo6ZzuHPKo9 - zz74QCNBUkKDQ7dE7kYpR2ZIpknqSzBMek3GTxVQZ1G9UxRUb1XNVy1Yj1n0W1xcxV4x - X59hD2KAY/RlaWbfaFhp0WtMbMluR2/GcUZyx3RKdc53U3jZemB76H1xfvuAh4ITg6CF - L4a+iE6J3otwjQKOlZAokbyTUZTmlnuYEJmmmzyc0Z5nn/2hkqMnpLymUKfkqXarCKyZ - rimvuLFGstK0XLXkt2u477pxu/G9br7owF/B08NExLHGG8eByOLKQMuazO/OQc+O0NbS - G9Nb1JjVztcC2DHZXdqD26fcx93j3vzgEeEi4jHjPORF5UvmTedO6EvpR+pA6zjsLe0g - 7hLvAe/x8N3xyfKz8530hPVr9lD3Nfgd+QT56PrM+6/8kP1v/k7/Jv//AABkZXNjAAAA - AAAAAApDb2xvciBMQ0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAA - AAAPAAAADG5iTk8AAAASAAAAxHN2U0UAAAAQAAAA1mZpRkkAAAAQAAAA5mRhREsAAAAc - AAAA9npoQ04AAAAMAAABEmZyRlIAAAAWAAABHmphSlAAAAAOAAABNGVuVVMAAAASAAAB - QnB0QlIAAAAYAAABVGVzRVMAAAASAAABbHpoVFcAAAAOAAABfmtvS1IAAAAMAAABjGRl - REUAAAAQAAABmG5sTkwAAAAWAAABqGl0SVQAAAAUAAABvgBGAGEAcgBnAGUALQBMAEMA - RABGAOQAcgBnAC0ATABDAEQAVgDkAHIAaQAtAEwAQwBEAEwAQwBEAC0AZgBhAHIAdgBl - AHMAawDmAHIAbV9pgnIAIABMAEMARABMAEMARAAgAGMAbwB1AGwAZQB1AHIwqzDpMPwA - IABMAEMARABDAG8AbABvAHIAIABMAEMARABMAEMARAAgAGMAbwBsAG8AcgBpAGQAbwBM - AEMARAAgAGMAbwBsAG8Acl9pgnJtsmZ2mG95OlZozuy37AAgAEwAQwBEAEYAYQByAGIA - LQBMAEMARABLAGwAZQB1AHIAZQBuAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAaQAA - bW1vZAAAAAAAAAYQAACcXgAAAADAJqAAAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29w - eXJpZ2h0IEFwcGxlLCBJbmMuLCAyMDA5AA== - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - FitInWindow - - Frame - {{66, 0}, {716, 778}} - ListView - - OutlineWidth - 142 - RightSidebar - - Sidebar - - SidebarWidth - 138 - VisibleRegion - {{-102.812, 1.09403}, {783.325, 731.906}} - Zoom - 0.91405183076858521 - ZoomValues - - - Canvas 1 - 0.0 - 1 - - - - saveQuickLookFiles - YES - - diff --git a/libtorrent_utp/docs/storage.png b/libtorrent_utp/docs/storage.png deleted file mode 100644 index 64bf13035ed24b6e013e61caef47cdb1a58aca19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30224 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�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 { font-size: 13px; } - -.entry { min-height: 135px; } - -#main { - font-family: Verdana; - text-align: left; - margin-top: 10px; -} - -/* Base elements */ - -* {margin: 0; padding: 0;} -body { font: 0.8125em Verdana, sans-serif;} - -h1, h2, h3 { - font: 1.5em Georgia "Times New Roman", serif; - letter-spacing: 1px; - padding-bottom: 0.5em; -} - -h1 { font-size: 180%; } -h2 { font-size: 130%; } -h3 { font-size: 100%; } - -p { - font-size: 92%; - line-height: 1.4em; -} - -a { - text-decoration: none; - color: #8D370A; - border-bottom: dotted 1px #8D370A; -} - -a:hover { - border-bottom: solid 1px #8D370A; - background: #eee; -} - -ul, ol { /*line-height: 1.8em;*/ } -ul { list-style: square; } -li { margin-left: 2.8em; font-size: 92%; } - -p, ul, ol, img {margin-bottom: 1em;} - - -.document { - margin-left: 10px; - margin-right: 10px; -} - -.document a { - border: none; - color: black; -} - -.document a:hover { - background: none; -} - -.document a.reference { - color: #8D370A; - border-bottom: dotted 1px #8D370A; -} - -.document a.reference:hover { - border-bottom: solid 1px #8D370A; - background: #eee; -} - -div.section { - margin-bottom: 3em; -} - -div.section div.section div.section { - margin-bottom: 2em; -} - -h3 { text-transform: uppercase; } - -div.section p, div.section ul, div.section dl { -} - -table.docinfo { - text-align: left; - float: right; - width: 200px; - margin-right: 0px; - margin-left: 20px; - margin-bottom: 20px; -} - -table.docinfo th { - border-top: none; - font-size: 72%; - padding-left: 10px; -} - -table.docinfo td { - padding-left: 10px; - font-size: 88%; -} - -table.docinfo tr.field td, table.docinfo tr.field th {display: none;} - -h1.title { text-align: center; } - -dt { - font-size: 100%; - letter-spacing: 2px; - line-height: 1em; - color: #315586; - color: #000; - font-family: Tahoma; - font-weight: bold; -} - -dd { -/* line-height: 1.5em;*/ - margin-left: 1em; - margin-bottom: 1em; - font-size: 92%; -} - -tt { - font: 1em "Courier New", "Courier"; - color: #315566; -} - -pre { - font-family: "Courier", monospace; - margin-right: 10px; - background: #C1E5F6; - border-left: solid 2px #6185A6; - border-right: solid 2px #6185A6; - padding: 5px 10px 5px 10px; - - background: #f6f6f6; - border: solid 1px #ddd; - margin: 1em 0; -} - -div.warning, div.note, div.important { - width: 80%; - margin: 1.5em auto; - background: #C1E5F6; - background: #F1FFF5; - border: solid 1px #D1DFD5; - padding: 5px 10px 5px 10px; -} - -p.admonition-title { - font-family: Georgia, "Lucida Grande"; - font-size: 128%; - letter-spacing: 2px; - text-transform: uppercase; - margin: 0 0 0.5em 0; - border-bottom: solid 1px #D1DFD5 -} - -div.sidebar { - background: #f8f8e8; - float: right; - width: 20em; - margin-right: 1em; - border: solid 1px #e5e5d5; - padding: 1.3em; -} - -div.sidebar p.sidebar-title { - font: 1.3em Georgia; - border-bottom: solid 1px #e5e5d5; - padding-bottom: 0.5em; - margin: 0 0 0.5em 0; -} - -h1 { font-size: 230%; } -h2 { font-size: 180%; } -h3 { font-size: 130%; } - -table { margin-bottom: 1em; border-collapse: collapse; } -table, th, td { border: none; } - -th, td { padding: 0.3em; } - -th { - text-align: left; - background: #f0f0e0; - border-right: solid 1px #f0f0e0; - border-top: solid 1px #e8e8d8; - border-bottom: solid 1px #e8e8d8; -} - -td { - background: #f8f8e8; - border-right: solid 1px #f8f8e8; - border-bottom: solid 1px #e8e8d8; -} - -td td { - background: #e8e8d8; - border-right: solid 1px #e8e8d8; - border-bottom: solid 1px #d8d8c8; -} - -div.topic { - border-left: solid 1px #eee; - padding-left: 1em; - margin: 0 0 1.5em; -} - -p.topic-title { - font: 1.3em Georgia, "Times New Roman", serif; -} - -/* TOC */ - -div.contents { - border: none; -} - -#table-of-contents { - margin-left: 20px; - padding: 0 0 1em; - width: 200px; - float: right; - clear: right; - border-right: solid 1px #A1C5D6; -} - -#table-of-contents p { - font-family: Georgia, "Times New Roman", serif; - color: #AD370A; - padding: 0.5em; - margin: 0; -} - -#table-of-contents li { - margin: 0 0.5em 0 0.5em; -} - -#table-of-contents ul { - margin: 0; - padding: 0 0 0 0.8em; - list-style: none; - text-align: left; -} - -#table-of-contents a.reference { - border: none; - font: 0.88em Tahoma; - font-weight: bold; - color: #000050; - margin-right: 1em; - padding-left: 15px; -} - -#table-of-contents li li a.reference { - font-weight: normal; - background: none; - padding: 0; -} - -#table-of-contents a.reference:hover {text-decoration: underline;} - -dd p { - font-size: 100%; -} - -dd pre { - font-size: 108.7%; -} - -li p, li li { font-size: 100%; } - -#librarySidebar { - float: left; - width: 150px; -} - -#libraryBody { - border-left: solid 1px #eee; - padding-left: 10px; - margin-left: 158px; - margin-right: 10px; -} - -/* IE Hacks */ - -/* Hides from IE-mac \*/ -* html li pre { height: 1%; } -* html .topic pre { height: 1%; } -* html #table-of-contents ul ul { height: 1%; } -/* End hide from IE-mac */ - diff --git a/libtorrent_utp/docs/stylesheet b/libtorrent_utp/docs/stylesheet deleted file mode 100644 index caa6def02..000000000 --- a/libtorrent_utp/docs/stylesheet +++ /dev/null @@ -1,287 +0,0 @@ -{ - "embeddedFonts" : [ ], - "pageSetup" : { - "size": "A4", - "width": null, - "height": null, - "margin-top": "2cm", - "margin-bottom": "2cm", - "margin-left": "2cm", - "margin-right": "2cm", - "margin-gutter": "0cm", - "firstTemplate": "oneColumn" - }, - "pageTemplates" : { - "coverPage": { - "frames": [ - ["0cm", "0cm", "100%", "100%"] - ], - "showHeader" : false, - "showFooter" : false - }, - "oneColumn": { - "frames": [ - ["0cm", "0cm", "100%", "100%"] - ] - }, - "twoColumn": { - "frames": [ - ["0cm", "0cm", "49%", "100%"], - ["51%", "0cm", "49%", "100%"] - ] - } - }, - "fontsAlias" : { - "stdFont": "Times-Roman", - "stdBold": "Times-Bold", - "stdItalic": "Times-Italic", - "stdBoldItalic": "Times-BoldItalic", - "stdMono": "Courier", - "stdMonoItalic": "Courier-Oblique", - "stdMonoBold": "Courier-Bold", - "stdMonoBoldItalic": "Courier-BoldOblique", - "stdSerif": "Times-Roman" - }, - "linkColor" : "black", - "styles" : [ - [ "base" , { - "parent": null, - "fontName": "stdFont", - "fontSize":10, - "leading":12, - "leftIndent":0, - "rightIndent":0, - "firstLineIndent":0, - "alignment":"TA_LEFT", - "spaceBefore":0, - "spaceAfter":0, - "bulletFontName":"stdFont", - "bulletFontSize":10, - "bulletIndent":0, - "textColor": "black", - "backColor": null, - "wordWrap": null, - "borderWidth": 0, - "borderPadding": 0, - "borderColor": null, - "borderRadius": null, - "allowWidows": false, - "allowOrphans": false, - "hyphenation": false - }] , - ["normal" , { - "parent": "base" - }], - ["title_reference" , { - "parent": "normal", - "fontName": "stdItalic" - }], - ["bodytext" , { - "parent": "normal", - "spaceBefore":6, - "alignment": "TA_JUSTIFY", - "hyphenation": true - }], - ["footer" , { - "parent": "normal", - "alignment": "TA_CENTER" - }], - ["header" , { - "parent": "normal", - "alignment": "TA_CENTER" - }], - ["attribution" , { - "parent": "bodytext", - "alignment": "TA_RIGHT" - }], - ["figure" , { - "parent": "bodytext", - "alignment": "TA_CENTER" - }], - ["definition_list_term" , { - "parent": "normal", - "fontName": "stdBold", - "spaceBefore": 4, - "spaceAfter": 0, - "keepWithNext": true - }], - ["definition_list_classifier" , { - "parent": "normal", - "fontName": "stdItalic" - }], - ["definition" , { - "parent": "bodytext", - "firstLineIndent": 0, - "bulletIndent": 0, - "spaceBefore": 0 - }], - ["fieldname" , { - "parent": "bodytext", - "alignment": "TA_RIGHT", - "fontName": "stdBold" - }], - ["rubric" , { - "parent": "bodytext", - "textColor": "darkred", - "alignment": "TA_CENTER" - }], - ["italic" , { - "parent": "bodytext", - "fontName": "stdItalic" - }], - ["title" , { - "parent": "normal", - "fontName": "stdBold", - "fontSize": "200%", - "alignment": "TA_CENTER", - "spaceBefore": 12, - "spaceAfter": 10 - }], - ["subtitle" , { - "parent": "title", - "spaceBefore": 9, - "spaceAfter": 6, - "fontSize": "75%" - }], - ["heading1" , { - "parent": "normal", - "fontName": "stdBold", - "fontSize": "150%", - "keepWithNext": true, - "spaceBefore": 9, - "spaceAfter": 3 - }], - ["heading2" , { - "parent": "normal", - "fontName": "stdBold", - "fontSize": "125%", - "keepWithNext": true, - "spaceBefore": 9, - "spaceAfter": 3 - }], - ["heading3" , { - "parent": "normal", - "fontName": "stdBold", - "keepWithNext": true, - "spaceBefore": 4, - "spaceAfter": 2 - }], - ["heading4" , { - "parent": "normal", - "fontName": "stdBold", - "keepWithNext": true - }], - ["sidebar-title" , { - "parent": "heading3" - }], - ["sidebar-subtitle" , { - "parent": "heading4" - }], - ["sidebar" , { - "float": "left", - "width": "30%", - "parent": "normal", - "backColor": "beige", - "borderColor": "darkgray", - "borderPadding": 8, - "borderWidth": 0.5 - }], - ["literal" , { - "parent": "normal", - "fontName": "stdMono", - "firstLineIndent": 0 - }], - ["table" , { - "rowBackgrounds" : ["f0f0d8","#ffffe8"], - "borderColor": "white" - }], - ["table-title" , { - "parent" : "heading4", - "backColor" : "#e0e0c8", - "alignment" : "TA_CENTER" - }], - ["table-heading" , { - "parent" : "heading4", - "backColor" : "#e0e0c0", - "alignment" : "TA_CENTER", - "valign" : "BOTTOM" - }], - ["code" , { - "parent": "literal", - "fontSize": "75%", - "leftIndent": 0, - "spaceBefore": 5, - "spaceAfter": 5, - "backColor": "#e7e7e7", - "borderColor": "#808080", - "borderRadius": 3, - "borderWidth": 0.5, - "borderPadding": 4 - }], - ["pygments-n" , {"parent": "code"}], - ["pygments-nx" , {"parent": "code"}], - ["pygments-p" , {"parent": "code"}], - ["pygments-hll", {"parent": "code", "backColor": "#ffffcc"}], - ["pygments-c", {"textColor": "#008800", "parent": "code"}], - ["pygments-err", {"parent": "code"}], - ["pygments-k", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-o", {"textColor": "#666666", "parent": "code"}], - ["pygments-cm", {"textColor": "#008800", "parent": "code"}], - ["pygments-cp", {"textColor": "#008800", "parent": "code"}], - ["pygments-c1", {"textColor": "#008800", "parent": "code"}], - ["pygments-cs", {"textColor": "#008800", "parent": "code"}], - ["pygments-gd", {"textColor": "#A00000", "parent": "code"}], - ["pygments-ge", {"parent": "code"}], - ["pygments-gr", {"textColor": "#FF0000", "parent": "code"}], - ["pygments-gh", {"textColor": "#000080", "parent": "code"}], - ["pygments-gi", {"textColor": "#00A000", "parent": "code"}], - ["pygments-go", {"textColor": "#808080", "parent": "code"}], - ["pygments-gp", {"textColor": "#000080", "parent": "code"}], - ["pygments-gs", {"parent": "code"}], - ["pygments-gu", {"textColor": "#800080", "parent": "code"}], - ["pygments-gt", {"textColor": "#0040D0", "parent": "code"}], - ["pygments-kc", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-kd", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-kn", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-kp", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-kr", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-kt", {"textColor": "#00BB00", "parent": "code"}], - ["pygments-m", {"textColor": "#666666", "parent": "code"}], - ["pygments-s", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-na", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-nb", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-nc", {"textColor": "#0000FF", "parent": "code"}], - ["pygments-no", {"textColor": "#880000", "parent": "code"}], - ["pygments-nd", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-ni", {"textColor": "#999999", "parent": "code"}], - ["pygments-ne", {"textColor": "#D2413A", "parent": "code"}], - ["pygments-nf", {"textColor": "#00A000", "parent": "code"}], - ["pygments-nl", {"textColor": "#A0A000", "parent": "code"}], - ["pygments-nn", {"textColor": "#0000FF", "parent": "code"}], - ["pygments-nt", {"textColor": "#008000", "parent": "code"}], - ["pygments-nv", {"textColor": "#B8860B", "parent": "code"}], - ["pygments-ow", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-w", {"textColor": "#bbbbbb", "parent": "code"}], - ["pygments-mf", {"textColor": "#666666", "parent": "code"}], - ["pygments-mh", {"textColor": "#666666", "parent": "code"}], - ["pygments-mi", {"textColor": "#666666", "parent": "code"}], - ["pygments-mo", {"textColor": "#666666", "parent": "code"}], - ["pygments-sb", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-sc", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-sd", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-s2", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-se", {"textColor": "#BB6622", "parent": "code"}], - ["pygments-sh", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-si", {"textColor": "#BB6688", "parent": "code"}], - ["pygments-sx", {"textColor": "#008000", "parent": "code"}], - ["pygments-sr", {"textColor": "#BB6688", "parent": "code"}], - ["pygments-s1", {"textColor": "#BB4444", "parent": "code"}], - ["pygments-ss", {"textColor": "#B8860B", "parent": "code"}], - ["pygments-bp", {"textColor": "#AA22FF", "parent": "code"}], - ["pygments-vc", {"textColor": "#B8860B", "parent": "code"}], - ["pygments-vg", {"textColor": "#B8860B", "parent": "code"}], - ["pygments-vi", {"textColor": "#B8860B", "parent": "code"}], - ["pygments-il", {"textColor": "#666666", "parent": "code"}] - ] -} - diff --git a/libtorrent_utp/docs/t2e.jpg b/libtorrent_utp/docs/t2e.jpg deleted file mode 100644 index f8be0e6bd78dc4242b3f4a40457ae190e22b8a7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2368 zcmbV}c{mi@9>>pY%vgqzUA@zy$a+x`V~HZN6xn66rLkp64aS;mL%n3EsW++cYLteE zC|R;qq{vdTOv#!VSq7uIGu@u<^W4Agz2EbkbH2}WzQ6OF&+l*txT8Sa%G}Z%Kp+rs zm^T1-3?v*P5>5eNV*^wG07!swS0w<43!qU*6hHC5iTfO2kU$Nn!ywxM6a#@_ zAlw#!<&hwG{o=Rmmmp9W93ddcdy_CPP$dqa5Eu*!hw_50LJT5344ucEwxggNO8^B>V5clKwBU-(dgd8Ui9P2=DV?7+?%GD&8%Qq;df~4(d*L5?oy{ zhJ{Z$ktM^-Q-QDYDDaSU5Fd z9scm^gMnHPE+{ipwP2(?jv8+uV+WWzMi&Ze5Bu03&YOIoczxs)sc6zM`Nr6S@xEv3 z9B-Bx@w+1v-^>M%6x@-+RkW8|59X{!X{$)BXBXmF{aeE>;4}O=u9sg|M)|q=u6o&W zzCW$*p)K3l><*~Lx$9Vcu`K^_!eaG35jOsoQaP1X%PvTS`Hs*F{3LZN;QsxU(6po`S5%=F{hOqj!}D>zIXB{E=BA zl4H@6u=SFyfML{a4+W=%wd%d=E;D_B3&AuF7dQx!EK+iN1HAe*+c!4Y?yS)n>g$4w zoY-K$;=q11ov=o^S1~x!;Fy`B)NyrLPWdyb424u;zgyo)5@&BPM0HxQ7?SGpa7e2_ zqGs6_aT{%k8dn#w8yX||hPv{cRkj&}h9@@&`PJ=Xxz9+K%nm)h%>}}YaH|?a1w?9S zW9?+ylFaBl`3&v|Ge@~9-7PlII(O)V?QjSHZCXoe$su}jp={X~(H{}5dSy;3#g12} z(n|VQ^YNntltW5btkWA!;HTuKjrH%iJ zb0ML`Kah^Ha@Wf8=lsJ)=d28B)lCS4ax$-!Wk=gBtg}02J~i0HDYEmhDB`*+vu{?w z+vA=|n~)cYqSSlx$IaFTsd|S}Hf=1vts65HO{oi%Kk+*2MNvJ}A$ZH{tfo0jpU^U+ zZ*Sa1Q$+42Y2mYpF{ahhQP0p1sy>!Kar3mCUN?R;g^3jR@qD@0Bne_A!VeEA^bJL{gE7Nh>J-7pRCvSp&j2M1H}~4`VI%S@w(Y6dv2|sFjHk@T z6Apf~wRYXHV&Ic&_x9qx^N;^BwG!kYSU*;$@U&;Ly-LiY_jfd|2XB5&q?aY!l-#w@ zaJegf8~)M=ojS9IzB6u|PMyuS9||y-J?o+|LGV5M-K=Ze>?P<-Q=nmW5+C)?er@y- z%fJc7mfJycIteXKcQdhnZW%e22Q#|0=KS?RR#1F;Zb?es!;<;61*>^G)}iQv!Z&yH zakwH!!fwJ!IeS<9oG>$*F)o&JtDj^F^(WQd-uoZdDzAdS)HYQ86ENzt{t!l|7UBCW$X#cAgDxpP&Egu$RC z(yEfU*+7|q#^%LO!#!-o-#^%)m<1zIz5#Dy~dktdh3@BU>B_VfQ%xgz%U_>*% zQupGXSaa2R1tJwIYbggOL~pilChQh8I8{TDFum4#Y=_5kB(A?NPWw#|$B^-1L#6A; zwfm#78SftYdnJU2g6=m$E7-(%6iJma5z~M%qG_8BtmAZrvfCk(Z6UK^gBRm==d;Nz zwZ*&igfwm_sqeq9X*(DHxgl(^V$9=?uvl4#)sdJJ@%Z%5Bj{)HMDn;@*8M$M@?BdD z4n979gq)k|S6u*QU~XrkWfEv>zSIpD=1A%+ZtrNYrpa6J#*zW|GA*c?j!(|37`kFk ztYyh*H+0rmH`=Ut(k=KV#Y?WKF - -%(stylesheet)s - -%(body_prefix)s -
- - -
-%(body_pre_docinfo)s -%(docinfo)s -%(body)s -
- -
- - -%(body_suffix)s diff --git a/libtorrent_utp/docs/tonidoplug.png b/libtorrent_utp/docs/tonidoplug.png deleted file mode 100644 index 1e30d7c812d1eb72595fa64af449923e1e9930df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18260 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 diff --git a/libtorrent_utp/docs/tuning.html b/libtorrent_utp/docs/tuning.html deleted file mode 100644 index dce4511d4..000000000 --- a/libtorrent_utp/docs/tuning.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - -libtorrent manual - - - - - - - -
-
-
- -
- -
-

libtorrent manual

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
- -
-

tuning libtorrent

-

libtorrent expose most constants used in the bittorrent engine for -customization through the session_settings. This makes it possible to -test and tweak the parameters for certain algorithms to make a client -that fits a wide range of needs. From low memory embedded devices to -servers seeding thousands of torrents. The default settings in libtorrent -are tuned for an end-user bittorrent client running on a normal desktop -computer.

-

This document describes techniques to benchmark libtorrent performance -and how parameters are likely to affect it.

-
-
-

reducing memory footprint

-

These are things you can do to reduce the memory footprint of libtorrent. You get -some of this by basing your default session_settings on the min_memory_usage() -setting preset function.

-

Keep in mind that lowering memory usage will affect performance, always profile -and benchmark your settings to determine if it's worth the trade-off.

-

The typical buffer usage of libtorrent, for a single download, with the cache -size set to 256 blocks (256 * 16 kiB = 4 MiB) is:

-
-read cache:      128.6 (2058 kiB)
-write cache:     103.5 (1656 kiB)
-receive buffers: 7.3   (117 kiB)
-send buffers:    4.8   (77 kiB)
-hash temp:       0.001 (19 Bytes)
-
-

The receive buffers is proportional to the number of connections we make, and is -limited by the total number of connections in the session (default is 200).

-

The send buffers is proportional to the number of upload slots that are allowed -in the session. The default is auto configured based on the observed upload rate.

-

The read and write cache can be controlled (see section below).

-

The "hash temp" entry size depends on whether or not hashing is optimized for -speed or memory usage. In this test run it was optimized for memory usage.

-
-

disable disk cache

-

The bulk of the memory libtorrent will use is used for the disk cache. To save -the absolute most amount of memory, you can disable the cache by setting -session_settings::cache_size to 0. You might want to consider using the cache -but just disable caching read operations. You do this by settings -session_settings::use_read_cache to false. This is the main factor in how much -memory will be used by the client. Keep in mind that you will degrade performance -by disabling the cache. You should benchmark the disk access in order to make an -informed trade-off.

-
-
-

remove torrents

-

Torrents that have been added to libtorrent will inevitably use up memory, even -when it's paused. A paused torrent will not use any peer connection objects or -any send or receive buffers though. Any added torrent holds the entire .torrent -file in memory, it also remembers the entire list of peers that it's heard about -(which can be fairly long unless it's capped). It also retains information about -which blocks and pieces we have on disk, which can be significant for torrents -with many pieces.

-

If you need to minimize the memory footprint, consider removing torrents from -the session rather than pausing them. This will likely only make a difference -when you have a very large number of torrents in a session.

-

The downside of removing them is that they will no longer be auto-managed. Paused -auto managed torrents are scraped periodically, to determine which torrents are -in the greatest need of seeding, and libtorrent will prioritize to seed those.

-
-
-

socket buffer sizes

-

You can make libtorrent explicitly set the kernel buffer sizes of all its peer -sockets. If you set this to a low number, you may see reduced throughput, especially -for high latency connections. It is however an opportunity to save memory per -connection, and might be worth considering if you have a very large number of -peer connections. This memory will not be visible in your process, this sets -the amount of kernel memory is used for your sockets.

-

Change this by setting session_settings::recv_socket_buffer_size and -session_settings::send_socket_buffer_size.

-
-
-

peer list size

-

The default maximum for the peer list is 4000 peers. For IPv4 peers, each peer -entry uses 32 bytes, which ends up using 128 kB per torrent. If seeding 4 popular -torrents, the peer lists alone uses about half a megabyte.

-

The default limit is the same for paused torrents as well, so if you have a -large number of paused torrents (that are popular) it will be even more -significant.

-

If you're short of memory, you should consider lowering the limit. 500 is probably -enough. You can do this by setting session_settings::max_peerlist_size to -the max number of peers you want in the torrent's peer list.

-

You should also lower the same limit but for paused torrents. It might even make sense -to set that even lower, since you only need a few peers to start up while waiting -for the tracker and DHT to give you fresh ones. The max peer list size for paused -torrents is set by session_settings::max_paused_peerlist_size.

-

The drawback of lowering this number is that if you end up in a position where -the tracker is down for an extended period of time, your only hope of finding live -peers is to go through your list of all peers you've ever seen. Having a large -peer list will also help increase performance when starting up, since the torrent -can start connecting to peers in parallel with connecting to the tracker.

-
-
-

send buffer watermark

-

The send buffer watermark controls when libtorrent will ask the disk I/O thread -to read blocks from disk, and append it to a peer's send buffer.

-

When the send buffer has fewer than or equal number of bytes as -session_settings::send_buffer_watermark, the peer will ask the disk I/O thread -for more data to send. The trade-off here is between wasting memory by having too -much data in the send buffer, and hurting send rate by starving out the socket, -waiting for the disk read operation to complete.

-

If your main objective is memory usage and you're not concerned about being able -to achieve high send rates, you can set the watermark to 9 bytes. This will guarantee -that no more than a single (16 kiB) block will be on the send buffer at a time, for -all peers. This is the least amount of memory possible for the send buffer.

-

You should benchmark your max send rate when adjusting this setting. If you have -a very fast disk, you are less likely see a performance hit.

-
-
-

optimize hashing for memory usage

-

When libtorrent is doing hash checks of a file, or when it re-reads a piece that -was just completed to verify its hash, there are two options. The default one -is optimized for speed, which allocates buffers for the entire piece, reads in -the whole piece in one read call, then hashes it.

-

The second option is to optimize for memory usage instead, where a single buffer -is allocated, and the piece is read one block at a time, hashing it as each -block is read from the file. For low memory environments, this latter approach -is recommended. Change this by settings session_settings::optimize_hashing_for_speed -to false. This will significantly reduce peak memory usage, especially for -torrents with very large pieces.

-
-
-

reduce executable size

-

Compilers generally add a significant number of bytes to executables that make use -of C++ exceptions. By disabling exceptions (-fno-exceptions on GCC), you can -reduce the executable size with up to 45%. In order to build without exception -support, you need to patch parts of boost.

-

Also make sure to optimize for size when compiling.

-

Another way of reducing the executable size is to disable code that isn't used. -There are a number of TORRENT_* macros that control which features are included -in libtorrent. If these macros are used to strip down libtorrent, make sure the same -macros are defined when building libtorrent as when linking against it. If these -are different the structures will look different from the libtorrent side and from -the client side and memory corruption will follow.

-

One, probably, safe macro to define is TORRENT_NO_DEPRECATE which removes all -deprecated functions and struct members. As long as no deprecated functions are -relied upon, this should be a simple way to eliminate a little bit of code.

-

For all available options, see the building libtorrent secion.

-
-
-

reduce statistics

-

You can save some memory for each connection and each torrent by reducing the -number of separate rates kept track of by libtorrent. If you build with full-stats=off -(or -DTORRENT_DISABLE_FULL_STATS) you will save a few hundred bytes for each -connection and torrent. It might make a difference if you have a very large number -of peers or torrents.

-
-
-
-

play nice with the disk

-

When checking a torrent, libtorrent will try to read as fast as possible from the disk. -The only thing that might hold it back is a CPU that is slow at calculating SHA-1 hashes, -but typically the file checking is limited by disk read speed. Most operating systems -today do not prioritize disk access based on the importance of the operation, this means -that checking a torrent might delay other disk accesses, such as virtual memory swapping -or just loading file by other (interactive) applications.

-

In order to play nicer with the disk, and leave some spare time for it to service other -processes that might be of higher importance to the end-user, you can introduce a sleep -between the disc accesses. This is a direct tradeoff between how fast you can check a -torrent and how soft you will hit the disk.

-

You control this by setting the session_settings::file_checks_delay_per_block to greater -than zero. This number is the number of milliseconds to sleep between each read of 16 kiB.

-

The sleeps are not necessarily in between each 16 kiB block (it might be read in larger chunks), -but the number will be multiplied by the number of blocks that were read, to maintain the -same semantics.

-
-
-

high performance seeding

-

In the case of a high volume seed, there are two main concerns. Performance and scalability. -This translates into high send rates, and low memory and CPU usage per peer connection.

-
-

file pool

-

libtorrent keeps an LRU file cache. Each file that is opened, is stuck in the cache. The main -purpose of this is because of anti-virus software that hooks on file-open and file close to -scan the file. Anti-virus software that does that will significantly increase the cost of -opening and closing files. However, for a high performance seed, the file open/close might -be so frequent that it becomes a significant cost. It might therefore be a good idea to allow -a large file descriptor cache. Adjust this though session_settings::file_pool_size.

-

Don't forget to set a high rlimit for file descriptors in your process as well. This limit -must be high enough to keep all connections and files open.

-
-
-

disk cache

-

You typically want to set the cache size to as high as possible. The -session_settings::cache_size is specified in 16 kiB blocks. Since you're seeding, -the cache would be useless unless you also set session_settings::use_read_cache -to true.

-

In order to increase the possibility of read cache hits, set the -session_settings::cache_expiry to a large number. This won't degrade anything as -long as the client is only seeding, and not downloading any torrents.

-

In order to increase the disk cache hit rate, you can enable suggest messages based on -what's in the read cache. To do this, set session_settings::suggest_mode to -session_settings::suggest_read_cache. This will send suggest messages to peers -for the most recently used pieces in the read cache. This is especially useful if you -also enable explicit read cache, by settings session_settings::explicit_read_cache -to the number of pieces to keep in the cache. The explicit read cache will make the -disk read cache stick, and not be evicted by cache misses. The explicit read cache -will automatically pull in the rarest pieces in the read cache.

-

Assuming that you seed much more data than you can keep in the cache, to a large -numbers of peers (so that the read cache wouldn't be useful anyway), this may be a -good idea.

-

When peers first connect, libtorrent will send them a number of allow-fast messages, -which lets the peers download certain pieces even when they are choked, since peers -are choked by default, this often triggers immediate requests for those pieces. In the -case of using explicit read cache and suggesting those pieces, allowing fast pieces -should be disabled, to not systematically trigger requests for pieces that are not cached -for all peers. You can turn off allow-fast by settings session_settings::allowed_fast_set_size -to 0.

-

As an alternative to the explicit cache and suggest messages, there's a guided cache -mode. This means the size of the read cache line that's stored in the cache is determined -based on the upload rate to the peer that triggered the read operation. The idea being -that slow peers don't use up a disproportional amount of space in the cache. This -is enabled through session_settings::guided_read_cache.

-

In cases where the assumption is that the cache is only used as a read-ahead, and that no -other peer will ever request the same block while it's still in the cache, the read -cache can be set to be volatile. This means that every block that is requested out of -the read cache is removed immediately. This saves a significant amount of cache space -which can be used as read-ahead for other peers. This mode should never be combined -with either explicit_read_cache or suggest_read_cache, since those uses opposite -strategies for the read cache. You don't want to on one hand attract peers to request -the same pieces, and on the other hand assume that they won't request the same pieces -and drop them when the first peer requests it. To enable volatile read cache, set -session_settings::volatile_read_cache to true.

-
-
-

send buffer low watermark

-

libtorrent uses a low watermark for send buffers to determine when a new piece should -be requested from the disk I/O subsystem, to be appended to the send buffer. The low -watermark is determined based on the send rate of the socket. It needs to be large -enough to not draining the socket's send buffer before the disk operation completes.

-

The watermark is bound to a max value, to avoid buffer sizes growing out of control. -The default max send buffer size might not be enough to sustain very high upload rates, -and you might have to increase it. It's specified in bytes in -session_settings::send_buffer_watermark. The high_performance_seed() preset -sets this value to 5 MB.

-
-
-

peers

-

First of all, in order to allow many connections, set the global connection limit -high, session::set_max_connections(). Also set the upload rate limit to -infinite, session::set_upload_rate_limit(), passing 0 means infinite.

-

When dealing with a large number of peers, it might be a good idea to have slightly -stricter timeouts, to get rid of lingering connections as soon as possible.

-

There are a couple of relevant settings: session_settings::request_timeout, -session_settings::peer_timeout and session_settings::inactivity_timeout.

-

For seeds that are critical for a delivery system, you most likely want to allow -multiple connections from the same IP. That way two people from behind the same NAT -can use the service simultaneously. This is controlled by -session_settings::allow_multiple_connections_per_ip.

-

In order to always unchoke peers, turn off automatic unchoke -session_settings::auto_upload_slots and set the number of upload slots to a large -number via session::set_max_uploads(), or use -1 (which means infinite).

-
-
-

torrent limits

-

To seed thousands of torrents, you need to increase the session_settings::active_limit -and session_settings::active_seeds.

-
-
-
-

benchmarking

-

There are a bunch of built-in instrumentation of libtorrent that can be used to get an insight -into what it's doing and how well it performs. This instrumentation is enabled by defining -preprocessor symbols when building.

-

There are also a number of scripts that parses the log files and generates graphs (requires -gnuplot and python).

-
-

disk metrics

-

To enable disk I/O instrumentation, define TORRENT_DISK_STATS when building. When built -with this configuration libtorrent will create three log files, measuring various aspects of -the disk I/O. The following table is an overview of these files and what they measure.

- ---- - - - - - - - - - - - - - - - - -
filenamedescription
disk_io_thread.logThis is a log of which operation the disk I/O thread is -engaged in, with timestamps. This tells you what the thread -is spending its time doing.
disk_buffers.logThis log keeps track of what the buffers allocated from the -disk buffer pool are used for. There are 5 categories. -receive buffer, send buffer, write cache, read cache and -temporary hash storage. This is key when optimizing memory -usage.
disk_access.logThis is a low level log of read and write operations, with -timestamps and file offsets. The file offsets are byte -offsets in the torrent (not in any particular file, in the -case of a multi-file torrent). This can be used as an -estimate of the physical drive location. The purpose of -this log is to identify the amount of seeking the drive has -to do.
-
-

disk_io_thread.log

-

The structure of this log is simple. For each line, there are two columns, a timestamp and -the operation that was started. There is a special operation called idle which means -it looped back to the top and started waiting for new jobs. If there are more jobs to -handle immediately, the idle state is still there, but the timestamp is the same as the -next job that is handled.

-

Some operations have a 3:rd column with an optional parameter. read and write tells -you the number of bytes that were requested to be read or written. flushing tells you -the number of bytes that were flushed from the disk cache.

-

This is an example excerpt from a log:

-
-3702 idle
-3706 check_fastresume
-3707 idle
-4708 save_resume_data
-4708 idle
-8230 read 16384
-8255 idle
-8431 read 16384
-
-

The script to parse this log and generate a graph is called parse_disk_log.py. It takes -the log file as the first command line argument, and produces a file: disk_io.png. -The time stamp is in milliseconds since start.

-

You can pass in a second, optional, argument to specify the window size it will average -the time measurements over. The default is 5 seconds. For long test runs, it might be interesting -to increase that number. It is specified as a number of seconds.

-disk_io.png -

This is an example graph generated by the parse script.

-
-
-

disk_buffers.log

-

The disk buffer log tells you where the buffer memory is used. The log format has a time stamp, -the name of the buffer usage which use-count changed, colon, and the new number of blocks that are -in use for this particular key. For example:

-
-23671 write cache: 18
-23671 receive buffer: 3
-24153 receive buffer: 2
-24153 write cache: 19
-24154 receive buffer: 3
-24198 receive buffer: 2
-24198 write cache: 20
-24202 receive buffer: 3
-24305 send buffer: 0
-24305 send buffer: 1
-24909 receive buffer: 2
-24909 write cache: 21
-24910 receive buffer: 3
-
-

The time stamp is in milliseconds since start.

-

To generate a graph, use parse_disk_buffer_log.py. It takes the log file as the first -command line argument. It generates disk_buffer.png.

-disk_buffer_sample.png -

This is an example graph generated by the parse script.

-
-
-

disk_access.log

-

The disc access log has three fields. The timestamp (milliseconds since start), operation -and offset. The offset is the absolute offset within the torrent (not within a file). This -log is only useful when you're downloading a single torrent, otherwise the offsets will not -be unique.

-

In order to easily plot this directly in gnuplot, without parsing it, there are two lines -associated with each read or write operation. The first one is the offset where the operation -started, and the second one is where the operation ended.

-

Example:

-
-15437 read 301187072
-15437 read_end 301203456
-16651 read 213385216
-16680 read_end 213647360
-25879 write 249036800
-25879 write_end 249298944
-26811 read 325582848
-26943 read_end 325844992
-36736 read 367001600
-36766 read_end 367263744
-
-

The disk access log does not have any good visualization tool yet. There is however a gnuplot -file, disk_access.gnuplot which assumes disk_access.log is in the current directory.

-disk_access.png -

The density of the disk seeks tells you how hard the drive has to work.

-
-
-
-

session stats

-

By defining TORRENT_STATS libtorrent will write a log file called session_stats.log which -is in a format ready to be passed directly into gnuplot. The parser script parse_session_stats.py -will however parse out the field names and generate 3 different views of the data. This script -is easy to modify to generate the particular view you're interested in.

-

The first line in the log contains all the field names, separated by colon:

-
-second:upload rate:download rate:downloading torrents:seeding torrents:peers...
-
-

The rest of the log is one line per second with all the fields' values.

-

These are the fields:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
field namedescription
secondthe time, in seconds, for this log line
upload ratethe number of bytes uploaded in the last second
download ratethe number of bytes downloaded in the last second
downloading torrentsthe number of torrents that are not seeds
seeding torrentsthe number of torrents that are seed
peersthe total number of connected peers
connecting peersthe total number of peers attempting to connect (half-open)
disk block buffersthe total number of disk buffer blocks that are in use
unchoked peersthe total number of unchoked peers
num list peersthe total number of known peers, but not necessarily connected
peer allocationsthe total number of allocations for the peer list pool
peer storage bytesthe total number of bytes allocated for the peer list pool
-

This is an example of a graph that can be generated from this log:

-session_stats_peers.png -

It shows statistics about the number of peers and peers states. How at the startup -there are a lot of half-open connections, which tapers off as the total number of -peers approaches the limit (50). It also shows how the total peer list slowly but steadily -grows over time. This list is plotted against the right axis, as it has a different scale -as the other fields.

-
-
-
-

contributions

-

If you have added instrumentation for some part of libtorrent that is not covered here, or -if you have improved any of the parser scrips, please consider contributing it back to the -project.

-

If you have run tests and found that some algorithm or default value in libtorrent is -suboptimal, please contribute that knowledge back as well, to allow us to improve the library.

-

If you have additional suggestions on how to tune libtorrent for any specific use case, -please let us know and we'll update this document.

-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/tuning.rst b/libtorrent_utp/docs/tuning.rst deleted file mode 100644 index 14dd00d78..000000000 --- a/libtorrent_utp/docs/tuning.rst +++ /dev/null @@ -1,524 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -tuning libtorrent -================= - -libtorrent expose most constants used in the bittorrent engine for -customization through the ``session_settings``. This makes it possible to -test and tweak the parameters for certain algorithms to make a client -that fits a wide range of needs. From low memory embedded devices to -servers seeding thousands of torrents. The default settings in libtorrent -are tuned for an end-user bittorrent client running on a normal desktop -computer. - -This document describes techniques to benchmark libtorrent performance -and how parameters are likely to affect it. - -reducing memory footprint -========================= - -These are things you can do to reduce the memory footprint of libtorrent. You get -some of this by basing your default ``session_settings`` on the ``min_memory_usage()`` -setting preset function. - -Keep in mind that lowering memory usage will affect performance, always profile -and benchmark your settings to determine if it's worth the trade-off. - -The typical buffer usage of libtorrent, for a single download, with the cache -size set to 256 blocks (256 * 16 kiB = 4 MiB) is:: - - read cache: 128.6 (2058 kiB) - write cache: 103.5 (1656 kiB) - receive buffers: 7.3 (117 kiB) - send buffers: 4.8 (77 kiB) - hash temp: 0.001 (19 Bytes) - -The receive buffers is proportional to the number of connections we make, and is -limited by the total number of connections in the session (default is 200). - -The send buffers is proportional to the number of upload slots that are allowed -in the session. The default is auto configured based on the observed upload rate. - -The read and write cache can be controlled (see section below). - -The "hash temp" entry size depends on whether or not hashing is optimized for -speed or memory usage. In this test run it was optimized for memory usage. - -disable disk cache ------------------- - -The bulk of the memory libtorrent will use is used for the disk cache. To save -the absolute most amount of memory, you can disable the cache by setting -``session_settings::cache_size`` to 0. You might want to consider using the cache -but just disable caching read operations. You do this by settings -``session_settings::use_read_cache`` to false. This is the main factor in how much -memory will be used by the client. Keep in mind that you will degrade performance -by disabling the cache. You should benchmark the disk access in order to make an -informed trade-off. - -remove torrents ---------------- - -Torrents that have been added to libtorrent will inevitably use up memory, even -when it's paused. A paused torrent will not use any peer connection objects or -any send or receive buffers though. Any added torrent holds the entire .torrent -file in memory, it also remembers the entire list of peers that it's heard about -(which can be fairly long unless it's capped). It also retains information about -which blocks and pieces we have on disk, which can be significant for torrents -with many pieces. - -If you need to minimize the memory footprint, consider removing torrents from -the session rather than pausing them. This will likely only make a difference -when you have a very large number of torrents in a session. - -The downside of removing them is that they will no longer be auto-managed. Paused -auto managed torrents are scraped periodically, to determine which torrents are -in the greatest need of seeding, and libtorrent will prioritize to seed those. - -socket buffer sizes -------------------- - -You can make libtorrent explicitly set the kernel buffer sizes of all its peer -sockets. If you set this to a low number, you may see reduced throughput, especially -for high latency connections. It is however an opportunity to save memory per -connection, and might be worth considering if you have a very large number of -peer connections. This memory will not be visible in your process, this sets -the amount of kernel memory is used for your sockets. - -Change this by setting ``session_settings::recv_socket_buffer_size`` and -``session_settings::send_socket_buffer_size``. - -peer list size --------------- - -The default maximum for the peer list is 4000 peers. For IPv4 peers, each peer -entry uses 32 bytes, which ends up using 128 kB per torrent. If seeding 4 popular -torrents, the peer lists alone uses about half a megabyte. - -The default limit is the same for paused torrents as well, so if you have a -large number of paused torrents (that are popular) it will be even more -significant. - -If you're short of memory, you should consider lowering the limit. 500 is probably -enough. You can do this by setting ``session_settings::max_peerlist_size`` to -the max number of peers you want in the torrent's peer list. - -You should also lower the same limit but for paused torrents. It might even make sense -to set that even lower, since you only need a few peers to start up while waiting -for the tracker and DHT to give you fresh ones. The max peer list size for paused -torrents is set by ``session_settings::max_paused_peerlist_size``. - -The drawback of lowering this number is that if you end up in a position where -the tracker is down for an extended period of time, your only hope of finding live -peers is to go through your list of all peers you've ever seen. Having a large -peer list will also help increase performance when starting up, since the torrent -can start connecting to peers in parallel with connecting to the tracker. - -send buffer watermark ---------------------- - -The send buffer watermark controls when libtorrent will ask the disk I/O thread -to read blocks from disk, and append it to a peer's send buffer. - -When the send buffer has fewer than or equal number of bytes as -``session_settings::send_buffer_watermark``, the peer will ask the disk I/O thread -for more data to send. The trade-off here is between wasting memory by having too -much data in the send buffer, and hurting send rate by starving out the socket, -waiting for the disk read operation to complete. - -If your main objective is memory usage and you're not concerned about being able -to achieve high send rates, you can set the watermark to 9 bytes. This will guarantee -that no more than a single (16 kiB) block will be on the send buffer at a time, for -all peers. This is the least amount of memory possible for the send buffer. - -You should benchmark your max send rate when adjusting this setting. If you have -a very fast disk, you are less likely see a performance hit. - -optimize hashing for memory usage ---------------------------------- - -When libtorrent is doing hash checks of a file, or when it re-reads a piece that -was just completed to verify its hash, there are two options. The default one -is optimized for speed, which allocates buffers for the entire piece, reads in -the whole piece in one read call, then hashes it. - -The second option is to optimize for memory usage instead, where a single buffer -is allocated, and the piece is read one block at a time, hashing it as each -block is read from the file. For low memory environments, this latter approach -is recommended. Change this by settings ``session_settings::optimize_hashing_for_speed`` -to false. This will significantly reduce peak memory usage, especially for -torrents with very large pieces. - -reduce executable size ----------------------- - -Compilers generally add a significant number of bytes to executables that make use -of C++ exceptions. By disabling exceptions (-fno-exceptions on GCC), you can -reduce the executable size with up to 45%. In order to build without exception -support, you need to patch parts of boost. - -Also make sure to optimize for size when compiling. - -Another way of reducing the executable size is to disable code that isn't used. -There are a number of ``TORRENT_*`` macros that control which features are included -in libtorrent. If these macros are used to strip down libtorrent, make sure the same -macros are defined when building libtorrent as when linking against it. If these -are different the structures will look different from the libtorrent side and from -the client side and memory corruption will follow. - -One, probably, safe macro to define is ``TORRENT_NO_DEPRECATE`` which removes all -deprecated functions and struct members. As long as no deprecated functions are -relied upon, this should be a simple way to eliminate a little bit of code. - -For all available options, see the `building libtorrent`_ secion. - -.. _`building libtorrent`: building.html - -reduce statistics ------------------ - -You can save some memory for each connection and each torrent by reducing the -number of separate rates kept track of by libtorrent. If you build with ``full-stats=off`` -(or ``-DTORRENT_DISABLE_FULL_STATS``) you will save a few hundred bytes for each -connection and torrent. It might make a difference if you have a very large number -of peers or torrents. - -play nice with the disk -======================= - -When checking a torrent, libtorrent will try to read as fast as possible from the disk. -The only thing that might hold it back is a CPU that is slow at calculating SHA-1 hashes, -but typically the file checking is limited by disk read speed. Most operating systems -today do not prioritize disk access based on the importance of the operation, this means -that checking a torrent might delay other disk accesses, such as virtual memory swapping -or just loading file by other (interactive) applications. - -In order to play nicer with the disk, and leave some spare time for it to service other -processes that might be of higher importance to the end-user, you can introduce a sleep -between the disc accesses. This is a direct tradeoff between how fast you can check a -torrent and how soft you will hit the disk. - -You control this by setting the ``session_settings::file_checks_delay_per_block`` to greater -than zero. This number is the number of milliseconds to sleep between each read of 16 kiB. - -The sleeps are not necessarily in between each 16 kiB block (it might be read in larger chunks), -but the number will be multiplied by the number of blocks that were read, to maintain the -same semantics. - -high performance seeding -======================== - -In the case of a high volume seed, there are two main concerns. Performance and scalability. -This translates into high send rates, and low memory and CPU usage per peer connection. - -file pool ---------- - -libtorrent keeps an LRU file cache. Each file that is opened, is stuck in the cache. The main -purpose of this is because of anti-virus software that hooks on file-open and file close to -scan the file. Anti-virus software that does that will significantly increase the cost of -opening and closing files. However, for a high performance seed, the file open/close might -be so frequent that it becomes a significant cost. It might therefore be a good idea to allow -a large file descriptor cache. Adjust this though ``session_settings::file_pool_size``. - -Don't forget to set a high rlimit for file descriptors in your process as well. This limit -must be high enough to keep all connections and files open. - -disk cache ----------- - -You typically want to set the cache size to as high as possible. The -``session_settings::cache_size`` is specified in 16 kiB blocks. Since you're seeding, -the cache would be useless unless you also set ``session_settings::use_read_cache`` -to true. - -In order to increase the possibility of read cache hits, set the -``session_settings::cache_expiry`` to a large number. This won't degrade anything as -long as the client is only seeding, and not downloading any torrents. - -In order to increase the disk cache hit rate, you can enable suggest messages based on -what's in the read cache. To do this, set ``session_settings::suggest_mode`` to -``session_settings::suggest_read_cache``. This will send suggest messages to peers -for the most recently used pieces in the read cache. This is especially useful if you -also enable explicit read cache, by settings ``session_settings::explicit_read_cache`` -to the number of pieces to keep in the cache. The explicit read cache will make the -disk read cache stick, and not be evicted by cache misses. The explicit read cache -will automatically pull in the rarest pieces in the read cache. - -Assuming that you seed much more data than you can keep in the cache, to a large -numbers of peers (so that the read cache wouldn't be useful anyway), this may be a -good idea. - -When peers first connect, libtorrent will send them a number of allow-fast messages, -which lets the peers download certain pieces even when they are choked, since peers -are choked by default, this often triggers immediate requests for those pieces. In the -case of using explicit read cache and suggesting those pieces, allowing fast pieces -should be disabled, to not systematically trigger requests for pieces that are not cached -for all peers. You can turn off allow-fast by settings ``session_settings::allowed_fast_set_size`` -to 0. - -As an alternative to the explicit cache and suggest messages, there's a *guided cache* -mode. This means the size of the read cache line that's stored in the cache is determined -based on the upload rate to the peer that triggered the read operation. The idea being -that slow peers don't use up a disproportional amount of space in the cache. This -is enabled through ``session_settings::guided_read_cache``. - -In cases where the assumption is that the cache is only used as a read-ahead, and that no -other peer will ever request the same block while it's still in the cache, the read -cache can be set to be *volatile*. This means that every block that is requested out of -the read cache is removed immediately. This saves a significant amount of cache space -which can be used as read-ahead for other peers. This mode should **never** be combined -with either ``explicit_read_cache`` or ``suggest_read_cache``, since those uses opposite -strategies for the read cache. You don't want to on one hand attract peers to request -the same pieces, and on the other hand assume that they won't request the same pieces -and drop them when the first peer requests it. To enable volatile read cache, set -``session_settings::volatile_read_cache`` to true. - -send buffer low watermark -------------------------- - -libtorrent uses a low watermark for send buffers to determine when a new piece should -be requested from the disk I/O subsystem, to be appended to the send buffer. The low -watermark is determined based on the send rate of the socket. It needs to be large -enough to not draining the socket's send buffer before the disk operation completes. - -The watermark is bound to a max value, to avoid buffer sizes growing out of control. -The default max send buffer size might not be enough to sustain very high upload rates, -and you might have to increase it. It's specified in bytes in -``session_settings::send_buffer_watermark``. The ``high_performance_seed()`` preset -sets this value to 5 MB. - -peers ------ - -First of all, in order to allow many connections, set the global connection limit -high, ``session::set_max_connections()``. Also set the upload rate limit to -infinite, ``session::set_upload_rate_limit()``, passing 0 means infinite. - -When dealing with a large number of peers, it might be a good idea to have slightly -stricter timeouts, to get rid of lingering connections as soon as possible. - -There are a couple of relevant settings: ``session_settings::request_timeout``, -``session_settings::peer_timeout`` and ``session_settings::inactivity_timeout``. - -For seeds that are critical for a delivery system, you most likely want to allow -multiple connections from the same IP. That way two people from behind the same NAT -can use the service simultaneously. This is controlled by -``session_settings::allow_multiple_connections_per_ip``. - -In order to always unchoke peers, turn off automatic unchoke -``session_settings::auto_upload_slots`` and set the number of upload slots to a large -number via ``session::set_max_uploads()``, or use -1 (which means infinite). - -torrent limits --------------- - -To seed thousands of torrents, you need to increase the ``session_settings::active_limit`` -and ``session_settings::active_seeds``. - -benchmarking -============ - -There are a bunch of built-in instrumentation of libtorrent that can be used to get an insight -into what it's doing and how well it performs. This instrumentation is enabled by defining -preprocessor symbols when building. - -There are also a number of scripts that parses the log files and generates graphs (requires -gnuplot and python). - -disk metrics ------------- - -To enable disk I/O instrumentation, define ``TORRENT_DISK_STATS`` when building. When built -with this configuration libtorrent will create three log files, measuring various aspects of -the disk I/O. The following table is an overview of these files and what they measure. - -+--------------------------+--------------------------------------------------------------+ -| filename | description | -+==========================+==============================================================+ -| ``disk_io_thread.log`` | This is a log of which operation the disk I/O thread is | -| | engaged in, with timestamps. This tells you what the thread | -| | is spending its time doing. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``disk_buffers.log`` | This log keeps track of what the buffers allocated from the | -| | disk buffer pool are used for. There are 5 categories. | -| | receive buffer, send buffer, write cache, read cache and | -| | temporary hash storage. This is key when optimizing memory | -| | usage. | -| | | -+--------------------------+--------------------------------------------------------------+ -| ``disk_access.log`` | This is a low level log of read and write operations, with | -| | timestamps and file offsets. The file offsets are byte | -| | offsets in the torrent (not in any particular file, in the | -| | case of a multi-file torrent). This can be used as an | -| | estimate of the physical drive location. The purpose of | -| | this log is to identify the amount of seeking the drive has | -| | to do. | -| | | -+--------------------------+--------------------------------------------------------------+ - - -disk_io_thread.log -'''''''''''''''''' - -The structure of this log is simple. For each line, there are two columns, a timestamp and -the operation that was started. There is a special operation called ``idle`` which means -it looped back to the top and started waiting for new jobs. If there are more jobs to -handle immediately, the ``idle`` state is still there, but the timestamp is the same as the -next job that is handled. - -Some operations have a 3:rd column with an optional parameter. ``read`` and ``write`` tells -you the number of bytes that were requested to be read or written. ``flushing`` tells you -the number of bytes that were flushed from the disk cache. - -This is an example excerpt from a log:: - - 3702 idle - 3706 check_fastresume - 3707 idle - 4708 save_resume_data - 4708 idle - 8230 read 16384 - 8255 idle - 8431 read 16384 - -The script to parse this log and generate a graph is called ``parse_disk_log.py``. It takes -the log file as the first command line argument, and produces a file: ``disk_io.png``. -The time stamp is in milliseconds since start. - -You can pass in a second, optional, argument to specify the window size it will average -the time measurements over. The default is 5 seconds. For long test runs, it might be interesting -to increase that number. It is specified as a number of seconds. - -.. image:: disk_io.png - -This is an example graph generated by the parse script. - -disk_buffers.log -'''''''''''''''' - -The disk buffer log tells you where the buffer memory is used. The log format has a time stamp, -the name of the buffer usage which use-count changed, colon, and the new number of blocks that are -in use for this particular key. For example:: - - 23671 write cache: 18 - 23671 receive buffer: 3 - 24153 receive buffer: 2 - 24153 write cache: 19 - 24154 receive buffer: 3 - 24198 receive buffer: 2 - 24198 write cache: 20 - 24202 receive buffer: 3 - 24305 send buffer: 0 - 24305 send buffer: 1 - 24909 receive buffer: 2 - 24909 write cache: 21 - 24910 receive buffer: 3 - -The time stamp is in milliseconds since start. - -To generate a graph, use ``parse_disk_buffer_log.py``. It takes the log file as the first -command line argument. It generates ``disk_buffer.png``. - -.. image:: disk_buffer_sample.png - -This is an example graph generated by the parse script. - -disk_access.log -''''''''''''''' - -The disc access log has three fields. The timestamp (milliseconds since start), operation -and offset. The offset is the absolute offset within the torrent (not within a file). This -log is only useful when you're downloading a single torrent, otherwise the offsets will not -be unique. - -In order to easily plot this directly in gnuplot, without parsing it, there are two lines -associated with each read or write operation. The first one is the offset where the operation -started, and the second one is where the operation ended. - -Example:: - - 15437 read 301187072 - 15437 read_end 301203456 - 16651 read 213385216 - 16680 read_end 213647360 - 25879 write 249036800 - 25879 write_end 249298944 - 26811 read 325582848 - 26943 read_end 325844992 - 36736 read 367001600 - 36766 read_end 367263744 - -The disk access log does not have any good visualization tool yet. There is however a gnuplot -file, ``disk_access.gnuplot`` which assumes ``disk_access.log`` is in the current directory. - -.. image:: disk_access.png - -The density of the disk seeks tells you how hard the drive has to work. - -session stats -------------- - -By defining ``TORRENT_STATS`` libtorrent will write a log file called ``session_stats.log`` which -is in a format ready to be passed directly into gnuplot. The parser script ``parse_session_stats.py`` -will however parse out the field names and generate 3 different views of the data. This script -is easy to modify to generate the particular view you're interested in. - -The first line in the log contains all the field names, separated by colon:: - - second:upload rate:download rate:downloading torrents:seeding torrents:peers... - -The rest of the log is one line per second with all the fields' values. - -These are the fields: - -===================== =============================================================== -field name description -===================== =============================================================== -second the time, in seconds, for this log line -upload rate the number of bytes uploaded in the last second -download rate the number of bytes downloaded in the last second -downloading torrents the number of torrents that are not seeds -seeding torrents the number of torrents that are seed -peers the total number of connected peers -connecting peers the total number of peers attempting to connect (half-open) -disk block buffers the total number of disk buffer blocks that are in use -unchoked peers the total number of unchoked peers -num list peers the total number of known peers, but not necessarily connected -peer allocations the total number of allocations for the peer list pool -peer storage bytes the total number of bytes allocated for the peer list pool -===================== =============================================================== - -This is an example of a graph that can be generated from this log: - -.. image:: session_stats_peers.png - -It shows statistics about the number of peers and peers states. How at the startup -there are a lot of half-open connections, which tapers off as the total number of -peers approaches the limit (50). It also shows how the total peer list slowly but steadily -grows over time. This list is plotted against the right axis, as it has a different scale -as the other fields. - -contributions -============= - -If you have added instrumentation for some part of libtorrent that is not covered here, or -if you have improved any of the parser scrips, please consider contributing it back to the -project. - -If you have run tests and found that some algorithm or default value in libtorrent is -suboptimal, please contribute that knowledge back as well, to allow us to improve the library. - -If you have additional suggestions on how to tune libtorrent for any specific use case, -please let us know and we'll update this document. - diff --git a/libtorrent_utp/docs/tvblob.jpg b/libtorrent_utp/docs/tvblob.jpg deleted file mode 100644 index 14a77c81acbe21d995570749db827df0113343aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8686 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 nw4QO!O?M*jLwt>(1qb% diff --git a/libtorrent_utp/docs/udp_tracker_protocol.html b/libtorrent_utp/docs/udp_tracker_protocol.html deleted file mode 100644 index 8778db217..000000000 --- a/libtorrent_utp/docs/udp_tracker_protocol.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - - - -Bittorrent udp-tracker protocol extension - - - - - - - -
-
-
- -
- -
-

Bittorrent udp-tracker protocol extension

- --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
- -
-

introduction

-

A tracker with the protocol "udp://" in its URI -is supposed to be contacted using this protocol.

-

This protocol is supported by -xbt-tracker.

-

For additional information and descritptions of -the terminology used in this document, see -the protocol specification

-

All values are sent in network byte order (big endian). The sizes -are specified with ANSI-C standard types.

-

If no response to a request is received within 15 seconds, resend -the request. If no reply has been received after 60 seconds, stop -retrying.

-
-
-

connecting

-

Client sends packet:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int64_tconnection_idMust be initialized to 0x41727101980 -in network byte order. This will -identify the protocol.
int32_taction0 for a connection request
int32_ttransaction_idRandomized by client.
-

Server replies with packet:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tactionDescribes the type of packet, in this -case it should be 0, for connect. -If 3 (for error) see errors.
int32_ttransaction_idMust match the transaction_id sent -from the client.
int64_tconnection_idA connection id, this is used when -further information is exchanged with -the tracker, to identify you. -This connection id can be reused for -multiple requests, but if it's cached -for too long, it will not be valid -anymore.
-
-
-

announcing

-

Client sends packet:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int64_tconnection_idThe connection id acquired from -establishing the connection.
int32_tactionAction. in this case, 1 for announce. -See actions.
int32_ttransaction_idRandomized by client.
int8_t[20]info_hashThe info-hash of the torrent you want -announce yourself in.
int8_t[20]peer_idYour peer id.
int64_tdownloadedThe number of byte you've downloaded -in this session.
int64_tleftThe number of bytes you have left to -download until you're finished.
int64_tuploadedThe number of bytes you have uploaded -in this session.
int32_tevent

The event, one of

-
-
    -
  • none = 0
  • -
  • completed = 1
  • -
  • started = 2
  • -
  • stopped = 3
  • -
-
-
uint32_tipYour ip address. Set to 0 if you want -the tracker to use the sender of -this udp packet.
uint32_tkeyA unique key that is randomized by the -client.
int32_tnum_wantThe maximum number of peers you want -in the reply. Use -1 for default.
uint16_tportThe port you're listening on.
uint16_textensionsSee extensions
-

Server replies with packet:

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tactionThe action this is a reply to. Should -in this case be 1 for announce. -If 3 (for error) see errors. -See actions.
int32_ttransaction_idMust match the transaction_id sent -in the announce request.
int32_tintervalthe number of seconds you should wait -until reannouncing yourself.
int32_tleechersThe number of peers in the swarm that -has not finished downloading.
int32_tseedersThe number of peers in the swarm that -has finished downloading and are -seeding.
-

The rest of the server reply is a variable number of the following structure:

- ----- - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tipThe ip of a peer in the swarm.
uint16_tportThe peer's listen port.
-
-
-

scraping

-

Client sends packet:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int64_tconnection_idThe connection id retreived from the -establishing of the connection.
int32_tactionThe action, in this case, 2 for -scrape. See actions.
int32_ttransaction_idRandomized by client.
-

The following structure is repeated for each info-hash to scrape, but limited by -the MTU.

- ----- - - - - - - - - - - - - -
sizenamedescription
int8_t[20]info_hashThe info hash that is to be scraped.
-

Server replies with packet:

- ----- - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tactionThe action, should in this case be -2 for scrape. -If 3 (for error) see errors.
int32_ttransaction_idMust match the sent transaction id.
-

The rest of the packet contains the following structures once for each info-hash -you asked in the scrape request.

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tcompleteThe current number of connected seeds.
int32_tdownloadedThe number of times this torrent has -been downloaded.
int32_tincompleteThe current number of connected -leechers.
-
-
-

errors

-

In case of a tracker error,

-

server replies packet:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int32_tactionThe action, in this case 3, for error. -See actions.
int32_ttransaction_idMust match the transaction_id sent -from the client.
int8_t[]error_stringThe rest of the packet is a string -describing the error.
-
-
-

actions

-

The action fields has the following encoding:

-
-
    -
  • connect = 0
  • -
  • announce = 1
  • -
  • scrape = 2
  • -
  • error = 3 (only in server replies)
  • -
-
-
-
-

extensions

-

The extensions field is a bitmask. The following -bits are assigned:

-
- -
-
-

authentication

-

The packet will have an authentication part -appended to it. It has the following format:

- ----- - - - - - - - - - - - - - - - - - - - - -
sizenamedescription
int8_tusername_lengthThe number of characters in the -username.
int8_t[]usernameThe username, the number of characters -as specified in the previous field.
uint8_t[8]passwd_hashsha1(packet + sha1(password)) -The packet in this case means the -entire packet except these 8 bytes -that are the password hash. These are -the 8 first bytes (most significant) -from the 20 bytes hash calculated.
-
-
-
-

credits

-

Protocol designed by Olaf van der Spek

-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/udp_tracker_protocol.rst b/libtorrent_utp/docs/udp_tracker_protocol.rst deleted file mode 100644 index d9fcbee82..000000000 --- a/libtorrent_utp/docs/udp_tracker_protocol.rst +++ /dev/null @@ -1,289 +0,0 @@ -Bittorrent udp-tracker protocol extension -========================================= - -:Author: Arvid Norberg, arvid@rasterbar.com - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - - -introduction ------------- - -A tracker with the protocol "udp://" in its URI -is supposed to be contacted using this protocol. - -This protocol is supported by -xbt-tracker_. - - -.. _xbt-tracker: http://xbtt.sourceforge.net - -For additional information and descritptions of -the terminology used in this document, see -the `protocol specification`__ - -__ http://wiki.theory.org/index.php/BitTorrentSpecification - -All values are sent in network byte order (big endian). The sizes -are specified with ANSI-C standard types. - -If no response to a request is received within 15 seconds, resend -the request. If no reply has been received after 60 seconds, stop -retrying. - - -connecting ----------- - -Client sends packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int64_t | connection_id | Must be initialized to 0x41727101980 | -| | | in network byte order. This will | -| | | identify the protocol. | -+-------------+---------------------+----------------------------------------+ -| int32_t | action | 0 for a connection request | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Randomized by client. | -+-------------+---------------------+----------------------------------------+ - -Server replies with packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | action | Describes the type of packet, in this | -| | | case it should be 0, for connect. | -| | | If 3 (for error) see errors_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Must match the transaction_id sent | -| | | from the client. | -+-------------+---------------------+----------------------------------------+ -| int64_t | connection_id | A connection id, this is used when | -| | | further information is exchanged with | -| | | the tracker, to identify you. | -| | | This connection id can be reused for | -| | | multiple requests, but if it's cached | -| | | for too long, it will not be valid | -| | | anymore. | -+-------------+---------------------+----------------------------------------+ - - -announcing ----------- - -Client sends packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int64_t | connection_id | The connection id acquired from | -| | | establishing the connection. | -+-------------+---------------------+----------------------------------------+ -| int32_t | action | Action. in this case, 1 for announce. | -| | | See actions_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Randomized by client. | -+-------------+---------------------+----------------------------------------+ -| int8_t[20] | info_hash | The info-hash of the torrent you want | -| | | announce yourself in. | -+-------------+---------------------+----------------------------------------+ -| int8_t[20] | peer_id | Your peer id. | -+-------------+---------------------+----------------------------------------+ -| int64_t | downloaded | The number of byte you've downloaded | -| | | in this session. | -+-------------+---------------------+----------------------------------------+ -| int64_t | left | The number of bytes you have left to | -| | | download until you're finished. | -+-------------+---------------------+----------------------------------------+ -| int64_t | uploaded | The number of bytes you have uploaded | -| | | in this session. | -+-------------+---------------------+----------------------------------------+ -| int32_t | event | The event, one of | -| | | | -| | | * none = 0 | -| | | * completed = 1 | -| | | * started = 2 | -| | | * stopped = 3 | -+-------------+---------------------+----------------------------------------+ -| uint32_t | ip | Your ip address. Set to 0 if you want | -| | | the tracker to use the ``sender`` of | -| | | this udp packet. | -+-------------+---------------------+----------------------------------------+ -| uint32_t | key | A unique key that is randomized by the | -| | | client. | -+-------------+---------------------+----------------------------------------+ -| int32_t | num_want | The maximum number of peers you want | -| | | in the reply. Use -1 for default. | -+-------------+---------------------+----------------------------------------+ -| uint16_t | port | The port you're listening on. | -+-------------+---------------------+----------------------------------------+ -| uint16_t | extensions | See extensions_ | -+-------------+---------------------+----------------------------------------+ - - -Server replies with packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | action | The action this is a reply to. Should | -| | | in this case be 1 for announce. | -| | | If 3 (for error) see errors_. | -| | | See actions_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Must match the transaction_id sent | -| | | in the announce request. | -+-------------+---------------------+----------------------------------------+ -| int32_t | interval | the number of seconds you should wait | -| | | until reannouncing yourself. | -+-------------+---------------------+----------------------------------------+ -| int32_t | leechers | The number of peers in the swarm that | -| | | has not finished downloading. | -+-------------+---------------------+----------------------------------------+ -| int32_t | seeders | The number of peers in the swarm that | -| | | has finished downloading and are | -| | | seeding. | -+-------------+---------------------+----------------------------------------+ - -The rest of the server reply is a variable number of the following structure: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | ip | The ip of a peer in the swarm. | -+-------------+---------------------+----------------------------------------+ -| uint16_t | port | The peer's listen port. | -+-------------+---------------------+----------------------------------------+ - - -scraping --------- - -Client sends packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int64_t | connection_id | The connection id retreived from the | -| | | establishing of the connection. | -+-------------+---------------------+----------------------------------------+ -| int32_t | action | The action, in this case, 2 for | -| | | scrape. See actions_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Randomized by client. | -+-------------+---------------------+----------------------------------------+ - -The following structure is repeated for each info-hash to scrape, but limited by -the MTU. - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int8_t[20] | info_hash | The info hash that is to be scraped. | -+-------------+---------------------+----------------------------------------+ - - -Server replies with packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | action | The action, should in this case be | -| | | 2 for scrape. | -| | | If 3 (for error) see errors_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Must match the sent transaction id. | -+-------------+---------------------+----------------------------------------+ - -The rest of the packet contains the following structures once for each info-hash -you asked in the scrape request. - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | complete | The current number of connected seeds. | -+-------------+---------------------+----------------------------------------+ -| int32_t | downloaded | The number of times this torrent has | -| | | been downloaded. | -+-------------+---------------------+----------------------------------------+ -| int32_t | incomplete | The current number of connected | -| | | leechers. | -+-------------+---------------------+----------------------------------------+ - - -errors ------- - -In case of a tracker error, - -server replies packet: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int32_t | action | The action, in this case 3, for error. | -| | | See actions_. | -+-------------+---------------------+----------------------------------------+ -| int32_t | transaction_id | Must match the transaction_id sent | -| | | from the client. | -+-------------+---------------------+----------------------------------------+ -| int8_t[] | error_string | The rest of the packet is a string | -| | | describing the error. | -+-------------+---------------------+----------------------------------------+ - - -actions -------- - -The action fields has the following encoding: - - * connect = 0 - * announce = 1 - * scrape = 2 - * error = 3 (only in server replies) - - -extensions ----------- - -The extensions field is a bitmask. The following -bits are assigned: - - * 1 = authentication_. - - - -authentication -~~~~~~~~~~~~~~ - -The packet will have an authentication part -appended to it. It has the following format: - -+-------------+---------------------+----------------------------------------+ -| size | name | description | -+=============+=====================+========================================+ -| int8_t | username_length | The number of characters in the | -| | | username. | -+-------------+---------------------+----------------------------------------+ -| int8_t[] | username | The username, the number of characters | -| | | as specified in the previous field. | -+-------------+---------------------+----------------------------------------+ -| uint8_t[8] | passwd_hash | sha1(packet + sha1(password)) | -| | | The packet in this case means the | -| | | entire packet except these 8 bytes | -| | | that are the password hash. These are | -| | | the 8 first bytes (most significant) | -| | | from the 20 bytes hash calculated. | -+-------------+---------------------+----------------------------------------+ - - -credits -------- - -Protocol designed by Olaf van der Spek - diff --git a/libtorrent_utp/docs/unicode_support.png b/libtorrent_utp/docs/unicode_support.png deleted file mode 100644 index 33c3f3c70117a78514eedf9bc1a356ee8bddf975..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49602 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

!3I@p5Sv6GjfV7>{mpBNLPKFAE9e-QX1|>DM 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 - - - - - -libtorrent manual - - - - - - - -

-
-
- -
- -
-

libtorrent manual

- --- - - - - - -
Author:Arvid Norberg, arvid@rasterbar.com
Version:0.16.0
- -
-

uTP

-

uTP (uTorrent transport protocol) is a transport protocol which uses one-way -delay measurements for its congestion controller. This article is about uTP -in general and specifically about libtorrent's implementation of it.

-
-

rationale

-

One of the most common problems users are experiencing using bittorrent is -that their internet "stops working". This can be caused by a number of things, -for example:

-
    -
  1. a home router that crashes or slows down when its NAT pin-hole -table overflows, triggered by DHT or simply many TCP connections.
  2. -
  3. a home router that crashes or slows down by UDP traffic (caused by -the DHT)
  4. -
  5. a home DSL or cable modem having its send buffer filled up by outgoing -data, and the buffer fits seconds worth of bytes. This adds seconds -of delay on interactive traffic. For a web site that needs 10 round -trips to load this may mean 10s of seconds of delay to load compared -to without bittorrent. Skype or other delay sensitive applications -would be affected even more.
  6. -
-

This document will cover (3).

-

Typically this is solved by asking the user to enter a number of bytes -that the client is allowed to send per second (i.e. setting an upload -rate limit). The common recommendation is to set this limit to 80% of the -uplink's capacity. This is to leave some headroom for things like TCP -ACKs as well as the user's interactive use of the connection such as -browsing the web or checking email.

-

There are two major drawbacks with this technique:

-
    -
  1. The user needs to actively make this setting (very few protocols -require the user to provide this sort of information). This also -means the user needs to figure out what its up-link capacity is. -This is unfortunately a number that many ISPs are not advertizing -(because it's often much lower than the download capacity) which -might make it hard to find.
  2. -
  3. The 20% headroom is wasted most of the time. Whenever the user -is not using the internet connection for anything, those extra 20% -could have been used by bittorrent to upload, but they're already -allocated for interactive traffic. On top of that, 20% of the up-link -is often not enough to give a good and responsive browsing experience.
  4. -
-

The ideal bandwidth allocation would be to use 100% for bittorrent when -there is no interactive cross traffic, and 100% for interactive traffic -whenever there is any. This would not waste any bandwidth while the user -is idling, and it would make for a much better experience when the user -is using the internet connection for other things.

-

This is what uTP does.

-
-
-

TCP

-

The reason TCP will fill the send buffer, and cause the delay on all traffic, -is because its congestion control is only based on packet loss (and timeout).

-

Since the modem is buffering, packets won't get dropped until the entire queue -is full, and no more packets will fit. The packets will be dropped, TCP will -detect this within an RTT or so. When TCP notices a packet loss, it will slow -down its send rate and the queue will start to drain again. However, TCP will -immediately start to ramp up its send rate again until the buffer is full and -it detects packet loss again.

-

TCP is designed to fully utilize the link capacity, without causing congestion. -Whenever it sense congestion (through packet loss) it backs off. TCP is not -designed to keep delays low. When you get the first packet loss (assuming the -kind of queue described above, tail-queue) it is already too late. Your queue -is full and you have the maximum amount of delay your modem can provide.

-

TCP controls its send rate by limiting the number of bytes in-flight at any -given time. This limit is called congestion window (cwnd for short). During -steady state, the congestion window is constantly increasing linearly. Each -packet that is successfully transferred will increase cwnd.

-
-            cwnd
-send_rate = ----
-            RTT
-
-

Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause -the send rate to be lower and a larger cwnd will cause the send rate to be -higher.

-

Using a congestion window instead of controlling the rate directly is simple -because it also introduces an upper bound for memory usage for packets that -haven't been ACKed yet and needs to be kept around.

-

The behavior of TCP, where it bumps up against the ceiling, backs off and then -starts increasing again until it hits the ceiling again, forms a saw tooth shape. -If the modem wouldn't have any send buffer at all, a single TCP stream would -not be able to fully utilize the link because of this behavior, since it would -only fully utilize the link right before the packet loss and the back-off.

-
-
-

LEDBAT congestion controller

-

The congestion controller in uTP is called LEDBAT, which also is an IETF working -group attempting to standardize it. The congestion controller, on top of reacting -to packet loss the same way TCP does, also reacts to changes in delays.

-

For any uTP (or LEDBAT) implementation, there is a target delay. This is the -amount of delay that is acceptable, and is in fact targeted for the connection. -The target delay is defined to 25 ms in LEDBAT, uTorrent uses 100 ms and -libtorrent uses 75 ms. Whenever a delay measurement is lower than the target, -cwnd is increased proportional to (target_delay - delay). Whenever the measurement -is higher than the target, cwnd is decreased proportional to (delay - target_delay).

-

It can simply be expressed as:

-
-cwnd += gain * (target_delay - delay)
-
-cwnd_thumb.png -

Similarly to TCP, this is scaled so that the increase is evened out over one RTT.

-

The linear controller will adjust the cwnd more for delays that are far off the -target, and less for delays that are close to the target. This makes it converge -at the target delay. Although, due to noise there is almost always some amount of -oscillation. This oscillation is typically smaller than the saw tooth TCP forms.

-

The figure to the right shows how (TCP) cross traffic causese uTP to essentially -entirely stop sending anything. Its delay measurements are mostly well above the target -during this time. The cross traffic is only a single TCP stream in this test.

-

As soon as the cross traffic ceases, uTP will pick up its original send rate within -a second.

-

Since uTP constantly measures the delay, with every single packet, the reaction time -to cross traffic causing delays is a single RTT (typically a fraction of a second).

-
-
-

one way delays

-

uTP measures the delay imposed on packets being sent to the other end -of the connection. This measurement only includes buffering delay along -the link, not propagation delay (the speed of light times distance) nor -the routing delay (the time routers spend figuring out where to forward -the packet). It does this by always comparing all measurements to a -baseline measurement, to cancel out any fixed delay. By focusing on the -variable delay along a link, it will specifically detect points where -there might be congestion, since those points will have buffers.

-delays_thumb.png -

Delay on the return link is explicitly not included in the delay measurement. -This is because in a peer-to-peer application, the other end is likely to also -be connected via a modem, with the same send buffer restrictions as we assume -for the sending side. The other end having its send queue full is not an indication -of congestion on the path going the other way.

-

In order to measure one way delays for packets, we cannot rely on clocks being -synchronized, especially not at the microsecond level. Instead, the actual time -it takes for a packet to arrive at the destination is not measured, only the changes -in the transit time is measured.

-

Each packet that is sent includes a time stamp of the current time, in microseconds, -of the sending machine. The receiving machine calculates the difference between its -own timestamp and the one in the packet and sends this back in the ACK. This difference, -since it is in microseconds, will essentially be a random 32 bit number. However, -the difference will stay somewhat similar over time. Any changes in this difference -indicates that packets are either going through faster or slower.

-

In order to measure the one-way buffering delay, a base delay is established. The -base delay is the lowest ever seen value of the time stamp difference. Each delay -sample we receive back, is compared against the base delay and the delay is the -difference.

-

This is the delay that's fed into the congestion controller.

-

A histogram of typical delay measurements is shown to the right. This is from -a transfer between a cable modem connection and a DSL connection.

-

The details of the delay measurements are slightly more complicated since the -values needs to be able to wrap (cross the 2^32 boundry and start over at 0).

-
-
-

Path MTU discovery

-

MTU is short for Maximum Transfer Unit and describes the largest packet size that -can be sent over a link. Any datagrams which size exceeds this limit will either -be fragmented or dropped. A fragmented datagram means that the payload is split up -in multiple packets, each with its own individual packet header.

-

There are several reasons to avoid sending datagrams that get fragmented:

-
    -
  1. A fragmented datagram is more likely to be lost. If any fragment is lost, -the whole datagram is dropped.
  2. -
  3. Bandwidth is likely to be wasted. If the datagram size is not divisible -by the MTU the last packet will not contain as much payload as it could, and the -payload over protocol header ratio decreases.
  4. -
  5. It's expensive to fragment datagrams. Few routers are optimized to handle large -numbers of fragmented packets. Datagrams that have to fragment are likely to -be delayed significantly, and contribute to more CPU being used on routers. -Typically fragmentation (and other advanced IP features) are implemented in -software (slow) and not hardware (fast).
  6. -
-

The path MTU is the lowest MTU of any link along a path from two endpoints on the -internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can -be anywhere in between.

-

The most common MTU is 1500 bytes, which is the largest packet size for ethernet -networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to -Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This -protocol uses up 8 bytes per packet for its own header.

-

If the user happens to be on an internet connection over a VPN, it will add another -layer, with its own packet headers.

-

In short; if you would pick the largest possible packet size on an ethernet network, -1472, and stick with it, you would be quite likely to generate fragments for a lot -of connections. The fragments that will be created will be very small and especially -inflate the overhead waste.

-

The other approach of picking a very conservative packet size, that would be very -unlikely to get fragmented has the following drawbacks:

-
    -
  1. People on good, normal, networks will be penalized with a small packet size. -Both in terms of router load but also bandwidth waste.
  2. -
  3. Software routers are typically not limited by the number of bytes they can route, -but the number of packets. Small packets means more of them, and more load on -software routers.
  4. -
-

The solution to the problem of finding the optimal packet size, is to dynamically -adjust the packet size and search for the largest size that can make it through -without being fragmented along the path.

-

To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This -asks routers that otherwise would fragment packets to instead drop them, and send -back an ICMP message reporting the MTU of the link the packet couldn't fit. With -this message, it's very simple to discover the path MTU. You simply mark your packets -not to be fragmented, and change your packet size whenever you receive the ICMP -packet-too-big message.

-

Unfortunately it's not quite that simple. There are a significant number of firewalls -in the wild blocking all ICMP messages. This means we can't rely on them, we also have -to guess that a packet was dropped because of its size. This is done by only marking -certain packets with DF, and if all other packets go through, except for the MTU probes, -we know that we need to lower our packet sizes.

-

If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500), -we can do a binary search for the MTU. This would let us find it in just a few round-trips.

-

On top of this, libtorrent has an optimization where it figures out which interface a -uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU. -This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would -immediately know to send smaller packets, no search required. It also has the side-effect -of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links -with jumbo frames.

-
-
-

clock drift

-our_delay_base_thumb.png -

Clock drift is clocks progressing at different rates. It's different from clock -skew which means clocks set to different values (but which may progress at the same -rate).

-

Any clock drift between the two machines involved in a uTP transfer will result -in systematically inflated or deflated delay measurements.

-

This can be solved by letting the base delay be the lowest seen sample in the last -n minutes. This is a trade-off between seeing a single packet go straight through -the queue, with no delay, and the amount of clock drift one can assume on normal computers.

-

It turns out that it's fairly safe to assume that one of your packets will in fact go -straight through without any significant delay, once every 20 minutes or so. However, -the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms -is quite significant, especially if your target delay is 25 ms (as in the LEDBAT spec).

-

Clocks progresses at different rates depending on temperature. This means computers -running hot are likely to have a clock drift compared to computers running cool.

-

So, by updating the delay base periodically based on the lowest seen sample, you'll either -end up changing it upwards (artificaially making the delay samples appear small) without -the congestion or delay actually having changed, or you'll end up with a significant clock -drift and have artificially low samples because of that.

-

The solution to this problem is based on the fact that the clock drift is only a problem -for one of the sides of the connection. Only when your delay measurements keep increasing -is it a problem. If your delay measurements keep decreasing, the samples will simply push -down the delay base along with it. With this in mind, we can simply keep track of the -other end's delay measurements as well, applying the same logic to it. Whenever the -other end's base delay is adjusted downwards, we adjust our base delay upwards by the same -amount.

-

This will accurately keep the base delay updated with the clock drift and improve -the delay measurements. The figure on the right shows the absolute timestamp differences -along with the base delay. The slope of the measurements is caused by clock drift.

-

For more information on the clock drift compensation, see the slides from BitTorrent's -presentation at IPTPS10.

-
-
-

features

-

libtorrent's uTP implementation includes the following features:

-
    -
  • Path MTU discovery, including jumbo frames and detecting restricted -MTU tunnels. Binary search packet sizes to find the largest non-fragmented.
  • -
  • Selective ACK. The ability to acknowledge individual packets in the -event of packet loss
  • -
  • Fast resend. The first time a packet is lost, it's resent immediately. -Triggered by duplicate ACKs.
  • -
  • Nagle's algorithm. Minimize protocol overhead by attempting to lump -full packets of payload together before sending a packet.
  • -
  • Delayed ACKs to minimize protocol overhead.
  • -
  • Microsecond resolution timestamps.
  • -
  • Advertised receive window, to support download rate limiting.
  • -
  • Correct handling of wrapping sequence numbers.
  • -
  • Easy configuration of target-delay, gain-factor, timeouts, delayed-ack -and socket buffers.
  • -
-
-
-
- -
- - -
- - diff --git a/libtorrent_utp/docs/utp.rst b/libtorrent_utp/docs/utp.rst deleted file mode 100644 index b744109b7..000000000 --- a/libtorrent_utp/docs/utp.rst +++ /dev/null @@ -1,347 +0,0 @@ -================= -libtorrent manual -================= - -:Author: Arvid Norberg, arvid@rasterbar.com -:Version: 0.16.0 - -.. contents:: Table of contents - :depth: 2 - :backlinks: none - -uTP -=== - -uTP (uTorrent transport protocol) is a transport protocol which uses one-way -delay measurements for its congestion controller. This article is about uTP -in general and specifically about libtorrent's implementation of it. - -rationale ---------- - -One of the most common problems users are experiencing using bittorrent is -that their internet "stops working". This can be caused by a number of things, -for example: - -1. a home router that crashes or slows down when its NAT pin-hole - table overflows, triggered by DHT or simply many TCP connections. - -2. a home router that crashes or slows down by UDP traffic (caused by - the DHT) - -3. a home DSL or cable modem having its send buffer filled up by outgoing - data, and the buffer fits seconds worth of bytes. This adds seconds - of delay on interactive traffic. For a web site that needs 10 round - trips to load this may mean 10s of seconds of delay to load compared - to without bittorrent. Skype or other delay sensitive applications - would be affected even more. - -This document will cover (3). - -Typically this is solved by asking the user to enter a number of bytes -that the client is allowed to send per second (i.e. setting an upload -rate limit). The common recommendation is to set this limit to 80% of the -uplink's capacity. This is to leave some headroom for things like TCP -ACKs as well as the user's interactive use of the connection such as -browsing the web or checking email. - -There are two major drawbacks with this technique: - -1. The user needs to actively make this setting (very few protocols - require the user to provide this sort of information). This also - means the user needs to figure out what its up-link capacity is. - This is unfortunately a number that many ISPs are not advertizing - (because it's often much lower than the download capacity) which - might make it hard to find. - -2. The 20% headroom is wasted most of the time. Whenever the user - is not using the internet connection for anything, those extra 20% - could have been used by bittorrent to upload, but they're already - allocated for interactive traffic. On top of that, 20% of the up-link - is often not enough to give a good and responsive browsing experience. - -The ideal bandwidth allocation would be to use 100% for bittorrent when -there is no interactive cross traffic, and 100% for interactive traffic -whenever there is any. This would not waste any bandwidth while the user -is idling, and it would make for a much better experience when the user -is using the internet connection for other things. - -This is what uTP does. - -TCP ---- - -The reason TCP will fill the send buffer, and cause the delay on all traffic, -is because its congestion control is *only* based on packet loss (and timeout). - -Since the modem is buffering, packets won't get dropped until the entire queue -is full, and no more packets will fit. The packets will be dropped, TCP will -detect this within an RTT or so. When TCP notices a packet loss, it will slow -down its send rate and the queue will start to drain again. However, TCP will -immediately start to ramp up its send rate again until the buffer is full and -it detects packet loss again. - -TCP is designed to fully utilize the link capacity, without causing congestion. -Whenever it sense congestion (through packet loss) it backs off. TCP is not -designed to keep delays low. When you get the first packet loss (assuming the -kind of queue described above, tail-queue) it is already too late. Your queue -is full and you have the maximum amount of delay your modem can provide. - -TCP controls its send rate by limiting the number of bytes in-flight at any -given time. This limit is called congestion window (*cwnd* for short). During -steady state, the congestion window is constantly increasing linearly. Each -packet that is successfully transferred will increase cwnd. - -:: - - cwnd - send_rate = ---- - RTT - - -Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause -the send rate to be lower and a larger cwnd will cause the send rate to be -higher. - -Using a congestion window instead of controlling the rate directly is simple -because it also introduces an upper bound for memory usage for packets that -haven't been ACKed yet and needs to be kept around. - -The behavior of TCP, where it bumps up against the ceiling, backs off and then -starts increasing again until it hits the ceiling again, forms a saw tooth shape. -If the modem wouldn't have any send buffer at all, a single TCP stream would -not be able to fully utilize the link because of this behavior, since it would -only fully utilize the link right before the packet loss and the back-off. - -LEDBAT congestion controller ----------------------------- - -The congestion controller in uTP is called LEDBAT_, which also is an IETF working -group attempting to standardize it. The congestion controller, on top of reacting -to packet loss the same way TCP does, also reacts to changes in delays. - -For any uTP (or LEDBAT_) implementation, there is a target delay. This is the -amount of delay that is acceptable, and is in fact targeted for the connection. -The target delay is defined to 25 ms in LEDBAT_, uTorrent uses 100 ms and -libtorrent uses 75 ms. Whenever a delay measurement is lower than the target, -cwnd is increased proportional to (target_delay - delay). Whenever the measurement -is higher than the target, cwnd is decreased proportional to (delay - target_delay). - -It can simply be expressed as:: - - cwnd += gain * (target_delay - delay) - -.. image:: cwnd_thumb.png - :target: cwnd.png - :align: right - -Similarly to TCP, this is scaled so that the increase is evened out over one RTT. - -The linear controller will adjust the cwnd more for delays that are far off the -target, and less for delays that are close to the target. This makes it converge -at the target delay. Although, due to noise there is almost always some amount of -oscillation. This oscillation is typically smaller than the saw tooth TCP forms. - -The figure to the right shows how (TCP) cross traffic causese uTP to essentially -entirely stop sending anything. Its delay measurements are mostly well above the target -during this time. The cross traffic is only a single TCP stream in this test. - -As soon as the cross traffic ceases, uTP will pick up its original send rate within -a second. - -Since uTP constantly measures the delay, with every single packet, the reaction time -to cross traffic causing delays is a single RTT (typically a fraction of a second). - -one way delays --------------- - -uTP measures the delay imposed on packets being sent to the other end -of the connection. This measurement only includes buffering delay along -the link, not propagation delay (the speed of light times distance) nor -the routing delay (the time routers spend figuring out where to forward -the packet). It does this by always comparing all measurements to a -baseline measurement, to cancel out any fixed delay. By focusing on the -variable delay along a link, it will specifically detect points where -there might be congestion, since those points will have buffers. - -.. image:: delays_thumb.png - :target: delays.png - :align: right - -Delay on the return link is explicitly not included in the delay measurement. -This is because in a peer-to-peer application, the other end is likely to also -be connected via a modem, with the same send buffer restrictions as we assume -for the sending side. The other end having its send queue full is not an indication -of congestion on the path going the other way. - -In order to measure one way delays for packets, we cannot rely on clocks being -synchronized, especially not at the microsecond level. Instead, the actual time -it takes for a packet to arrive at the destination is not measured, only the changes -in the transit time is measured. - -Each packet that is sent includes a time stamp of the current time, in microseconds, -of the sending machine. The receiving machine calculates the difference between its -own timestamp and the one in the packet and sends this back in the ACK. This difference, -since it is in microseconds, will essentially be a random 32 bit number. However, -the difference will stay somewhat similar over time. Any changes in this difference -indicates that packets are either going through faster or slower. - -In order to measure the one-way buffering delay, a base delay is established. The -base delay is the lowest ever seen value of the time stamp difference. Each delay -sample we receive back, is compared against the base delay and the delay is the -difference. - -This is the delay that's fed into the congestion controller. - -A histogram of typical delay measurements is shown to the right. This is from -a transfer between a cable modem connection and a DSL connection. - -The details of the delay measurements are slightly more complicated since the -values needs to be able to wrap (cross the 2^32 boundry and start over at 0). - -Path MTU discovery ------------------- - -MTU is short for *Maximum Transfer Unit* and describes the largest packet size that -can be sent over a link. Any datagrams which size exceeds this limit will either -be *fragmented* or dropped. A fragmented datagram means that the payload is split up -in multiple packets, each with its own individual packet header. - -There are several reasons to avoid sending datagrams that get fragmented: - -1. A fragmented datagram is more likely to be lost. If any fragment is lost, - the whole datagram is dropped. - -2. Bandwidth is likely to be wasted. If the datagram size is not divisible - by the MTU the last packet will not contain as much payload as it could, and the - payload over protocol header ratio decreases. - -3. It's expensive to fragment datagrams. Few routers are optimized to handle large - numbers of fragmented packets. Datagrams that have to fragment are likely to - be delayed significantly, and contribute to more CPU being used on routers. - Typically fragmentation (and other advanced IP features) are implemented in - software (slow) and not hardware (fast). - -The path MTU is the lowest MTU of any link along a path from two endpoints on the -internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can -be anywhere in between. - -The most common MTU is 1500 bytes, which is the largest packet size for ethernet -networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to -Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This -protocol uses up 8 bytes per packet for its own header. - -If the user happens to be on an internet connection over a VPN, it will add another -layer, with its own packet headers. - -In short; if you would pick the largest possible packet size on an ethernet network, -1472, and stick with it, you would be quite likely to generate fragments for a lot -of connections. The fragments that will be created will be very small and especially -inflate the overhead waste. - -The other approach of picking a very conservative packet size, that would be very -unlikely to get fragmented has the following drawbacks: - -1. People on good, normal, networks will be penalized with a small packet size. - Both in terms of router load but also bandwidth waste. - -2. Software routers are typically not limited by the number of bytes they can route, - but the number of packets. Small packets means more of them, and more load on - software routers. - -The solution to the problem of finding the optimal packet size, is to dynamically -adjust the packet size and search for the largest size that can make it through -without being fragmented along the path. - -To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This -asks routers that otherwise would fragment packets to instead drop them, and send -back an ICMP message reporting the MTU of the link the packet couldn't fit. With -this message, it's very simple to discover the path MTU. You simply mark your packets -not to be fragmented, and change your packet size whenever you receive the ICMP -packet-too-big message. - -Unfortunately it's not quite that simple. There are a significant number of firewalls -in the wild blocking all ICMP messages. This means we can't rely on them, we also have -to guess that a packet was dropped because of its size. This is done by only marking -certain packets with DF, and if all other packets go through, except for the MTU probes, -we know that we need to lower our packet sizes. - -If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500), -we can do a binary search for the MTU. This would let us find it in just a few round-trips. - -On top of this, libtorrent has an optimization where it figures out which interface a -uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU. -This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would -immediately know to send smaller packets, no search required. It also has the side-effect -of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links -with jumbo frames. - -clock drift ------------ - -.. image:: our_delay_base_thumb.png - :target: our_delay_base.png - :align: right - -Clock drift is clocks progressing at different rates. It's different from clock -skew which means clocks set to different values (but which may progress at the same -rate). - -Any clock drift between the two machines involved in a uTP transfer will result -in systematically inflated or deflated delay measurements. - -This can be solved by letting the base delay be the lowest seen sample in the last -*n* minutes. This is a trade-off between seeing a single packet go straight through -the queue, with no delay, and the amount of clock drift one can assume on normal computers. - -It turns out that it's fairly safe to assume that one of your packets will in fact go -straight through without any significant delay, once every 20 minutes or so. However, -the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms -is quite significant, especially if your target delay is 25 ms (as in the LEDBAT_ spec). - -Clocks progresses at different rates depending on temperature. This means computers -running hot are likely to have a clock drift compared to computers running cool. - -So, by updating the delay base periodically based on the lowest seen sample, you'll either -end up changing it upwards (artificaially making the delay samples appear small) without -the congestion or delay actually having changed, or you'll end up with a significant clock -drift and have artificially low samples because of that. - -The solution to this problem is based on the fact that the clock drift is only a problem -for one of the sides of the connection. Only when your delay measurements keep increasing -is it a problem. If your delay measurements keep decreasing, the samples will simply push -down the delay base along with it. With this in mind, we can simply keep track of the -other end's delay measurements as well, applying the same logic to it. Whenever the -other end's base delay is adjusted downwards, we adjust our base delay upwards by the same -amount. - -This will accurately keep the base delay updated with the clock drift and improve -the delay measurements. The figure on the right shows the absolute timestamp differences -along with the base delay. The slope of the measurements is caused by clock drift. - -For more information on the clock drift compensation, see the slides from BitTorrent's -presentation at IPTPS10_. - -.. _IPTPS10: http://www.usenix.org/event/iptps10/tech/slides/cohen.pdf -.. _LEDBAT: https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/ - -features --------- - -libtorrent's uTP implementation includes the following features: - -* Path MTU discovery, including jumbo frames and detecting restricted - MTU tunnels. Binary search packet sizes to find the largest non-fragmented. -* Selective ACK. The ability to acknowledge individual packets in the - event of packet loss -* Fast resend. The first time a packet is lost, it's resent immediately. - Triggered by duplicate ACKs. -* Nagle's algorithm. Minimize protocol overhead by attempting to lump - full packets of payload together before sending a packet. -* Delayed ACKs to minimize protocol overhead. -* Microsecond resolution timestamps. -* Advertised receive window, to support download rate limiting. -* Correct handling of wrapping sequence numbers. -* Easy configuration of target-delay, gain-factor, timeouts, delayed-ack - and socket buffers. - diff --git a/libtorrent_utp/docs/write_disk_buffers.dot b/libtorrent_utp/docs/write_disk_buffers.dot deleted file mode 100644 index 1fc257533..000000000 --- a/libtorrent_utp/docs/write_disk_buffers.dot +++ /dev/null @@ -1,20 +0,0 @@ -digraph downloading { - label="" - node [shape=box]; - - subgraph user_space { - rank=same; - "receive buffer" -> "plain text buffer" [label="decrypt in-place (no copy)" style=dashed]; - "plain text buffer" -> "disk cache" [label="move buffer reference (no copy)" style=dashed] - } - - subgraph kernel { - rank=same; - "socket kernel buffer"; - "kernel page cache" - } - - "socket kernel buffer" -> "receive buffer" [label="read() on socket (copy)"]; - "disk cache" -> "kernel page cache" [label="write() to file (copy)"] -} - diff --git a/libtorrent_utp/docs/write_disk_buffers.graffle b/libtorrent_utp/docs/write_disk_buffers.graffle deleted file mode 100644 index e4db0baa6..000000000 --- a/libtorrent_utp/docs/write_disk_buffers.graffle +++ /dev/null @@ -1,2745 +0,0 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 137.11.0.108132 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {803, 733}} - Class - SolidGraphic - ID - 2 - Style - - fill - - GradientColor - - w - 0.666667 - - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - CanvasOrigin - {0, 0} - CanvasSize - {803, 733} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2009-05-24 19:00:26 -0700 - Creator - Arvid Norberg - DisplayScale - 1.000 cm = 1.000 cm - FileType - flat - GraphDocumentVersion - 6 - GraphicsList - - - Bounds - {{657.5, 133}, {89, 40}} - Class - ShapedGraphic - Head - - ID - 55 - - ID - 56 - Rotation - 90 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 51 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 writev() to file (copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{612, 198}, {180, 39.0763}} - Class - ShapedGraphic - ID - 55 - Shape - Rectangle - Style - - fill - - Color - - b - 0.853983 - g - 0.680927 - r - 0.98913 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 kernel page cache} - - - - Bounds - {{482.661, 61}, {128.839, 40}} - Class - ShapedGraphic - Head - - ID - 51 - - ID - 54 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.4 - g - 1 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.8 - r - 1 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.4822 - r - 0.910613 - - - - Tail - - ID - 49 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 move reference (no-copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{185.661, 61}, {128.839, 40}} - Class - ShapedGraphic - Head - - ID - 49 - - ID - 28 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.4 - g - 1 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.8 - r - 1 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.4822 - r - 0.910613 - - - - Tail - - ID - 1 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 in-pace decrypt (no copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{612, 54}, {180, 54}} - Class - ShapedGraphic - ID - 51 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 disk cache\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\fs24 \cf0 managed by the disk_io_thread} - - - - Bounds - {{315, 54}, {167.161, 54}} - Class - ShapedGraphic - ID - 49 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 plain text buffer\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\fs24 \cf0 page aligned in user space} - - - - Bounds - {{57.0805, 133}, {89, 40}} - Class - ShapedGraphic - Head - - ID - 1 - - ID - 30 - Rotation - 270 - Shape - AdjustableArrow - ShapeData - - ratio - 0.50000017881393433 - width - 20.000001907348633 - - Style - - fill - - Color - - b - 0.497307 - g - 0.504555 - r - 1 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.304265 - g - 0.307897 - r - 0.788251 - - MiddleFraction - 0.4523809552192688 - - shadow - - Color - - a - 0.4 - b - 0 - g - 0 - r - 0 - - ShadowVector - {0, 2} - - stroke - - Color - - b - 0 - g - 0.0271458 - r - 0.689052 - - - - Tail - - ID - 3 - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 read() on socket (copy)} - - TextRelativeArea - {{0.125, 0.25}, {0.75, 0.5}} - isConnectedShape - - - - Bounds - {{18, 198}, {167.161, 39.0763}} - Class - ShapedGraphic - ID - 3 - Shape - Rectangle - Style - - fill - - Color - - b - 0.853983 - g - 0.680927 - r - 0.98913 - - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 socket kernel buffer} - - - - Bounds - {{18, 54}, {167.161, 54}} - Class - ShapedGraphic - ID - 1 - Shape - Rectangle - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs36 \cf0 receive buffer -\fs24 \ -page aligned in user space} - - - - GridInfo - - SnapsToGrid - YES - - GuidesLocked - NO - GuidesVisible - YES - HPages - 2 - ImageCounter - 3 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - AutoLayout - 2 - LineLength - 0.4643835723400116 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2009-05-24 19:21:26 -0700 - Modifier - Arvid Norberg - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - OutlineStyle - Basic - PageBreaks - NO - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {612, 792} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - QuickLookPreview - - JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAG1WMtu3EYQvPMr+igdlpr3kNcoCZCc - 4khALgKM9Yryrr0Pmbt24p/Nt6R6hkPOcklJMBDpIHLYU83pR3VRX+gdfSGB30po8lpT - 29BftCdROhF+aEM3t0dJqyPJ8Htc0UKUtns6XHUABQCe6OaPpl01z6evyy21G7iQNTsR - 5B15clLQakc3v+0k/XwIr5CeO4XnpuLnBZ6r9BzrvF1KSR4+K2HJKlXaDkcnu4QDP1qY - zhMjmWShRISChRMyexebLKSVwRl8Sm9K50QNs7pz5Yr0yoOZ8RJmDJasfAJL7gDG7uLR - +OgVLAqOfnY05+XZseqIcvNns12eNt+a28P20G52zandrDiussJuS9L5UsK9NTH6kn5H - qj7FxN3ehcMIurvlPISbBf/hbCMLwLAmg+AKuKN3BbKuOOuCkHGBMsg2SyFjDpyFzY5+ - umcYTtACfxfWlNrV0uD+Hmn+VZaikHT/RFdts2pwDvrw9ema7j/RL/cx+91eehlYa+Bq - BU9FwsWBGPepad+MF9O/kIoWSJuXlUcE7RjxefmxoeV283HfPNJmT1+PTUvH5+WqGRxN - NYko66qWqL0CTytRK2fRTJXVdYV4I5QIkKltWSvt+6jruhTeqZpj/5QHOmSpiFmSdTW5 - AcniTL+eLCttaVCijDfKmK9KXYmKm3MU2eNh9bk50eem3Tfb1/M278P6UnuLFrhwcZY8 - 7gh0edfi2pa22FEd7k1NlhdoOyw4YcJClQqyux8KSUUDqaJFkXb0TtLCeuSXdoEJgSwC - 3WhLRhJ4rKPIjOOKwHGgp9BfvW/QVCCNu7Wk4xppmiHb1K99PRUd6XLvoopqYRWaqxTK - S8Og882cIidrj7gMkSviQhY5qaqzyKX7/u2liAYpctRZFH3k0pYscsnvTFFOhSiVY9aX - yJk21oNNBgbpOr1tlo8PVw/XdNjT0Ik8wy6DP4EsUYVGVCDimUL/EUwH8q9r+K/GoA9X - q8Pz94frAbXPMW2YYs8Hq5Z2ROiRETjjsznnTWcMzkNgJv49/YOOXmJwJ9GslUFKLnhx - uwQbnpp/TrNk8DK2EYBW1l1Cn/EAWCAcGqNqCi8rlv+BxJFKDCyQtcOUCBMWTYcYT9Iz - tzjbcQ56syz+xdQE9VAh08PTQNUoaXicjqj4cXP8TKvlat1cFxOzcwSZRajSpVdgfoy5 - MeZuucege6QP3+m0bohdvN8c3p/W3GeDm8DKle20EOqCdUO6hYDZkqqZZ11+7Xldy7Bu - VdHb6MweUFsCf/RgAZsVKC/JIL00mBvkU5eVRpMZyfXNFhkBdyIzVYyyIEsm7S7EN3dr - NcnACeqCgZPsDQxcS+Ew0sHAplIKyne6GeOc7qKCN81iBF2Xjp9dIiMpQGCkZAAC7mMF - lLPwBNSstvAil+osP/0EBRoMNIVKWOhxMWz2CxY49Nis2u/PpzN+7bvxFXRlSyEhZjBM - x/APV/sDjfmQKwtRZflcuJD87q6rJcuz//w61JXjSRfyu6XOJtRVuA5IHLgeqy8rXmGt - 7PKq4sE+U1TgAU43aMgaPy4qCLofHutvLCpml3SKkP3upgh1FE+eXYaSirEJJRUNUFKo - o3jdldSACX31WkWNj87qMeMXVJSruNUWejwudgeo/bYBtTd7VNYwBzN+fwUdFaUNRquU - Y3AuqMW4oLIB23+59qI8yKkgyiHPM1HO/D2ocrD4uSLnCpidv7w3aPNs21tGMPN1kssT - H1Fel9ZK5cmPj91p8fCFEsdB/ynF3eRqnlnMujvyeLd4veXrwtqon72C73TN79HpalcN - 64yTbNZnqCDfwrHi5n2zojhycmid+L3JfhAjFDT42PStg/l48e+Hl/l4VhEPzdp/3vIp - opKN0UiqliMjPYtkRAbR6K/xlkHEgoM5Gmk94HT2MRoJdaZ5Lg4+QcXKlVqb2k1w8d/t - 5tR8C0r3dDjrmzcCC+EqKJILFv4378E3YWGWzmBtG7pUuO/+A2VIBCcKZW5kc3RyZWFt - CmVuZG9iago1IDAgb2JqCjE0OTMKZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2Ug - L1BhcmVudCAzIDAgUiAvUmVzb3VyY2VzIDYgMCBSIC9Db250ZW50cyA0IDAgUiAvTWVk - aWFCb3ggWzAgMCA4MDMgNzMzXQo+PgplbmRvYmoKNiAwIG9iago8PCAvUHJvY1NldCBb - IC9QREYgL1RleHQgL0ltYWdlQiAvSW1hZ2VDIC9JbWFnZUkgXSAvQ29sb3JTcGFjZSA8 - PCAvQ3MxIDcgMCBSCi9DczIgMjYgMCBSID4+IC9Gb250IDw8IC9GMS4wIDI3IDAgUiA+ - PiAvWE9iamVjdCA8PCAvSW0xIDggMCBSIC9JbTUgMTYgMCBSCi9JbTIgMTAgMCBSIC9J - bTggMjIgMCBSIC9JbTkgMjQgMCBSIC9JbTYgMTggMCBSIC9JbTMgMTIgMCBSIC9JbTQg - MTQgMCBSIC9JbTcKMjAgMCBSID4+IC9TaGFkaW5nIDw8IC9TaDEgMjggMCBSIC9TaDIg - MjkgMCBSIC9TaDMgMzAgMCBSIC9TaDQgMzEgMCBSID4+ID4+CmVuZG9iagoyOCAwIG9i - ago8PCAvQ29sb3JTcGFjZSA3IDAgUiAvU2hhZGluZ1R5cGUgMiAvQ29vcmRzIFsgNDUg - LTIwLjUgNDUgMjAuNTAwMDIgXSAvRG9tYWluClsgMCAxIF0gL0V4dGVuZCBbIGZhbHNl - IGZhbHNlIF0gL0Z1bmN0aW9uIDMyIDAgUiA+PgplbmRvYmoKMjkgMCBvYmoKPDwgL0Nv - bG9yU3BhY2UgNyAwIFIgL1NoYWRpbmdUeXBlIDIgL0Nvb3JkcyBbIDY0LjkxOTUyIC0y - MC41IDY0LjkxOTUyIDIwLjUwMDAyCl0gL0RvbWFpbiBbIDAgMSBdIC9FeHRlbmQgWyBm - YWxzZSBmYWxzZSBdIC9GdW5jdGlvbiAzMyAwIFIgPj4KZW5kb2JqCjMwIDAgb2JqCjw8 - IC9Db2xvclNwYWNlIDcgMCBSIC9TaGFkaW5nVHlwZSAyIC9Db29yZHMgWyA2NC45MTk1 - IC0yMC41IDY0LjkxOTUgMjAuNTAwMDIKXSAvRG9tYWluIFsgMCAxIF0gL0V4dGVuZCBb - IGZhbHNlIGZhbHNlIF0gL0Z1bmN0aW9uIDM0IDAgUiA+PgplbmRvYmoKMzEgMCBvYmoK - PDwgL0NvbG9yU3BhY2UgNyAwIFIgL1NoYWRpbmdUeXBlIDIgL0Nvb3JkcyBbIDQ1IC0y - MC41IDQ1IDIwLjUwMDAyIF0gL0RvbWFpbgpbIDAgMSBdIC9FeHRlbmQgWyBmYWxzZSBm - YWxzZSBdIC9GdW5jdGlvbiAzNSAwIFIgPj4KZW5kb2JqCjggMCBvYmoKPDwgL0xlbmd0 - aCA5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDM4MCAv - SGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQozNiAwIFIgL1NNYXNrIDM3IDAgUiAvQml0c1Bl - ckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEA - AADCoPVPbQ0PiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - vA8MpP4AAQplbmRzdHJlYW0KZW5kb2JqCjkgMCBvYmoKNzc5CmVuZG9iagoxNiAwIG9i - ago8PCAvTGVuZ3RoIDE3IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug - L1dpZHRoIDQwNCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQozOSAwIFIgL1NNYXNrIDQw - IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 - cmVhbQp4Ae3QMQEAAADCoPVP7WkJiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBg4AMDz74AAQplbmRzdHJlYW0KZW5kb2JqCjE3IDAgb2JqCjgyNwplbmRv - YmoKMTAgMCBvYmoKPDwgL0xlbmd0aCAxMSAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5 - cGUgL0ltYWdlIC9XaWR0aCAzODAgL0hlaWdodCAxMjQgL0NvbG9yU3BhY2UKNDIgMCBS - IC9TTWFzayA0MyAwIFIgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl - Y29kZSA+PgpzdHJlYW0KeAHt0IEAAAAAw6D5Ux/khVBhwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwMDLwAAoTgABCmVuZHN0cmVh - bQplbmRvYmoKMTEgMCBvYmoKNjM5CmVuZG9iagoyMiAwIG9iago8PCAvTGVuZ3RoIDIz - IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDQwNCAvSGVp - Z2h0IDEyNCAvQ29sb3JTcGFjZQo0NSAwIFIgL1NNYXNrIDQ2IDAgUiAvQml0c1BlckNv - bXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3QMQEAAADC - oPVPbQwfiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwb+ - AwNLLgABCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKNjc5CmVuZG9iagoyNCAwIG9i - ago8PCAvTGVuZ3RoIDI1IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug - L1dpZHRoIDEyNCAvSGVpZ2h0IDIyMiAvQ29sb3JTcGFjZQo0OCAwIFIgL1NNYXNrIDQ5 - IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 - cmVhbQp4Ae3QgQAAAADDoPlTX+AIhVBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGPgDA0KnAAEKZW5kc3RyZWFtCmVuZG9iagoy - NSAwIG9iagozODQKZW5kb2JqCjE4IDAgb2JqCjw8IC9MZW5ndGggMTkgMCBSIC9UeXBl - IC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMzAyIC9IZWlnaHQgMTI0IC9D - b2xvclNwYWNlCjUxIDAgUiAvU01hc2sgNTIgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDgg - L0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dAxAQAAAMKg9U9tCy+IQGHA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY - MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED - BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA - gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAwHtgtucAAQplbmRzdHJlYW0KZW5kb2Jq - CjE5IDAgb2JqCjUxMwplbmRvYmoKMTIgMCBvYmoKPDwgL0xlbmd0aCAxMyAwIFIgL1R5 - cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAxMjQgL0hlaWdodCAyMjIg - L0NvbG9yU3BhY2UKNTQgMCBSIC9TTWFzayA1NSAwIFIgL0JpdHNQZXJDb21wb25lbnQg - OCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt0IEAAAAAw6D5U1/gCIVQ - YcCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBj4AwNCpwABCmVuZHN0cmVhbQplbmRvYmoKMTMgMCBvYmoKMzg0CmVuZG9iagoxNCAw - IG9iago8PCAvTGVuZ3RoIDE1IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1h - Z2UgL1dpZHRoIDM4MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFjZQozNiAwIFIgL1NNYXNr - IDU3IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+ - CnN0cmVhbQp4Ae3QMQEAAADCoPVPbQ0PiEBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM - GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB - AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg - wIABAwYMGDBgwIABAwYMvA8MpP4AAQplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjc3 - OQplbmRvYmoKMjAgMCBvYmoKPDwgL0xlbmd0aCAyMSAwIFIgL1R5cGUgL1hPYmplY3Qg - L1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAzMDIgL0hlaWdodCAxMjQgL0NvbG9yU3BhY2UK - NTkgMCBSIC9TTWFzayA2MCAwIFIgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9G - bGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt0DEBAAAAwqD1T20LL4hAYcCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgw - YMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMG - DBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCAAQMGDBgwYMCA - AQMGDBgwYMCAAQMGDBgwYMDAe2C25wABCmVuZHN0cmVhbQplbmRvYmoKMjEgMCBvYmoK - NTEzCmVuZG9iagozNyAwIG9iago8PCAvTGVuZ3RoIDM4IDAgUiAvVHlwZSAvWE9iamVj - dCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDM4MCAvSGVpZ2h0IDE1MiAvQ29sb3JTcGFj - ZQovRGV2aWNlR3JheSAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVj - b2RlID4+CnN0cmVhbQp4Ae3d21dT97YHcCWBkAu5QEhCLiQkEFgECIFg5CZBMHKLYIQS - RRCM0oJBlN0oWxRBVCqKiJUNSsVLRYZYKg5OdfdUx/nXzlzBPfauBNtJf297fh8cPkwm - Y3zWZP5W8vLbtYtCAiRAAiRAAiRAAv9tArspDASQUwO/Me7fEVB2JvBvQgD9U49g0x1+ - m1AYT/nLAkKhUCDgn8If+0flN9kTRKLEzYgpaIFPdCJRQgI8P+En/i9M/yf5+PgEYBdL - JFKpVCaTJVF2IABwwCeViBMTRcAfnf4vrB6eXgCbBuCBXZYklyuUShVlhwJKpUIulyfJ - eH/gj87+dns/Sg8zz8snyRUqVXKKWp2aqtFoKXgBjSZVrU5JVqmUPD/ob26ebfD5qeeH - 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 - PRXemoMN/ubAkbavghS0QHtba6DlUGPdgerKUrcz124x6tT82MdY97BzBPH84KsA35LJ - 5bvce8urqmt9dfWNTf5DFKSAv6mpod53YL+3smxPEdBnmNJSk+WbY//56z2/8IUw+DLA - 1xnMtmxHgcvtKS2vrPJW19TU1FJQAkC23+utrCjzlBQ587gsC0+vSIKxh5UTy54ffEmS - gp98szUrx5HvdBWX7PGUlkHKKQgBXqx0r6fEXVRYkMfZbRYj0CvlUtj2wrgtRy0/9zD4 - okRpFF9vslgz7VxuXr7T6XK5iihIAUArdBbkObjsLJvFZNBt0sO2jzX2PL4A8MXSJLkq - RaPTm8wWqy3Lnp3Dcbm5DgpKIDeX47gcuz3TlmE2GdK0ahUsHHF048RYOdHBh60Dky+T - K5PVGq3eYAR/S4bVRtmBgNWaYTGnmwx6nVadopJv0sPG2brt+Y9asHUE8bB2JDD6yuQU - 4Nel6eEJGE0UtIDRaNDr03RaTao6WaWQSyWJMPWwcbaz38RPSIzqK1Qq8Fenpmo0Wgpe - QAPq6pRklUopT5LBvhHFf4EeBp/HF/KjD/pSWZJcrlAqVZQdCiiVCrmch5eA/ObQbzP1 - 0a0D+FF9WPuJYgk8AKlMJkui7EAA4ICPd+fhYeb5fRN72X/6bo0ffdCH4YdjFx5ANGIK - WuATnUiUAPDRbfNH9PyJy+vHCQSbDwB+jvJXBITC6MTz8F8c+k+zD/xRf/4ZQOAxUHYi - sMnH//sn3f/lH/0LgJ+h/EWB/wCl/5IACZAACZAACZDAf43A/wOGt0kcCmVuZHN0cmVh - bQplbmRvYmoKMzggMCBvYmoKMzQ5NgplbmRvYmoKNDYgMCBvYmoKPDwgL0xlbmd0aCA0 - NyAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA0MDQgL0hl - aWdodCAxMjQgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQg - OCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtnftXktkax2u8oNxRBAVJ - BG8ISihGYibmLRUrNQo1TUVd4TWdKEvtaFqU5nU0zUodS11ZTjaeajpT6/xr59nYTE3i - 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 - w40Cp1eWraDkVIWjqoaMKm9qbsFQItDcRIaV11Q5Kk6VFNiy4OSC2wSeuvzPKg8MCuHC - sHJ5tDpeZzRnZucWFNtPl1c6zjurqqtrMFQIVFdXOc87KstP24sLcrMzzUZdvDpaDqPK - uSFBfubHBwQFk2nl0kgiJTXtiDXblld4srjUfuo0hiKBU/bS4pOFebZs65G0VKIkUkom - lQfDdfKX8Sfw8y04vKBQ+CJJBEiJSzIY0zKOWrOP207k5RcUFGIoESgoyM87YTuebT2a - kWY0JMWBkgiJiO+vTIgTX6EIRGFEijZBZ0g1pZmPWDKt1qxjGGoEsqzWTMsRc5op1aBL - 0BIlYSKB/zIBJ1AonFAeSJHKFSqwkqgzpBgPm0xp6elmDCUC6elpJtNhY4pBlwhGVAq5 - lCgJ5cBtsufo+qNQiBSJVB6lVKljtfHgRZes1xsw1Ajo9ck68BGvjVWrlFFyqYQo8Xub - +C4U3+kVyuMLJeFSeaQiWnVIrdZotdo4DEUCAFSjVh9SRSsi5dJwiZD/SYmfMjlw0Hd6 - BXNCuKRUwqQRcnmUQqGMVmEoE4hWKhRRcnmENIwUCTeEE+w7ub5+6iK/WARO4EoJ4oSQ - UhGKJWHhUmmETI6hTkAWIZWGh0nEQl+RcIKIEn9l8klKQGAQlEool8cXCIUisQQShqFK - gDAVi4RCAZ/HDfUVScC+SqBSoFQCwArUCmjh8ng8Pl8gADkYegQAKJ8PaLkgJITUSAAo - Oejv4CKH164U0AJWoFpADCQUQ52ADywHrhFiBGqESYnPCrlWSLUQMSTBGMoEdrkGBfoq - hAjZv0h8lfJZy66aAMw/QoDQJfkeIX+Igbvlc3b/Of5Jg8Bnqt9RHn/awL8gASSABJAA - EkACSAAJIAEkgATYIfA/4DMJeQplbmRzdHJlYW0KZW5kb2JqCjQ3IDAgb2JqCjMzNTIK - ZW5kb2JqCjQ5IDAgb2JqCjw8IC9MZW5ndGggNTAgMCBSIC9UeXBlIC9YT2JqZWN0IC9T - dWJ0eXBlIC9JbWFnZSAvV2lkdGggMTI0IC9IZWlnaHQgMjIyIC9Db2xvclNwYWNlCi9E - ZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUg - 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 - CmVuZG9iago1MCAwIG9iago0MzE1CmVuZG9iago1NyAwIG9iago8PCAvTGVuZ3RoIDU4 - IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDM4MCAvSGVp - Z2h0IDE1MiAvQ29sb3JTcGFjZQovRGV2aWNlR3JheSAvQml0c1BlckNvbXBvbmVudCA4 - IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Ae3d21dT97YHcCWBkAu5QEhC - LiQkEFgECIFg5CZBMHKLYIQSRRCM0oJBlN0oWxRBVCqKiJUNSsVLRYZYKg5OdfdUx/nX - zlzBPfauBNtJf297fh8cPkwmY3zWZP5W8vLbtYtCAiRAAiRAAiRAAv9tArspDASQUwO/ - Me7fEVB2JvBvQgD9U49g0x1+m1AYT/nLAkKhUCDgn8If+0flN9kTRKLEzYgpaIFPdCJR - QgI8P+En/i9M/yf5+PgEYBdLJFKpVCaTJVF2IABwwCeViBMTRcAfnf4vrB6eXgCbBuCB - XZYklyuUShVlhwJKpUIulyfJeH/gj87+dns/Sg8zz8snyRUqVXKKWp2aqtFoKXgBjSZV - rU5JVqmUPD/ob26ebfD5qeeHnpdXgrtGq0vT6w1Go4mCFjAaDXp9mk4LTyBZpZBLJYmJ - m6MfEz9KD4teKgN5gAd1k9liybDaKDsQsFozLOZ0k0Gv06pTVPIkGH3Ah/edWOftbn7X - i8Qw9KoUjU4P7lZblj07h+Nycx0UlEBuLsdxOXZ7pi3DbDKkadUqxSY+vO/EwIexB/pE - aZJCpdbqTRZrpp3Lzct3Ol0uVxEFKQBohc6CPAeXnWWzmAy61GSlnJ98OHBj2gvgmJVE - 6Q1ma1aOI9/pKi7Z4yktg5RTEAK8WOleT4m7qLAgj7PbLMa0TXwRv3W2Lp3dMPaJEplc - pdYZzLZsR4HL7Sktr6zyVtfU1NRSUAJAtt/rrawo85QUOfO4LIsJ8GHtwMqPMfiw7WHs - pUCvNVgyuXyXe295VXWtr66+scl/iIIU8Dc1NdT7Duz3VpbtKXLm2jN4fLlMLNrOHjaO - EujNmZyz2FPhrTnY4G8OHGn7KkhBC7S3tQZaDjXWHaiuLHUDvsWoU6vkMPjCrQsfVg4/ - 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 - 38RPSIzqK1Qq8Fenpmo0WgpeQAPq6pRklUopT5LBvhHFf4EeBp/HF/KjD/pSWZJcrlAq - VZQdCiiVCrmch5eA/ObQbzP10a0D+FF9WPuJYgk8AKlMJkui7EAA4ICPd+fhYeb5fRN7 - 2X/6bo0ffdCH4YdjFx5ANGIKWuATnUiUAPDRbfNH9PyJy+vHCQSbDwB+jvJXBITC6MTz - 8F8c+k+zD/xRf/4ZQOAxUHYisMnH//sn3f/lH/0LgJ+h/EWB/wCl/5IACZAACZAACZDA - f43A/wOGt0kcCmVuZHN0cmVhbQplbmRvYmoKNTggMCBvYmoKMzQ5NgplbmRvYmoKNTIg - MCBvYmoKPDwgL0xlbmd0aCA1MyAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0lt - YWdlIC9XaWR0aCAzMDIgL0hlaWdodCAxMjQgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkg - L0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0K - eAHtnedi2zgWhZOZuPfeu2y5yb3ITe699ziuyc7s+z/CnnMBkJRESialbFx4flAs4CX4 - GQBRr798CRUSCAmEBEICIYGQQEggJBASCAmEBN46ga+itx7LNxA/BcrevoEovdkoaEp/ - KemjNxvbPxsxoUNQfytxV8792Wi9zacTjEL1TYnMFK+3GeE/GiuBBUDfvhVofftmeP3R - iL3FhxMWWYFUYZGosLCggMCYvt5ijP9knAQW0lUhWBWXQMXFRUUABl4hrtQ/DMssJqzC - ouLiktIyqLQUxACsgMkrTF1JwJi0FCywKq+gykEMvEJcSaDk4OtXllmFRcKqskpUCWCK - V5i6koippCWwKiqrampqoZrqqirhheQV4nLiIi2Vsioqq2vq6hug+joCqygvLQlxOVl9 - +YKM+K0A38Kyiqrq2vrGpmaoqQnAaqorK8pCXCm0UMYjH5YhZQFWc2sb1drS1AheVSGu - FFjMiEXFpeWE1dTS1tHZ1dXV2dHeRl7/Z1xsgaUoObK5HKUY5qFvc6rUYj4UWO2d3T29 - UE93Vwd4NdRW5yN1uUTU6xTb80ZeYYKfN5b5G8QKKqYs4lFoMWW1d/X09fcPDAz0R3p7 - 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 - 9Lzjc18QXoqY3SD/3Egyvr3mZf1kDBxeJAHFKmQREggJhARCAiGBkEBIICQQEggJfDYC - /wOuReZCCmVuZHN0cmVhbQplbmRvYmoKNTMgMCBvYmoKMzk4NAplbmRvYmoKNDMgMCBv - YmoKPDwgL0xlbmd0aCA0NCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdl - IC9XaWR0aCAzODAgL0hlaWdodCAxMjQgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0Jp - dHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHt - 3elTE+kWB2CFQCB7QpOQTUICwSaBEAhEEpE9CLIoi0ZQFoM4aDCKIiiCgCgKIouiKCO4 - IIwoisOo44zW/dfu6YRRR4POy/SXW/f8Plha9dZL1dOnTrd8OGfLFgwKoAAKoAAKoAAK - /L8JbMWwIEBYNfATQz4nFLM5gc+EAPqPHkHAHX4ahxOG+dcCHA4nNJR5Cj/298sH2MO5 - 3IhAIjHEAut0XG54ODw/zjr/d6p/XT4sLBzYI3k8Pp8vEAiEmE0IABzw8XmRERFc4PdX - /3daD0MfCp0G4IFdIBSJxBKJFLNJAYlELBKJhALGH/j9tb9R3/fTQ80z8kKRWCqVRVFU - dLRcrsCQC8jl0RQVJZNKJQw/6Ac6zwb4TNUzRc/IS8BdrohRqlRqjUaLIRbQaNQqlTJG - AU9AJhWL+LyIiEDpB8X300Oj5wtAHuBBXRur08XpDZhNCOj1cbrYbVq1KkZBRUlFQih9 - wIfvnWDv261Mr+dGQtFLo+QxKnDXGxKMidtpOinJhCESSEqiaXq70RhviIvVqpUKSioO - 4MP3ThB8KHugj+ALxVJKodLq9PFGOsmcbLFYrdY0DKEAoKVaUswmOjHBoNOqY6JlEhFT - +fDCDWofCq9Znp9eHatP2G5KtljTMzLtWQ6IE0MgwIhl7bBn2NJSU8y00aDTKAP4XKbr - fNt0tkLZR/AEIikVo441JJpSrDZ7ljM7JzevoKCgEEMkAGT5ubnZOx32jDSLmU7QaQEf - 2g60/CCFD90eyp4P9Aq1Lp5Ottp2OHPyCl27S/aUlVdgCAXKy8pKS1xF+bnZjsw0S5Ix - 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 - QyoAbEIBsPN5kRGf5DdoOEzdB/DhlQv6UP3wACCRmM0KMHxcbri/5KHdhGwN9okTgF/X - h1cu8IdymAfAJByzKYGAXhiHwwll4H8o/yV/4BGEYv6FAGPIZOuPSv6L6oe/wvFPCVyA - fxIIfLIjU//7M8B/oQAKoAAKoAAKoAAKoAAK/O8I/Be7QBGNCmVuZHN0cmVhbQplbmRv - YmoKNDQgMCBvYmoKMzM1NAplbmRvYmoKNDAgMCBvYmoKPDwgL0xlbmd0aCA0MSAwIFIg - L1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA0MDQgL0hlaWdodCAx - NTIgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmls - dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHtnf1Pkn8Xx/NZnkERFCToAh+AkK6k - SK3A4cwHLLM0etBZVAvDqBaLZQ80UyYz09L5MDVzalNj2py5Vu3+1+5zYff9vUv69P1u - V/fnl/P+obUd87jXq3POBf7Avn0YJIAEkAASQAJIAAkgAboE0jB/lMA/sgs/SfpfycDw - TOAvtumA+m+o2fUBP0VmZhbmDxLIzATInJ7faUka2dWRnZOTuxsBhkcC36Hm5GSDcBDz - GyvfjWRlZYMOgVAoEonEYrEEwysBQApghUJBbi7nhWyFU5IBGwuEgA6xRCqVyeUKDO8E - 5HKZVCoBM0LQsmvlFwssqQRmhDMikcoUirx8pbKgQKVSY3gkoFIVFCiV+XkKhUwq4azA - rMACSy2FmxJuSDgjcvChUhcWaTTa4mIdhkcCxcVajaaoUK0CL/KkFRgVTkqKB7CkEjgk - IjEYASFgQ6c3GA4wRgyvBJgDBoNeB2ZAC1gRi7izklpKGndLcgQwJIp8VaEGfDDGktKy - crPZYrFieCJgsZjN5WWlJUYGvGgKVfkKGBVBDnfp9w4KjAkoyRVJZAqlWqMzMKZSs+Wg - zW5nWfYwhjcCgNNutx20mEtNjEGnUSsVMglMSlZmiu0FYwLnXZhUotUzJeVWm52tPHLU - WVUNqcHwQoBjWeU8eqSStdus5SWMXpuUIoRDn2JQ0mBMcoViqUJZqNUby6wVrMNZVXPC - 5a71eDx1GJ4IAMxat+tETZXTwVZYy4x6baFSIRULc2FQfl5eu2MiAiVqrcFktrGOYzWu - 2rr6hsZmb8tpDG8EWrzNjQ31dbWummMO1mY2GbhJkYpSDQrnBDaXHJToTWZ7pfO423Oq - qeVM27n2Dh+GRwId7efazrQ0nfK4jzsr7WYTt77kEhiUPcsLVld2rkiap9LojeYKR7Wr - rtF79rzvcmdX9zW//zqGJwJ+/7Xurs7LvvNnvY11rmpHhdmo16jyuEHZs7zS0uE5GMak - UMeU2Sqr3fXNre0XO7v9NwO3g713QhieCNzpDd4O3PR3d15sb22ud1dX2soYXSEMCjwP - /3xQuNUF10SlMZRYWaer3tvmu3L1RiAYuv8g/DCC4Y3Aw/CD+6Fg4MbVK742b73LyVpL - DBoVd1Fgef34Uj7pRJav1jHlFY7jdaCkyx/ovReO9D15Gn2O4Y1A9OmTvkj4Xm/A3wVS - 6o47KsoZnTpfltJJtkAsVxbpTVb2mLux1dd1vScUjjyJ9g/EhuIY3ggMxQb6o08i4VDP - 9S5fa6P7GGs16YuUcrEge8+cZGYLJNzqKrU5ajwt7Vf8PXfDfdEXsfjLV6OvMbwRGH31 - Mh57Ee0L3+3xX2lv8dQ4bKXc8pII4Mj/tLsyc4TSPHUxU253njx19uLVW6Fw3/PB+MjY - +MTU1DSGJwJTUxPjYyPxwed94dCtqxfPnjrptJczxeo8qTAnhRORFM6J0cJW1Tad77wR - BCWx4dHxyZnZ+bcLGJ4IvJ2fnZkcHx2OgZTgjc7zTbVVrMUIB0UqSuEEHruURftLDlbC - 6vJ1B+5Fng0Oj01Mzy0sLi2vYHgisLy0uDA3PTE2PPgsci/Q7YPlVXmwZH+REh689swJ - PArLlNw5OXKi/swlfzD8uD8+OjEz/275/eraOoYnAmur75ffzc9MjMb7H4eD/ktn6k8c - 4Q6Kknvw+vmegBM5OCmzO10NbZ03Q5FobGR8en5xZW09sbGJ4YnARmJ9bWVxfnp8JBaN - hG52tjW4nPYycCJP6UQsL9AcgBPvbjzXFbj/qD8+NjkHShKbH7cwvBH4uJkAKXOTY/H+ - R/cDXeca3XDkD2gK5OJUcyJWFGgZ86Gq2ub27p4HjwdevplZWF5NbG5tf9rB8ETg0/bW - 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 - GVnZsL1k3KTomZJyq83OVh456qyqhtRgeCHAsaxyHj1Sydpt1vIShltcChlsrlRjAk5g - UHJyRUkpGp2BMZWaLQdtdjvLsocxvBEAnHa77aDFXGpiDDpNUokoNwfG5OdzAg9hMCgg - RSCSSBX5qkKNTm9gjCWlZeVms8VixfBEwGIxm8vLSkuMjEGv0xSq8hVSiQiegzP3XHju - uRgGBbYXTIpYKs9TqtQabTF4MRxgjBheCTAHDOCjWKtRq5R5cqkYpoTbXCnG5LsUWF9C - GBV5Xj5oKSzSgJliHYZHAsVgQ1NUCELywYhEJITF9Ssl+9KSkwKHPmlFplCAF2VBgUql - xvBIQKUqKFCCD4VCljQC5z2p5KcXJ99f0ielZGbBqIAVkVgilcrkcgWGdwJyuUwqlYhF - MCPckMAtSU9LrQS2F0wKd+m5s5IrEIIYkVgslmB4JQBIAaxQKAAhMCOckV8r4Q79rhXQ - Al5ATDICDI8EvkPN4XxkZf7WSPLxi7OSnpGRkcmJwfwxAqAjgxsR4ox8vyrcsCS9cF8P - gX+J4ZXALtfkn4D6v9j/zl/g6zF/kMDfcYBfgwSQABJAAkgACSABJPAnCfwbh/F8qgpl - bmRzdHJlYW0KZW5kb2JqCjQxIDAgb2JqCjM1OTIKZW5kb2JqCjU1IDAgb2JqCjw8IC9M - ZW5ndGggNTYgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGgg - MTI0IC9IZWlnaHQgMjIyIC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29t - cG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7VxnQ1TJEt01 - kkVyTkMYchjCAMOQo0QRBFHxqW///09451R1970TbkBmePuB/sAiupyp6uoKp6r7r7+e - 17MGnjXwrIF/lwb+xnr6T0RU/3qyT6CgL9zSPz8FvCC9ePHSt168kB+WHR0oAvzq1avX - svANPobAlxmc0IAi7ps3b7HevMG3go6/Kiu4CE2JgVtRUYlVUQF8oL8S0csILkK/EuDK - yqpqWVXAJzxkx7aXDVygKXMFgGtqa+uwamtrqquADtnLCU5oCl1RWV1dW/fuXf17rPp3 - 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 - V/g74/6EpAsn6XQUnuh8mbgbHyBv9XR387ViItu7zxFT15EfgdgOnE9By4vM7e0dHR2d - voU/tgOXrzTzkWhpv+q42h+ebvlgAm7vfNThBWy+RN3c3NzS0tLqFv6AH8nr1PXmeWpc - e464nxcpt9JssufQO57+Ni9wNzTgM3gLf7Svcte4Z7lxtv/cyo3gqna+tY7LF3x5HE+P - vytYfIwcd47lxnO8Jn+04FZymjvQ8eK6Prlek7P0EXa8wm6a3bE6zjHBaXF8aJ4v3Ffo - W/PAyVn4sXn9Xnrdj1a4fjIhlYEu8PwAWHxl37/kh6+xvKbvY0zcpxJFJzw/gCzieMv8 - UP5B3J6v7/eHf0t0LPxeLIEo+KJ/p/+uRDJ7n8n82sj/eP/H83fPGnjWwP9XA/8D1+7G - zgplbmRzdHJlYW0KZW5kb2JqCjU2IDAgb2JqCjU3MjQKZW5kb2JqCjYwIDAgb2JqCjw8 - IC9MZW5ndGggNjEgMCBSIC9UeXBlIC9YT2JqZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lk - dGggMzAyIC9IZWlnaHQgMTI0IC9Db2xvclNwYWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVy - Q29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7Z15Q9rM - Fsbb3rqvaEVFxR3cwBWLiNSFqiiuRa1Vq+193/v9P8J9zjmTlaA2EIqa54+QZTKZ/Jw9 - c47v3vnyCfgEfAI+AZ+AT8An4BPwCfgEfAK1TuA9q9ZTWQPpE1DGtgaSVLNJUJQ+iNRR - zab27yaM6RCo/4hol8/93WTV5tMJjKD6KCJmwqs2E/xXU8WwAOjjxzqljx81Xn81YbX4 - cIJFrECqvoFVX19XR8Aof9Viiv9mmhgW8lU9WDU2QY2NDQ0ABl4+Lvsfhuosylj1DY2N - Tc0tUHMziAFYHWUvP3dZgFHWElhg1dpGagUx8PJxWUDxwfv3VGfVNzCr9g5WO4AJLz93 - WYhJ1mJYbe0dgUAXFOjs6GBeyF4+LjMuoiU5q629M9D9qQf61E3A2lqbm3xcZlbv3qEg - fqxDW9jS1tHZ9SnY2wf19gJYoLO9rcXHZaNFdXxDU0trO8HqCw2QQv29QfDq8HHZYFFB - bGhsZli9/QND4eHh4fDQ4ADxegW4aFTnKCuG5x1xHY8qvqUNOQuwwiOjY9DoyPAQePV0 - dVYidzmmtmonaYqgSO6ezh1TgtURAKzB4dHxyclIJDI5MTY6PBiqAC6VLEkuxlcvW9zT - YlhBgjURnZqemZmejkYmxkaGBvrKzF3ECqAYkZrf0AbuL/NXmsNAdxDFcHQyOjMbi8Vj - sdmZqcjEaLg8XMIKqGRyo54kw/a/sW0klftgdOFRDAXWRHQ2Nr9IWojPzUxNjg2Xg4vz - laBiSkgsjdmrrWYHuU0DDQ1NsOKLyyuJRGJlZWkxPjcdGS8DF8FiVmpqo5lH7C2t1RaP - fO0bl4nAaEe1hqizZuOLK6vJ5NpaMrm6srQQmykDl4JFU2Y8tUHj9XY1DJXBaHW2nUoB - kTpy+ezOQFd3D1rDMGDNANbntfX0xkY6nQKvxfhM1G3uMmBhmID8C06dSC8God3V1yer - ykhAT5D6pMNjgBVbXEmmNjKb0JcMeCWW3OPiYlhX10BTG5R/8UfhMWgwGOytvmg4p6uc - x/f1DwyGR8Ynp6gYfl7PbG7vZLPZne3NTHpt1TUuqbN4/NmKmY0ukMIQtL8/FJKhFY+v - qrQZZA2xZH/Q9ZMHh4ZHkLGmGVYqs7XzdXcP2s1ub26kXOMiWjJYpyEVynpff2hgcIjH - VRhaVUsjrFG7+KybNGCkMz4ZmULPYWkluZ7Zyu7u5w4ODw5y+7s7mxnXuBgWj6jaO4lV - iAago3jW+EQVNakJw5NIJBqN8q920kVCJiPovVMva3mVYe3lDvNHx8dH+cPcXnbLLS7O - WjTD2Noe6O7pCw0C1fgEPWtquoqaIc2K5ljqYJav/HFKENlcLA5Wic+p9Jft7N5B/vjk - lHScP9j/uu0Sl5o0o5mN7h40Iijrkei0PGu+mlogcX/btOGTC26SgbiWltHJSqVRv3/d - Pzg6OT3/Bp2fnR4fusZFBVFmNpCzePhJ+ZcfRZ3famlV02dSMpnkX+3k6p8nY3X1c3It - ld74somMlcsfg1Xh4vLy8qJwfnacz7nMXaBFk2ZtHV09fQPDY2hwqawnVtHxTaXWq6m0 - pg2Rdph2l4h0eiMDVGgK9w/yJ2ffCpdX30ngdXbiEpcUxKYWVFq9oaHRyenZeRokpNbT - GTyrqtpibZslp9ylYmsbHSygyqHGOj0vXFx9v/5Buv5+WTh3iYto1WM+lrLW4MjE1Oz8 - UoLL+hY9q5ra1UXdInSMDLlKxu7u3j51Go6OT87OC5dgdXNLuvnhHhdo1dU3Yj4WWSs8 - FmVY1O/N4lm53EHVdWhRWY8/PMyjz3ByClbIWMTq7ufPn3e3tz+ury7c5S6utqggfkKt - NTEdW0wk09yVO+DuyXGVdWJXGc8/QY/h7Jwrd2QsZnUPgdeNM66nvwQJLSqI/UOjkdl5 - GlFtf0XvBC3uGZ5VdVErr6nMh3/7VqBmkCqsG+Sr+/sH1v3Px3E9sqSEaaHa6g6GwuPR - 2OIqRlRZ1TspXFRRaNxLylUyKLYrNIOo24nVT7D6xXpA9rLnLjXfxbkLK3BouaXTt6X3 - HzBGbKZqiwri/HIyvYl+7xE1IqrF5Wa3Cptrk6jpMuTu4XQ/4rmR+opY/Rb9enhwwCWT - zx1t+OgvuErQQpPYhmprcHRyZiGRymzvMixuRG6qKW6xnDYuE8FR3VHNTmWQWf0X+v27 - BK7wQC8+y7Y2N9KCJeflXchbaBLbA1JtLa6ub2b38yfU4kr+xbNetFCto7L6RawIFYt4 - 2XNXbHpyNBzq7Q600xKJZ9Aai84tJdNbu7mjU4J1y0VdKsYXvEVVxQUQnP4RSfbSce1/ - 3dpYSywSrqH+YDd/xC6VuShvoQOBJjE8PhVbTma29w6Pzy8I1r1e0lWBf6k/nKEUKv7h - 0si4qFe/n93aSK4szE1NjAz29UhZLJG5hFZHVzA0PD4VX1nL7OznT79dXt/e3T+YMq9k - 4Ze5NXNS+2ZcmJHIbqaTy/Oz0TEqi1rmcmoVTbQmpuMrqS/ZXP60cIWs9fBby7oOj3uB - p/5V4qTruC4LZ8cHeztfMJmKsqgyl2oWi1tFC635RGozmzs6u/h+c3f/6/c//2hPeF2/ - xEtw3d58v/x2epTb3d5YW1mYjY6HQ1RzUbPo2Co+Qet/r0jGn1zhQjf19sd3GjKiLK5T - 5qKa6xM3iyVoYXarCV156Zwm1je/HlAlf8MF8d9XxAqvYsf16+H+7vb6qnB2dLBLFf38 - bATNItXzXBQdKi5MQbwZWlZc6Eig2yWZ63BvJ5NKLM5xUexCD5X682+dloFLyiJlrptr - 1FxaUZzC9/7ebgx/GrmDWlTNv6m8ZSqMUtFTzXV9WTjN59CJ+EwVF9Hiap56XD4tVXn5 - tJ5uk/R63i+JfwDrX8pafi3/KDE9ZxEs9E9d9CB4VM3jREwGFvXlH336C7toh/XbRe/0 - cVrGE17RnuQs7m2VM/LxR9UyZfOcUXUtzNjQn/1J/eHEkUN8+hTEVeH8z2ds/NnA588G - qnn5mphplo9Yz90+Ywa84jPNtfMVg74l26WI2E+r46c+sSAYIqjoV4xKfyFz+sz1J+fu - zFI8zKeMfXOs9g9p3nwhq/jXV+O7Ka0AKi0jHO/ZA9rf3nxsDqtFY/tIS6cRrMJfXzHB - VeEv+6Yv9FdFkg/uV1emQPquKbDt1Z0PObx+96V1AYA8qNJf9mU6sPKrRmTpR0GTthLk - 8V8tNP9a3956pAek+EqsLvFo1YgHK5JkZREvIi61sS8+kiXHKvSZJkcS2kUOTPE4LVzy - YkWSV6vdeNVa3kFYg8ayLGuTA1PoI5ETBTqnLh/RHbjXcVWcF6vdPFhJqS2BpPWQ+0q0 - XyQtIP/qV+WOnJIjCHWNQuI2LRrrgkuPVlKy14RKrtLdIm2LdjSp46d/+AbYL7EIgMDQ - UezynrqOwBQjP9G2mNeLVbrvvFgBLmu3aR13xixZ2G3e6ou8scPnteBfSOr1GcWWzkLb - 4yAIT0aHLNtCcS9WgBOtyloXKIsAsRGAmUBJsf2AeaOHXFvDWn0REFhZ4AjntMswZiU7 - BPVMsw2CR9YFqLiUT45KWa7oJihLS0vLSti1y2SgIrsqAN+xIsL7W+wx5ABn1fXl5aUl - upktXCwGLh5ZrqjMVTGrKN28iWybYmaJtZOx1eyeLMZQck88HlfvrtCLwQ+2gkZdjMNn - wdwc328znvLIKoqLInlegt1rRSzuxEpO2c3BEg2CAV2x2KTOstHD4BYxGtPJ28AahmUU - NyKRh5oN87yxuIPnpQpbc45AbJRJjmPGlWifZTfXtByrMHQPvzlDsBCVA91uEQHHxigK - eqjF6NMra07Q0o1fK2QpTFa+bO4b1sRHto2yBdZ+jKu4Sd5cB28wBVA+oEvgEw7jNoqh - yKLYG0thzlz/qausFTrZkfezYJ4tO9pWLumG5uYdLQgZdWtvr7AbKHlP50ORUxTF9ure - WKGDluQueLqsmIcD9lNAvud6gkH+0TdWDwa2Iz0U/AVoDgPMONVfwMDDsVMkxa4QPPJw - YMJVIe8Z7PciwCLfhvBu6CjlHkP7MYWhe9T7W3gqmnxOrkvcHIXVzYZX3jPeabjICRN4 - NTU1l+mZRTlXaSfhHfhXNurKoz/qLn55wmCiqO8qwhK5is3qwsUzzyyCSzzZsJteOJ8t - y+uPOO+B81onOXj20U8Z4dWr27HamLfC3WsLXOSSbO6BvPP6w7jIT28FPUqRUygTczm0 - bItcRhlX9TcnDAZCY4/OQxSO7iqKCie88yiFZV2o6yvsrYx8eBXpac9ncovx/gZD654K - geDOcVKbpfuUqqy3MloFR7ygDyz28VbWRjmIs/88GqcR2ESgiDid0K/TLU5xeusJj3jp - xISb660Ax9b0Gvq55+yY7oO3uRLSAmkRWpKLZ/O8nWdeFoVXBbaWZLs/0ChYoD+GyJx0 - Hs+JnwvvPHiaH1iRfYblJqZSlD+QXapZJSInXK/ZO2yJ19bqUzMh2S91g5yXyQLf8/Dj - lLSr72WakxpGctTse7XWwDj+clEk5080ced7THdkZJzkel5zMO974zfAOO+hKKIXwbmL - XUfSwNP/Tw/OrKjvSH0u4CK3pP5/ESmFSZ0HLQ2X/x9qnmCFy5S5yPF0vZqI8v/70WPM - 0CvjaRWMKcGLRONL/z9rlWBGuIxpKBqI8wichwMlbnnDp6nLj9EmxpZqYE6jTBk6vWEq - JV+dcPEUlBqM00Cdz5W8421fYDZCTCP1vtim9W0zMr+94qX/mK/5+44EhJXjJf+kT8An - 4BPwCfgEfAI+AZ+AT8An8IoJ/B/4hOUjCmVuZHN0cmVhbQplbmRvYmoKNjEgMCBvYmoK - MzYwMgplbmRvYmoKNjIgMCBvYmoKPDwgL0xlbmd0aCA2MyAwIFIgL04gMyAvQWx0ZXJu - YXRlIC9EZXZpY2VSR0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVh5 - OFVd219ndMzHkDEcypB5yjyPGTNnKPMYjuMYQkhRlLnMhCSJepSIBlKmzKJEiTJFZiIy - vfvQ0/N91/e91/vPu65r7fVb9/rdv7X2vs/a91kbAOxDJwLBBw4A8MUHEs10NXDWNrY4 - siGAArQAC0QBzMklgKBuYmIIUf5NWR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHBsvl/4/S3 - mYYITQgATBgyMHocYDUSdj7AFiR8LpAQCHE8SdjF08kVwuEQFiZamGlCuBTCNB4HuJaE - nQ/wGxIOdvEg+Q4CgKbHu3rhASCbg7CKq1uACzRMmtfVNcDFF8IpEK/P19cP0sdCFQi4 - EIiQLzYBwkdJzwVqoWIL6UqVQzrJ/9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1239WMKae - AHcpyX05GJUGAKiRvb1VPkjzOgA7qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguEdQHwn/oH - 9/zbAwEFBwowzA6+igxBLZJ5YcYp3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUeZuVI4cLg - IrgXj7gcfcenw195DCeYKLQu4ijaLC4skSS5KG18/J4sXM5O/qEiUslSuVhlSU1RPVqj - VYtK21gnUbdDD62vbhBq+NBo8iSriaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY7ZUdnBwv - Ot1ybnAZcd1yZ/GQ8jT28jx7wTvD54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhrKD4s5HxU - eFxEcmTaheyovIv5lwqjb8Xculx4JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141vaKbJ - pQtn4DIZs8iyNrO/5wznduXV33yQf7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WUZZVH - 3fd8YPKXbAXHQ8TD6UddlZWPM6pCq08/Ua3hroXXTjxtelb8POaFS516PVf9zsuhhiev - Ul97N2o3cTX9bO5pKWkNf2PWxtf2q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr9xzv - vwwUfXAe5Bn8OpT/0fYT06fe4auf1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6UxTfk - t8fTzjN0Mw3fCbO42d65i/OS8xMLWYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4NsY2 - S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNMsFmw - t3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFSqUWF - QtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlpi7dL - Ov3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6Fpx2 - Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJKUlzy - hZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZuvstvKagtLLuVW5RwO7zY987pEsO7 - iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QXQXX2 - 9bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f7f5I - KPriA5iBLx+eDMYPOXyU+oT+NDh873PoiN4o8+jkl8qvkWN644zjIxN3JwlTslO735qm - 42YMv2O/v529Pmc2TzffvXBtUXtxd6l6+ewK50rvatQP0R8f16LXBdd7fvpv0G9UbOpv - TvwK28JuFW1LbzfumO6M7p7dXdsL248/Ai6MUEVaojzRkWQZmHvkdRQDlLPUcBpGWn6s - Ep0pvTNDEGPsoWymcuZnLO2sQ2zf2Nc54JwUXEw4Lm5eHqEjokfFeSX4JPhFBQSP8Qly - CDEIY4R/icyKfhJrFa+UyJO8LOUjbXZcRoZZZkN2UK5WPl2BqGisJKiMUh5RqVVNVfNS - 19Bg01jRbNcq0g7TMdMVPAE/8VHvkf41AxdDRSNGo3njlpOFJqGm5mYi5mjzLxZPLdOs - 8KdOWPNYb9sM2D60u3ba7YyyPbP9skO7422ncGcrF1FXMtdRtxr3JA93T2UvRq/Zs6+9 - s3zwvlp4VvyCXxMhy9+XqBbAGDAd+DwoMdjxnGQIMmQgtDQs9Lx+OHv4XMSLyMQLZ6JE - o3Yv9lwqiCbEqF+mvzx+pSo2Ns76quDVnWtv44sTQhINk7iT1pM7U4pSQ6+b3BBIA2lD - 6ZUZCZnuWerZh7M3ct7nPspLvumbb1AgWEhR+P1WZ9GD28nFxDtWJQp3uUpRpbP3+sue - lxffT3oQ+pdbhdlDtUdilVyP6aqQVRvVC08maz7Xfnja/6z3ee+LvrqB+uGXkw3Lr/Ya - sU28zcotNq0hb/LamtpXO3m77Lvzer68FeqL6H//Xm6gcJBuKP4T9XDmiPBo21e/ca6J - D1M5067fZefo5lcXPy93rTavNf6s22zYat8ZJsX/IPeRcgJaBoBMLACnjgJgXgRAbA6U - 6iyhXFUKgAk1ABbyAG6TDeD2tQDm7g5+5w8oh6AAJaAH7IAPSAI1cBI4AH8QA7JBBWgB - I2ADxgATgxnBvGHxsPuwbtgKnBmuDHeFJ8CfwL8gqBDyCE9EJuIN4hdSBOmMzEL2oMhQ - 6qhwVC1qHX0cHYSuQf8iUyW7TNaFYcI4YR5gtsj1yXPJFyg0KLIolij1KIupYFSOVA3U - PNRXqOdozGnqaAVo07FIbCD2G50d3Vt6ffomBlWGOkZlxvpD6odamYyZPjA7My+wRLDS - sBayHWfrYHdl3z2cyyHPMcQZzoXjasH5cbNxt/AEHeE7MnQ0iVeHd4/vOX+ogLzA9rFX - gnFCZsKcwvMi9aKpYh7iqhJsEhuSg1LPpAuOx8r4y9rLGcmrKkgpCiodUeZS4VTlVONR - F9AQ11TUOqFtrXNWN/JEul6FfofBjBGFsehJC5MI01KzAQu0pbyV36m71mO2ODvH07fP - zDhIOkY4dbgcdiW4tXrgPMO9hr1VfG7jKf2CCeNEi4A3QarBT0NkQ2vOK4a/jjS6MHwR - H42Myb+iFDtyNSZeLGE06UaK0XXKGz3pWZke2Uq5THk/80cKO4saip+XvChtLnt3f7YC - 80jksXX1tZrGZ4gXevVpDZONSs2ZrWvt1p0veo68vda/MmA72PDp6Ocro1NjGhO5U8sz - mrPX50eXjq0QfjxZ39xU3YreaSb9fn7HnwEcBvxACqgDE+AEAkAsyAOVoB2Mgx0YK+w4 - zBwWALsBq4YNwrbhPHA9uD88B94EX0JwIAwQ5xHliM9IOqQ28jyyEjmL4kU5om6iPqHZ - 0Hbom+ivZPxkeLInGIAxxGRjpsnlyePJv1Acp0ik+EapQVlAuUvlQPWKmo86gXqNxoGm - g1ae9h6WHZtMh6QLp1un96dfYPBlWGQkMq4fimBCM6UwH2YuZ1Fk6WJ1Yv3JlsQuwN54 - 2IkDcBRxanPOcl3HKeK+cafzaPGsHyk76sjLwtvPl8J/UgAr0H8sW9BJSFhoU/iNSI4o - XkxLnEN8U+KDZI1UjvSF4x4yprLKcsLynAr0ihgloLSlvKmyofpLbVcDpUmjxarNpyOj - e+LEaT0i9Fa7Y/ja6OtJBJQDjcyCzAssOi23TolYO9pk2HafJj+jbR/j0OJE7nzSJdN1 - zF3UI8Kz+yyP9zmfHij7XSFMEnUCyoKwwSHnxkNNwhrCpSJKL3BFZV9ijs64zHqlIE7g - alW8ZsL7JO8UZOqtG+ppkxmJWfLZM7l5Ny0KsIW9RenF9iVCd3fuvSuveJBcEfDozGOD - apWa408ln0vVKbzUeXWqEd8c31rRNtRJ0a3ZG9PX/f7oh5ihpeGzI6tfEyfkptZn2uce - Lz5eaVv7uSm5fWk//ghADhgADogDTWANCCAelIJWMAOjhknDzsDioJhPQjveAH4J/gy+ - hpBCEBFViA2kCjIW2Y/CoQioRjQLmoDuIBMgiyObxZhinpLzkqdRoCnCKX5QEiiXqAKo - NqmjabA0t2hlaLuxXnQYunJ6U/pfDCWMVofID71kCmGWZl5hqWINYVNlx7C/O3ybI5BT - l4uDaw3Xy32fJ+GI71FTXjk+bn4q/k2B78eGBd8KtQk3irwUhdK0eLNEp+SA1Jj0sgxC - lkVOXF5fwUMxVqlMuUdlQ+2IuplGtGaN1oLOMV33EyV63w3EDcOMWk4eMvEwrTNntPCz - 7ILilmzzw+706WZ7CYcCJ1rnKJcfbl7uo57WXm+9DX3a8Lp+Lf46xNZA/aDuc+YhQ2GO - 56cj/CO3omIvMUQXXha7Uh9nfHUknpAIT0pPEUh9ccM0bSrjfBY2uyRXKe9tvkfBzq3r - t/mLX5SY3J24F1yOuZ/917GK2kc6lX1Vp6snarxrF5/5P1+uw9dPNzi86m/UanrUwt56 - 8c1Yu1pHTudit3pPUu9AH3u/3bus990f4IMSQ2c+Rn+6O9z6eXxk+wvdV54xiXGlCe1J - gymjb8bTBjO639Vm5eZE5nEL9IuwxeWlz8ttK49Xb/6IWfNeN/l5fINjE7k5/atzq2I7 - Zcdv13iPjxT/g/PS/pmCQtPPx4+IM9TU2u/+9y6+PkHQOWq/0EJXKryz8UmopYdqU0Cw - uTbUQnkLDLh76ej9xlOuTloGED4M1fUwT01jqKWC/u3SuxN1zCDMBGHOs076JhCmgbC0 - G97SHMKQJkyDEKhB4rBA2MItQPtvu0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAsvI+xIWQn - 6Zd6Bertn1MhXA90oPciEXgANyACDKG9ogVZJvYtf/et9vtef8YPWCLAfd8zGPIMAN5g - CvLxdfC6RIT23AHj4IoDLsAPBAEfiBcEiOLl4jPi2384mtCYD1T/f6+DES/gCjH+p9q+ - nTSXb6V7cJZfqIKVZ/dczdwf1YN7cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noaeFEBDscuz - J6EGvkJS879KoFsIdNYGQNOPEEr08vAMxKlDXxPchHF6eBdRYZykuLg8+BcCxWXcCmVu - ZHN0cmVhbQplbmRvYmoKNjMgMCBvYmoKMzgyNwplbmRvYmoKNTEgMCBvYmoKWyAvSUND - QmFzZWQgNjIgMCBSIF0KZW5kb2JqCjY0IDAgb2JqCjw8IC9MZW5ndGggNjUgMCBSIC9O - IDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 - cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqUiAZS - psyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcOAPDF - BxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp5VeT - ovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYmWphp - QrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfCKRCv - z9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZpAO7w - /mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMSRAze - 50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIpMFez - CrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6hIlLJ - UrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWbe7a9 - dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxIJdjg - nHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJAcne - Kc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl6JLl - u59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/DzmhUud - ej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/rWIf - sq+r/8Y76/cc778MFH1wHuQZ/DqU/9H2E9On3uGrn9U/b45UjLp+Yf3S9TVqTHJsYjx9 - QntiffLOlMU35LfH084zdDMN3wmzuNneuYvzkvMTC1mLhkuwpZpl/Ar/ytfVvB82a8xr - A+uZP203ODbGNkt/EbbktuHbHTsZu057Ynt7v+Nvhxoks8L0UZygrKeWpinHctNlMNAz - xhzaYsazTLBZsLdwKHDexx3mvsbz66g7by+/okCRIEbIV7hHVFIsVXxBUkeqQHpdRk82 - V25GQUYxUqlFhULVSC1JvVOTXEtT+7zOQ91JPRZ9PYMQwxKjfuMdE35TU7MQ85sWry2/ - naKwFrE5aYu3Szr94EyH/bQjyonbWdHFyhXvFuue71Ht2eE1enbVh8yXFS/op0jQ97cm - ugcEBF4Iuhacdi4vpDi0POzR+Zrw5xEvI19deB3VeLHxUmP0q5iXl+uuPI2tiqu4Wnbt - Tnx+QmZiSlJc8oWU4FSf6y43bNKM0zUz5DJFsniymXLIc3Zyl/LGbr7LbymoLSy7lVuU - cDu82PfO6RLDu4qlgvfYyjBl6+UT9/sevPzrfkXOwyuPAiodHhtUyVbzPKF+slkzXtv9 - tPZZ0fOEF0F19vW6L8UamBt2X0297mp83JTdHNni0qr7RqiNpm25vb+jqjO9K7DbvEey - F9s79/ZN3+3+SCj64gOYgS8fngzGDzl8lPqE/jQ4fO9z6IjeKPPo5JfKr5FjeuOM4yMT - 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 - NWpMcmxiPH1Ce2J98s6UxTfkt8fTzjN0Mw3fCbO42d65i/OS8xMLWYuGS7ClmmX8Cv/K - 19W8HzZrzGsD65k/bTc4NsY2S38RtuS24dsdOxm7Tntie3u/42+HGiSzwvRRnKCsp5am - Kcdy02Uw0DPGHNpixrNMsFmwt3AocN7HHea+xvPrqDtvL7+iQJEgRshXuEdUUixVfEFS - R6pAel1GTzZXbkZBRjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09gxDDEqN+4x0TflNT - sxDzmxavLb+dorAWsTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W657vUe3Z4TV6dtWHzJcV - L+inSND3tya6BwQEXgi6Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4sfFSY/SrmJeX6648 - ja2Kq7hadu1OfH5CZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZcshzdnKX8sZuvstv - KagtLLuVW5RwO7zY987pEsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7DK48CKh0eG1TJVvM8 - oX6yWTNe2/209lnR84QXQXX29bovxRqYG3ZfTb3uanzclN0c2eLSqvtGqI2mbbm9v6Oq - M70rsNu8R7IX2zv39k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh873PoiN4o8+jkl8qv - kWN644zjIxN3JwlTslO735qm42YMv2O/v529Pmc2TzffvXBtUXtxd6l6+ewK50rvatQP - 0R8f16LXBdd7fvpv0G9UbOpvTvwK28JuFW1LbzfumO6M7p7dXdsL248/Ai6MUEVaojzR - kWQZmHvkdRQDlLPUcBpGWn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf2Nc54JwUXEw4Lm5e - HqEjokfFeSX4JPhFBQSP8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUjbXZcRoZZZkN2UK5W - Pl2BqGisJKiMUh5RqVVNVfNS19Bg01jRbNcq0g7TMdMVPAE/8VHvkf41AxdDRSNGo3nj - lpOFJqGm5mYi5mjzLxZPLdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9skO7422ncGcrF1FX - MtdRtxr3JA93T2UvRq/Zs6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd+DwoMdjxnGQIMmQg - tDQs9Lx+OHv4XMSLyMQLZ6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76quDVnWtv44sTQhIN - k7iT1pM7U4pSQ6+b3BBIA2lD6ZUZCZnuWerZh7M3ct7nPspLvumbb1AgWEhR+P1WZ9GD - 28nFxDtWJQp3uUpRpbP3+suelxffT3oQ+pdbhdlDtUdilVyP6aqQVRvVC08maz7Xfnja - /6z3ee+LvrqB+uGXkw3Lr/YasU28zcotNq0hb/LamtpXO3m77Lvzer68FeqL6H//Xm6g - cJBuKP4T9XDmiPBo21e/ca6JD1M5067fZefo5lcXPy93rTavNf6s22zYat8ZJsX/IPeR - cgJaBoBMLACnjgJgXgRAbA6U6iyhXFUKgAk1ABbyAG6TDeD2tQDm7g5+5w8oh6AAJaAH - 7IAPSAI1cBI4AH8QA7JBBWgBI2ADxgATgxnBvGHxsPuwbtgKnBmuDHeFJ8CfwL8gqBDy - CE9EJuIN4hdSBOmMzEL2oMhQ6qhwVC1qHX0cHYSuQf8iUyW7TNaFYcI4YR5gtsj1yXPJ - Fyg0KLIolij1KIupYFSOVA3UPNRXqOdozGnqaAVo07FIbCD2G50d3Vt6ffomBlWGOkZl - xvpD6odamYyZPjA7My+wRLDSsBayHWfrYHdl3z2cyyHPMcQZzoXjasH5cbNxt/AEHeE7 - MnQ0iVeHd4/vOX+ogLzA9rFXgnFCZsKcwvMi9aKpYh7iqhJsEhuSg1LPpAuOx8r4y9rL - GcmrKkgpCiodUeZS4VTlVONRF9AQ11TUOqFtrXNWN/JEul6FfofBjBGFsehJC5MI01Kz - AQu0pbyV36m71mO2ODvH07fPzDhIOkY4dbgcdiW4tXrgPMO9hr1VfG7jKf2CCeNEi4A3 - QarBT0NkQ2vOK4a/jjS6MHwRH42Myb+iFDtyNSZeLGE06UaK0XXKGz3pWZke2Uq5THk/ - 80cKO4saip+XvChtLnt3f7YC80jksXX1tZrGZ4gXevVpDZONSs2ZrWvt1p0veo68vda/ - MmA72PDp6Ocro1NjGhO5U8szmrPX50eXjq0QfjxZ39xU3YreaSb9fn7HnwEcBvxACqgD - E+AEAkAsyAOVoB2Mgx0YK+w4zBwWALsBq4YNwrbhPHA9uD88B94EX0JwIAwQ5xHliM9I - OqQ28jyyEjmL4kU5om6iPqHZ0Hbom+ivZPxkeLInGIAxxGRjpsnlyePJv1Acp0ik+Eap - QVlAuUvlQPWKmo86gXqNxoGmg1ae9h6WHZtMh6QLp1un96dfYPBlWGQkMq4fimBCM6Uw - H2YuZ1Fk6WJ1Yv3JlsQuwN542IkDcBRxanPOcl3HKeK+cafzaPGsHyk76sjLwtvPl8J/ - UgAr0H8sW9BJSFhoU/iNSI4oXkxLnEN8U+KDZI1UjvSF4x4yprLKcsLynAr0ihgloLSl - vKmyofpLbVcDpUmjxarNpyOje+LEaT0i9Fa7Y/ja6OtJBJQDjcyCzAssOi23TolYO9pk - 2HafJj+jbR/j0OJE7nzSJdN1zF3UI8Kz+yyP9zmfHij7XSFMEnUCyoKwwSHnxkNNwhrC - pSJKL3BFZV9ijs64zHqlIE7galW8ZsL7JO8UZOqtG+ppkxmJWfLZM7l5Ny0KsIW9RenF - 9iVCd3fuvSuveJBcEfDozGODapWa408ln0vVKbzUeXWqEd8c31rRNtRJ0a3ZG9PX/f7o - h5ihpeGzI6tfEyfkptZn2uceLz5eaVv7uSm5fWk//ghADhgADogDTWANCCAelIJWMAOj - hknDzsDioJhPQjveAH4J/gy+hpBCEBFViA2kCjIW2Y/CoQioRjQLmoDuIBMgiyObxZhi - npLzkqdRoCnCKX5QEiiXqAKoNqmjabA0t2hlaLuxXnQYunJ6U/pfDCWMVofID71kCmGW - Zl5hqWINYVNlx7C/O3ybI5BTl4uDaw3Xy32fJ+GI71FTXjk+bn4q/k2B78eGBd8KtQk3 - irwUhdK0eLNEp+SA1Jj0sgxClkVOXF5fwUMxVqlMuUdlQ+2IuplGtGaN1oLOMV33EyV6 - 3w3EDcOMWk4eMvEwrTNntPCz7ILilmzzw+706WZ7CYcCJ1rnKJcfbl7uo57WXm+9DX3a - 8Lp+Lf46xNZA/aDuc+YhQ2GO56cj/CO3omIvMUQXXha7Uh9nfHUknpAIT0pPEUh9ccM0 - bSrjfBY2uyRXKe9tvkfBzq3rt/mLX5SY3J24F1yOuZ/917GK2kc6lX1Vp6snarxrF5/5 - P1+uw9dPNzi86m/UanrUwt568c1Yu1pHTudit3pPUu9AH3u/3bus990f4IMSQ2c+Rn+6 - O9z6eXxk+wvdV54xiXGlCe1Jgymjb8bTBjO639Vm5eZE5nEL9IuwxeWlz8ttK49Xb/6I - WfNeN/l5fINjE7k5/atzq2I7Zcdv13iPjxT/g/PS/pmCQtPPx4+IM9TU2u/+9y6+PkHQ - OWq/0EJXKryz8UmopYdqU0CwuTbUQnkLDLh76ej9xlOuTloGED4M1fUwT01jqKWC/u3S - uxN1zCDMBGHOs076JhCmgbC0G97SHMKQJkyDEKhB4rBA2MItQPtvu0+Yp8Wp3/xIbz8D - EoekmeDqpvV7DbAsvI+xIWQn6Zd6Bertn1MhXA90oPciEXgANyACDKG9ogVZJvYtf/et - 9vtef8YPWCLAfd8zGPIMAN5gCvLxdfC6RIT23AHj4IoDLsAPBAEfiBcEiOLl4jPi2384 - mtCYD1T/f6+DES/gCjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7cv6zYoO/Z4fWgP9j - /T+KwAv6rrB/noaeFEBDscuzJ6EGvkJS879KoFsIdNYGQNOPEEr08vAMxKlDXxPchHF6 - eBdRYZykuLg8+BcCxWXcCmVuZHN0cmVhbQplbmRvYmoKNjkgMCBvYmoKMzgyNwplbmRv - YmoKMzkgMCBvYmoKWyAvSUNDQmFzZWQgNjggMCBSIF0KZW5kb2JqCjcwIDAgb2JqCjw8 - IC9MZW5ndGggNzEgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIg - L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa1YeThVXdtfZ3TMx5AxHMqQeco8jxkzZyjz - GI7jGEJIUZS5zIQkiXqUiAZSpsyiRIkyRWYiMr370NPzfdf3vdf7z7uua+31W/f63b+1 - 9r7P2vdZGwDsQycCwQcOAPDFBxLNdDVw1ja2OLIhgAK0AAtEAczJJYCgbmJiCFH+TVkf - ADDSUL8ISSvHKnUh7IVp5VeTovJBwbL5f+P0t5mGCE0IAEwYMjB6HGA1EnY+wBYkfC6Q - EAhxPEnYxdPJFcLhEBYmWphpQrgUwjQeB7iWhJ0P8BsSDnbxIPkOAoCmx7t64QEgm4Ow - iqtbgAs0TJrX1TXAxRfCKRCvz9fXD9LHQhUIuBCIkC82AcJHSc8FaqFiC+lKlUM6yf/Y - giGt6jgA+Pz/sfFCHKZpAO7w/mNbNdt/VjCmngB3Kcl9ORiVBgCokb29VT5I8zoAO6l7 - e1tFe3s7twFADAPQ4OMSRAze50ILhHUB8J/6B/f82wMBBQcKMMwOvooMQS2SeWHGKdwo - x6hP0fRjzejaGbQZXzIpMFezCrEVHmblSOHC4CK4F4+4HH3Hp8NfeQwnmCi0LuIo2iwu - LJEkuShtfPyeLFzOTv6hIlLJUrlYZUlNUT1ao1WLSttYJ1G3Qw+tr24QavjQaPIkq4mh - aZhZmfmgJdJK/JSd9SWbe7a9dutn2O2VHZwcLzrdcm5wGXHdcmfxkPI09vI8e8E7w+eB - 72v8B79Zwg6RNoArUCxIJdjgnHWIayg+LOR8VHhcRHJk2oXsqLyL+ZcKo2/F3LpceCU/ - Njcu62rateT4qwnRieFJAcneKc6p1teNb2imyaULZ+AyGbPIsjazv+cM53bl1d98kH+z - IL4w7JZHkeVtjWLRO2wl6JLlu59Km+9VlGWVR933fGDyl2wFx0PEw+lHXZWVjzOqQqtP - P1Gt4a6F1048bXpW/DzmhUudej1X/c7LoYYnr1JfezdqN3E1/WzuaSlpDX9j1sbX9qu9 - qyO/E9+l0k3V/bGnpNf/rWIfsq+r/8Y76/cc778MFH1wHuQZ/DqU/9H2E9On3uGrn9U/ - b45UjLp+Yf3S9TVqTHJsYjx9QntiffLOlMU35LfH084zdDMN3wmzuNneuYvzkvMTC1mL - hkuwpZpl/Ar/ytfVvB82a8xrA+uZP203ODbGNkt/EbbktuHbHTsZu057Ynt7v+Nvhxok - s8L0UZygrKeWpinHctNlMNAzxhzaYsazTLBZsLdwKHDexx3mvsbz66g7by+/okCRIEbI - V7hHVFIsVXxBUkeqQHpdRk82V25GQUYxUqlFhULVSC1JvVOTXEtT+7zOQ91JPRZ9PYMQ - wxKjfuMdE35TU7MQ85sWry2/naKwFrE5aYu3Szr94EyH/bQjyonbWdHFyhXvFuue71Ht - 2eE1enbVh8yXFS/op0jQ97cmugcEBF4Iuhacdi4vpDi0POzR+Zrw5xEvI19deB3VeLHx - UmP0q5iXl+uuPI2tiqu4WnbtTnx+QmZiSlJc8oWU4FSf6y43bNKM0zUz5DJFsniymXLI - c3Zyl/LGbr7LbymoLSy7lVuUcDu82PfO6RLDu4qlgvfYyjBl6+UT9/sevPzrfkXOwyuP - AiodHhtUyVbzPKF+slkzXtv9tPZZ0fOEF0F19vW6L8UamBt2X0297mp83JTdHNni0qr7 - RqiNpm25vb+jqjO9K7DbvEeyF9s79/ZN3+3+SCj64gOYgS8fngzGDzl8lPqE/jQ4fO9z - 6IjeKPPo5JfKr5FjeuOM4yMTdycJU7JTu9+apuNmDL9jv7+dvT5nNk83371wbVF7cXep - evnsCudK72rUD9EfH9ei1wXXe376b9BvVGzqb078CtvCbhVtS2837pjujO6e3V3bC9uP - PwIujFBFWqI80ZFkGZh75HUUA5Sz1HAaRlp+rBKdKb0zQxBj7KFspnLmZyztrENs39jX - OeCcFFxMOC5uXh6hI6JHxXkl+CT4RQUEj/EJcggxCGOEf4nMin4SaxWvlMiTvCzlI212 - XEaGWWZDdlCuVj5dgahorCSojFIeUalVTVXzUtfQYNNY0WzXKtIO0zHTFTwBP/FR75H+ - NQMXQ0UjRqN545aThSahpuZmIuZo8y8WTy3TrPCnTljzWG/bDNg+tLt22u2Msj2z/bJD - u+Ntp3BnKxdRVzLXUbca9yQPd09lL0av2bOvvbN88L5aeFb8gl8TIcvfl6gWwBgwHfg8 - KDHY8ZxkCDJkILQ0LPS8fjh7+FzEi8jEC2eiRKN2L/ZcKogmxKhfpr88fqUqNjbO+qrg - 1Z1rb+OLE0ISDZO4k9aTO1OKUkOvm9wQSANpQ+mVGQmZ7lnq2YezN3Le5z7KS77pm29Q - IFhIUfj9VmfRg9vJxcQ7ViUKd7lKUaWz9/rLnpcX3096EPqXW4XZQ7VHYpVcj+mqkFUb - 1QtPJms+13542v+s93nvi766gfrhl5MNy6/2GrFNvM3KLTatIW/y2praVzt5u+y783q+ - vBXqi+h//15uoHCQbij+E/Vw5ojwaNtXv3GuiQ9TOdOu32Xn6OZXFz8vd602rzX+rNts - 2GrfGSbF/yD3kXICWgaATCwAp44CYF4EQGwOlOosoVxVCoAJNQAW8gBukw3g9rUA5u4O - fucPKIegACWgB+yAD0gCNXASOAB/EAOyQQVoASNgA8YAE4MZwbxh8bD7sG7YCpwZrgx3 - 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 - DnotZGmd4hX05JX1jeHqMvZ8bdmjyRzianw11KUIZWrEOOPJrmX3RbLFN+HnW8v2r+lR - +3z2SU0l17K6eGYp+nw2XA1r/7OrYNKyq/DkjZAuPGuh7lUPqn1qi9oKTT2mtqttahff - jqoD5R3DnJWJC6zbZfUp9mBjmt7KSVdmi+Dfwi+G/6VeYQvXNDT5D024uYxpCd8R3DZw - h5T/w1+zAw3eCmVuZHN0cmVhbQplbmRvYmoKNzMgMCBvYmoKNzkyCmVuZG9iago3IDAg - b2JqClsgL0lDQ0Jhc2VkIDcyIDAgUiBdCmVuZG9iago3NCAwIG9iago8PCAvTGVuZ3Ro - IDc1IDAgUiAvTiAzIC9BbHRlcm5hdGUgL0RldmljZVJHQiAvRmlsdGVyIC9GbGF0ZURl - Y29kZSA+PgpzdHJlYW0KeAGtWHk4VV3bX2d0zMeQMRzKkHnKPI8ZM2co8xiO4xhCSFGU - ucyEJIl6lIgGUqbMokSJMkVmIjK9+9DT833X973X+8+7rmvt9Vv3+t2/tfa+z9r3WRsA - 7EMnAsEHDgDwxQcSzXQ1cNY2tjiyIYACtAALRAHMySWAoG5iYghR/k1ZHwAw0lC/CEkr - xyp1IeyFaeVXk6LyQcGy+X/j9LeZhghNCABMGDIwehxgNRJ2PsAWJHwukBAIcTxJ2MXT - yRXC4RAWJlqYaUK4FMI0Hge4loSdD/AbEg528SD5DgKApse7euEBIJuDsIqrW4ALNEya - 19U1wMUXwikQr8/X1w/Sx0IVCLgQiJAvNgHCR0nPBWqhYgvpSpVDOsn/2IIhreo4APj8 - /7HxQhymaQDu8P5jWzXbf1Ywpp4AdynJfTkYlQYAqJG9vVU+SPM6ADupe3tbRXt7O7cB - QAwD0ODjEkQM3udCC4R1AfCf+gf3/NsDAQUHCjDMDr6KDEEtknlhxincKMeoT9H0Y83o - 2hm0GV8yKTBXswqxFR5m5UjhwuAiuBePuBx9x6fDX3kMJ5gotC7iKNosLiyRJLkobXz8 - nixczk7+oSJSyVK5WGVJTVE9WqNVi0rbWCdRt0MPra9uEGr40GjyJKuJoWmYWZn5oCXS - SvyUnfUlm3u2vXbrZ9jtlR2cHC863XJucBlx3XJn8ZDyNPbyPHvBO8Pnge9r/Ae/WcIO - kTaAK1AsSCXY4Jx1iGsoPizkfFR4XERyZNqF7Ki8i/mXCqNvxdy6XHglPzY3Lutq2rXk - +KsJ0YnhSQHJ3inOqdbXjW9opsmlC2fgMhmzyLI2s7/nDOd25dXffJB/syC+MOyWR5Hl - bY1i0TtsJeiS5bufSpvvVZRllUfd93xg8pdsBcdDxMPpR12VlY8zqkKrTz9RreGuhddO - PG16Vvw85oVLnXo9V/3Oy6GGJ69SX3s3ajdxNf1s7mkpaQ1/Y9bG1/arvasjvxPfpdJN - 1f2xp6TX/61iH7Kvq//GO+v3HO+/DBR9cB7kGfw6lP/R9hPTp97hq5/VP2+OVIy6fmH9 - 0vU1akxybGI8fUJ7Yn3yzpTFN+S3x9POM3QzDd8Js7jZ3rmL85LzEwtZi4ZLsKWaZfwK - /8rX1bwfNmvMawPrmT9tNzg2xjZLfxG25Lbh2x07GbtOe2J7e7/jb4caJLPC9FGcoKyn - lqYpx3LTZTDQM8Yc2mLGs0ywWbC3cChw3scd5r7G8+uoO28vv6JAkSBGyFe4R1RSLFV8 - QVJHqkB6XUZPNlduRkFGMVKpRYVC1UgtSb1Tk1xLU/u8zkPdST0WfT2DEMMSo37jHRN+ - U1OzEPObFq8tv52isBaxOWmLt0s6/eBMh/20I8qJ21nRxcoV7xbrnu9R7dnhNXp21YfM - lxUv6KdI0Pe3JroHBAReCLoWnHYuL6Q4tDzs0fma8OcRLyNfXXgd1Xix8VJj9KuYl5fr - rjyNrYqruFp27U58fkJmYkpSXPKFlOBUn+suN2zSjNM1M+QyRbJ4splyyHN2cpfyxm6+ - y28pqC0su5VblHA7vNj3zukSw7uKpYL32MowZevlE/f7Hrz8635FzsMrjwIqHR4bVMlW - 8zyhfrJZM17b/bT2WdHzhBdBdfb1ui/FGpgbdl9Nve5qfNyU3RzZ4tKq+0aojaZtub2/ - o6ozvSuw27xHshfbO/f2Td/t/kgo+uIDmIEvH54Mxg85fJT6hP40OHzvc+iI3ijz6OSX - yq+RY3rjjOMjE3cnCVOyU7vfmqbjZgy/Y7+/nb0+ZzZPN9+9cG1Re3F3qXr57ArnSu9q - 1A/RHx/XotcF13t++m/Qb1Rs6m9O/Arbwm4VbUtvN+6Y7ozunt1d2wvbjz8CLoxQRVqi - PNGRZBmYe+R1FAOUs9RwGkZafqwSnSm9M0MQY+yhbKZy5mcs7axDbN/Y1zngnBRcTDgu - bl4eoSOiR8V5Jfgk+EUFBI/xCXIIMQhjhH+JzIp+EmsVr5TIk7ws5SNtdlxGhllmQ3ZQ - rlY+XYGoaKwkqIxSHlGpVU1V81LX0GDTWNFs1yrSDtMx0xU8AT/xUe+R/jUDF0NFI0aj - eeOWk4UmoabmZiLmaPMvFk8t06zwp05Y81hv2wzYPrS7dtrtjLI9s/2yQ7vjbadwZysX - UVcy11G3GvckD3dPZS9Gr9mzr72zfPC+WnhW/IJfEyHL35eoFsAYMB34PCgx2PGcZAgy - ZCC0NCz0vH44e/hcxIvIxAtnokSjdi/2XCqIJsSoX6a/PH6lKjY2zvqq4NWda2/jixNC - Eg2TuJPWkztTilJDr5vcEEgDaUPplRkJme5Z6tmHszdy3uc+yku+6ZtvUCBYSFH4/VZn - 0YPbycXEO1YlCne5SlGls/f6y56XF99PehD6l1uF2UO1R2KVXI/pqpBVG9ULTyZrPtd+ - eNr/rPd574u+uoH64ZeTDcuv9hqxTbzNyi02rSFv8tqa2lc7ebvsu/N6vrwV6ovof/9e - bqBwkG4o/hP1cOaI8GjbV79xrokPUznTrt9l5+jmVxc/L3etNq81/qzbbNhq3xkmxf8g - 95FyAloGgEwsAKeOAmBeBEBsDpTqLKFcVQqACTUAFvIAbpMN4Pa1AObuDn7nDyiHoAAl - oAfsgA9IAjVwEjgAfxADskEFaAEjYAPGABODGcG8YfGw+7Bu2AqcGa4Md4UnwJ/AvyCo - EPIIT0Qm4g3iF1IE6YzMQvagyFDqqHBULWodfRwdhK5B/yJTJbtM1oVhwjhhHmC2yPXJ - c8kXKDQosiiWKPUoi6lgVI5UDdQ81Feo52jMaepoBWjTsUhsIPYbnR3dW3p9+iYGVYY6 - RmXG+kPqh1qZjJk+MDszL7BEsNKwFrIdZ+tgd2XfPZzLIc8xxBnOheNqwflxs3G38AQd - 4TsydDSJV4d3j+85f6iAvMD2sVeCcUJmwpzC8yL1oqliHuKqEmwSG5KDUs+kC47HyvjL - 2ssZyasqSCkKKh1R5lLhVOVU41EX0BDXVNQ6oW2tc1Y38kS6XoV+h8GMEYWx6EkLkwjT - UrMBC7SlvJXfqbvWY7Y4O8fTt8/MOEg6Rjh1uBx2Jbi1euA8w72GvVV8buMp/YIJ40SL - gDdBqsFPQ2RDa84rhr+ONLowfBEfjYzJv6IUO3I1Jl4sYTTpRorRdcobPelZmR7ZSrlM - eT/zRwo7ixqKn5e8KG0ue3d/tgLzSOSxdfW1msZniBd69WkNk41KzZmta+3WnS96jry9 - 1r8yYDvY8Ono5yujU2MaE7lTyzOas9fnR5eOrRB+PFnf3FTdit5pJv1+fsefARwG/EAK - qAMT4AQCQCzIA5WgHYyDHRgr7DjMHBYAuwGrhg3CtuE8cD24PzwH3gRfQnAgDBDnEeWI - z0g6pDbyPLISOYviRTmibqI+odnQduib6K9k/GR4sicYgDHEZGOmyeXJ48m/UBynSKT4 - RqlBWUC5S+VA9YqajzqBeo3GgaaDVp72HpYdm0yHpAunW6f3p19g8GVYZCQyrh+KYEIz - pTAfZi5nUWTpYnVi/cmWxC7A3njYiQNwFHFqc85yXccp4r5xp/No8awfKTvqyMvC28+X - wn9SACvQfyxb0ElIWGhT+I1IjiheTEucQ3xT4oNkjVSO9IXjHjKmsspywvKcCvSKGCWg - tKW8qbKh+kttVwOlSaPFqs2nI6N74sRpPSL0Vrtj+Nro60kElAONzILMCyw6LbdOiVg7 - 2mTYdp8mP6NtH+PQ4kTufNIl03XMXdQjwrP7LI/3OZ8eKPtdIUwSdQLKgrDBIefGQ03C - GsKlIkovcEVlX2KOzrjMeqUgTuBqVbxmwvsk7xRk6q0b6mmTGYlZ8tkzuXk3LQqwhb1F - 6cX2JUJ3d+69K694kFwR8OjMY4NqlZrjTyWfS9UpvNR5daoR3xzfWtE21EnRrdkb09f9 - /uiHmKGl4bMjq18TJ+Sm1mfa5x4vPl5pW/u5Kbl9aT/+CEAOGAAOiANNYA0IIB6UglYw - A6OGScPOwOKgmE9CO94Afgn+DL6GkEIQEVWIDaQKMhbZj8KhCKhGNAuagO4gEyCLI5vF - mGKekvOSp1GgKcIpflASKJeoAqg2qaNpsDS3aGVou7FedBi6cnpT+l8MJYxWh8gPvWQK - YZZmXmGpYg1hU2XHsL87fJsjkFOXi4NrDdfLfZ8n4YjvUVNeOT5ufir+TYHvx4YF3wq1 - CTeKvBSF0rR4s0Sn5IDUmPSyDEKWRU5cXl/BQzFWqUy5R2VD7Yi6mUa0Zo3Wgs4xXfcT - JXrfDcQNw4xaTh4y8TCtM2e08LPsguKWbPPD7vTpZnsJhwInWucolx9uXu6jntZeb70N - fdrwun4t/jrE1kD9oO5z5iFDYY7npyP8I7eiYi8xRBdeFrtSH2d8dSSekAhPSk8RSH1x - wzRtKuN8Fja7JFcp722+R8HOreu3+YtflJjcnbgXXI65n/3XsYraRzqVfVWnqydqvGsX - n/k/X67D1083OLzqb9RqetTC3nrxzVi7WkdO52K3ek9S70Afe7/du6z33R/ggxJDZz5G - f7o73Pp5fGT7C91XnjGJcaUJ7UmDKaNvxtMGM7rf1Wbl5kTmcQv0i7DF5aXPy20rj1dv - /ohZ8143+Xl8g2MTuTn9q3OrYjtlx2/XeI+PFP+D89L+mYJC08/Hj4gz1NTa7/73Lr4+ - QdA5ar/QQlcqvLPxSailh2pTQLC5NtRCeQsMuHvp6P3GU65OWgYQPgzV9TBPTWOopYL+ - 7dK7E3XMIMwEYc6zTvomEKaBsLQb3tIcwpAmTIMQqEHisEDYwi1A+2+7T5inxanf/Ehv - PwMSh6SZ4Oqm9XsNsCy8j7EhZCfpl3oF6u2fUyFcD3Sg9yIReAA3IAIMob2iBVkm9i1/ - 9632+15/xg9YIsB93zMY8gwA3mAK8vF18LpEhPbcAePgigMuwA8EAR+IFwSI4uXiM+Lb - fzia0JgPVP9/r4MRL+AKMf6n2r6dNJdvpXtwll+ogpVn91zN3B/Vg3ty/rNig79nh9aA - /2P9P4rAC/qusH+ehp4UQEOxy7MnoQa+QlLzv0qgWwh01gZA048QSvTy8AzEqUNfE9yE - cXp4F1FhnKS4uDz4FwLFZdwKZW5kc3RyZWFtCmVuZG9iago3NSAwIG9iagozODI3CmVu - ZG9iago0NSAwIG9iagpbIC9JQ0NCYXNlZCA3NCAwIFIgXQplbmRvYmoKNzYgMCBvYmoK - PDwgL0xlbmd0aCA3NyAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VSR0IgL0ZpbHRl - ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVh5OFVd219ndMzHkDEcypB5yjyPGTNn - KPMYjuMYQkhRlLnMhCSJepSIBlKmzKJEiTJFZiIyvfvQ0/N91/e91/vPu65r7fVb9/rd - v7X2vs/a91kbAOxDJwLBBw4A8MUHEs10NXDWNrY4siGAArQAC0QBzMklgKBuYmIIUf5N - WR8AMNJQvwhJK8cqdSHshWnlV5Oi8kHBsvl/4/S3mYYITQgATBgyMHocYDUSdj7AFiR8 - LpAQCHE8SdjF08kVwuEQFiZamGlCuBTCNB4HuJaEnQ/wGxIOdvEg+Q4CgKbHu3rhASCb - g7CKq1uACzRMmtfVNcDFF8IpEK/P19cP0sdCFQi4EIiQLzYBwkdJzwVqoWIL6UqVQzrJ - /9iCIa3qOAD4/P+x8UIcpmkA7vD+Y1s1239WMKaeAHcpyX05GJUGAKiRvb1VPkjzOgA7 - qXt7W0V7ezu3AUAMA9Dg4xJEDN7nQguEdQHwn/oH9/zbAwEFBwowzA6+igxBLZJ5YcYp - 3CjHqE/R9GPN6NoZtBlfMikwV7MKsRUeZuVI4cLgIrgXj7gcfcenw195DCeYKLQu4ija - LC4skSS5KG18/J4sXM5O/qEiUslSuVhlSU1RPVqjVYtK21gnUbdDD62vbhBq+NBo8iSr - iaFpmFmZ+aAl0kr8lJ31JZt7tr1262fY7ZUdnBwvOt1ybnAZcd1yZ/GQ8jT28jx7wTvD - 54Hva/wHv1nCDpE2gCtQLEgl2OCcdYhrKD4s5HxUeFxEcmTaheyovIv5lwqjb8Xculx4 - JT82Ny7ratq15PirCdGJ4UkByd4pzqnW141vaKbJpQtn4DIZs8iyNrO/5wznduXV33yQ - f7MgvjDslkeR5W2NYtE7bCXokuW7n0qb71WUZZVH3fd8YPKXbAXHQ8TD6UddlZWPM6pC - q08/Ua3hroXXTjxtelb8POaFS516PVf9zsuhhievUl97N2o3cTX9bO5pKWkNf2PWxtf2 - q72rI78T36XSTdX9saek1/+tYh+yr6v/xjvr9xzvvwwUfXAe5Bn8OpT/0fYT06fe4auf - 1T9vjlSMun5h/dL1NWpMcmxiPH1Ce2J98s6UxTfkt8fTzjN0Mw3fCbO42d65i/OS8xML - WYuGS7ClmmX8Cv/K19W8HzZrzGsD65k/bTc4NsY2S38RtuS24dsdOxm7Tntie3u/42+H - GiSzwvRRnKCsp5amKcdy02Uw0DPGHNpixrNMsFmwt3AocN7HHea+xvPrqDtvL7+iQJEg - RshXuEdUUixVfEFSR6pAel1GTzZXbkZBRjFSqUWFQtVILUm9U5NcS1P7vM5D3Uk9Fn09 - gxDDEqN+4x0TflNTsxDzmxavLb+dorAWsTlpi7dLOv3gTIf9tCPKidtZ0cXKFe8W657v - Ue3Z4TV6dtWHzJcVL+inSND3tya6BwQEXgi6Fpx2Li+kOLQ87NH5mvDnES8jX114HdV4 - sfFSY/SrmJeX6648ja2Kq7hadu1OfH5CZmJKUlzyhZTgVJ/rLjds0ozTNTPkMkWyeLKZ - cshzdnKX8sZuvstvKagtLLuVW5RwO7zY987pEsO7iqWC99jKMGXr5RP3+x68/Ot+Rc7D - K48CKh0eG1TJVvM8oX6yWTNe2/209lnR84QXQXX29bovxRqYG3ZfTb3uanzclN0c2eLS - qvtGqI2mbbm9v6OqM70rsNu8R7IX2zv39k3f7f5IKPriA5iBLx+eDMYPOXyU+oT+NDh8 - 73PoiN4o8+jkl8qvkWN644zjIxN3JwlTslO735qm42YMv2O/v529Pmc2TzffvXBtUXtx - d6l6+ewK50rvatQP0R8f16LXBdd7fvpv0G9UbOpvTvwK28JuFW1LbzfumO6M7p7dXdsL - 248/Ai6MUEVaojzRkWQZmHvkdRQDlLPUcBpGWn6sEp0pvTNDEGPsoWymcuZnLO2sQ2zf - 2Nc54JwUXEw4Lm5eHqEjokfFeSX4JPhFBQSP8QlyCDEIY4R/icyKfhJrFa+UyJO8LOUj - bXZcRoZZZkN2UK5WPl2BqGisJKiMUh5RqVVNVfNS19Bg01jRbNcq0g7TMdMVPAE/8VHv - kf41AxdDRSNGo3njlpOFJqGm5mYi5mjzLxZPLdOs8KdOWPNYb9sM2D60u3ba7YyyPbP9 - skO7422ncGcrF1FXMtdRtxr3JA93T2UvRq/Zs6+9s3zwvlp4VvyCXxMhy9+XqBbAGDAd - +DwoMdjxnGQIMmQgtDQs9Lx+OHv4XMSLyMQLZ6JEo3Yv9lwqiCbEqF+mvzx+pSo2Ns76 - quDVnWtv44sTQhINk7iT1pM7U4pSQ6+b3BBIA2lD6ZUZCZnuWerZh7M3ct7nPspLvumb - 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 - M9TU2u/+9y6+PkHQOWq/0EJXKryz8UmopYdqU0CwuTbUQnkLDLh76ej9xlOuTloGED4M - 1fUwT01jqKWC/u3SuxN1zCDMBGHOs076JhCmgbC0G97SHMKQJkyDEKhB4rBA2MItQPtv - u0+Yp8Wp3/xIbz8DEoekmeDqpvV7DbAsvI+xIWQn6Zd6Bertn1MhXA90oPciEXgANyAC - DKG9ogVZJvYtf/et9vtef8YPWCLAfd8zGPIMAN5gCvLxdfC6RIT23AHj4IoDLsAPBAEf - iBcEiOLl4jPi2384mtCYD1T/f6+DES/gCjH+p9q+nTSXb6V7cJZfqIKVZ/dczdwf1YN7 - cv6zYoO/Z4fWgP9j/T+KwAv6rrB/noaeFEBDscuzJ6EGvkJS879KoFsIdNYGQNOPEEr0 - 8vAMxKlDXxPchHF6eBdRYZykuLg8+BcCxWXcCmVuZHN0cmVhbQplbmRvYmoKNzcgMCBv - YmoKMzgyNwplbmRvYmoKNDggMCBvYmoKWyAvSUNDQmFzZWQgNzYgMCBSIF0KZW5kb2Jq - Cjc4IDAgb2JqCjw8IC9MZW5ndGggNzkgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNl - UkdCIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa1YeThVXdtfZ3TMx5Ax - HMqQeco8jxkzZyjzGI7jGEJIUZS5zIQkiXqUiAZSpsyiRIkyRWYiMr370NPzfdf3vdf7 - z7uua+31W/f63b+19r7P2vdZGwDsQycCwQcOAPDFBxLNdDVw1ja2OLIhgAK0AAtEAczJ - JYCgbmJiCFH+TVkfADDSUL8ISSvHKnUh7IVp5VeTovJBwbL5f+P0t5mGCE0IAEwYMjB6 - HGA1EnY+wBYkfC6QEAhxPEnYxdPJFcLhEBYmWphpQrgUwjQeB7iWhJ0P8BsSDnbxIPkO - AoCmx7t64QEgm4OwiqtbgAs0TJrX1TXAxRfCKRCvz9fXD9LHQhUIuBCIkC82AcJHSc8F - aqFiC+lKlUM6yf/YgiGt6jgA+Pz/sfFCHKZpAO7w/mNbNdt/VjCmngB3Kcl9ORiVBgCo - kb29VT5I8zoAO6l7e1tFe3s7twFADAPQ4OMSRAze50ILhHUB8J/6B/f82wMBBQcKMMwO - vooMQS2SeWHGKdwox6hP0fRjzejaGbQZXzIpMFezCrEVHmblSOHC4CK4F4+4HH3Hp8Nf - eQwnmCi0LuIo2iwuLJEkuShtfPyeLFzOTv6hIlLJUrlYZUlNUT1ao1WLSttYJ1G3Qw+t - r24QavjQaPIkq4mhaZhZmfmgJdJK/JSd9SWbe7a9dutn2O2VHZwcLzrdcm5wGXHdcmfx - kPI09vI8e8E7w+eB72v8B79Zwg6RNoArUCxIJdjgnHWIayg+LOR8VHhcRHJk2oXsqLyL - +ZcKo2/F3LpceCU/Njcu62rateT4qwnRieFJAcneKc6p1teNb2imyaULZ+AyGbPIsjaz - v+cM53bl1d98kH+zIL4w7JZHkeVtjWLRO2wl6JLlu59Km+9VlGWVR933fGDyl2wFx0PE - w+lHXZWVjzOqQqtPP1Gt4a6F1048bXpW/DzmhUudej1X/c7LoYYnr1JfezdqN3E1/Wzu - aSlpDX9j1sbX9qu9qyO/E9+l0k3V/bGnpNf/rWIfsq+r/8Y76/cc778MFH1wHuQZ/DqU - /9H2E9On3uGrn9U/b45UjLp+Yf3S9TVqTHJsYjx9QntiffLOlMU35LfH084zdDMN3wmz - uNneuYvzkvMTC1mLhkuwpZpl/Ar/ytfVvB82a8xrA+uZP203ODbGNkt/EbbktuHbHTsZ - u057Ynt7v+Nvhxoks8L0UZygrKeWpinHctNlMNAzxhzaYsazTLBZsLdwKHDexx3mvsbz - 66g7by+/okCRIEbIV7hHVFIsVXxBUkeqQHpdRk82V25GQUYxUqlFhULVSC1JvVOTXEtT - +7zOQ91JPRZ9PYMQwxKjfuMdE35TU7MQ85sWry2/naKwFrE5aYu3Szr94EyH/bQjyonb - WdHFyhXvFuue71Ht2eE1enbVh8yXFS/op0jQ97cmugcEBF4Iuhacdi4vpDi0POzR+Zrw - 5xEvI19deB3VeLHxUmP0q5iXl+uuPI2tiqu4WnbtTnx+QmZiSlJc8oWU4FSf6y43bNKM - 0zUz5DJFsniymXLIc3Zyl/LGbr7LbymoLSy7lVuUcDu82PfO6RLDu4qlgvfYyjBl6+UT - 9/sevPzrfkXOwyuPAiodHhtUyVbzPKF+slkzXtv9tPZZ0fOEF0F19vW6L8UamBt2X029 - 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 - b2JqCjc5IDAgb2JqCjM4MjcKZW5kb2JqCjU0IDAgb2JqClsgL0lDQ0Jhc2VkIDc4IDAg - UiBdCmVuZG9iago4MCAwIG9iago8PCAvTGVuZ3RoIDgxIDAgUiAvTiAzIC9BbHRlcm5h - dGUgL0RldmljZVJHQiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGtWHk4 - VV3bX2d0zMeQMRzKkHnKPI8ZM2co8xiO4xhCSFGUucyEJIl6lIgGUqbMokSJMkVmIjK9 - +9DT833X973X+8+7rmvt9Vv3+t2/tfa+z9r3WRsA7EMnAsEHDgDwxQcSzXQ1cNY2tjiy - IYACtAALRAHMySWAoG5iYghR/k1ZHwAw0lC/CEkrxyp1IeyFaeVXk6LyQcGy+X/j9LeZ - hghNCABMGDIwehxgNRJ2PsAWJHwukBAIcTxJ2MXTyRXC4RAWJlqYaUK4FMI0Hge4loSd - D/AbEg528SD5DgKApse7euEBIJuDsIqrW4ALNEya19U1wMUXwikQr8/X1w/Sx0IVCLgQ - iJAvNgHCR0nPBWqhYgvpSpVDOsn/2IIhreo4APj8/7HxQhymaQDu8P5jWzXbf1Ywpp4A - dynJfTkYlQYAqJG9vVU+SPM6ADupe3tbRXt7O7cBQAwD0ODjEkQM3udCC4R1AfCf+gf3 - /NsDAQUHCjDMDr6KDEEtknlhxincKMeoT9H0Y83o2hm0GV8yKTBXswqxFR5m5UjhwuAi - uBePuBx9x6fDX3kMJ5gotC7iKNosLiyRJLkobXz8nixczk7+oSJSyVK5WGVJTVE9WqNV - i0rbWCdRt0MPra9uEGr40GjyJKuJoWmYWZn5oCXSSvyUnfUlm3u2vXbrZ9jtlR2cHC86 - 3XJucBlx3XJn8ZDyNPbyPHvBO8Pnge9r/Ae/WcIOkTaAK1AsSCXY4Jx1iGsoPizkfFR4 - XERyZNqF7Ki8i/mXCqNvxdy6XHglPzY3Lutq2rXk+KsJ0YnhSQHJ3inOqdbXjW9opsml - C2fgMhmzyLI2s7/nDOd25dXffJB/syC+MOyWR5HlbY1i0TtsJeiS5bufSpvvVZRllUfd - 93xg8pdsBcdDxMPpR12VlY8zqkKrTz9RreGuhddOPG16Vvw85oVLnXo9V/3Oy6GGJ69S - X3s3ajdxNf1s7mkpaQ1/Y9bG1/arvasjvxPfpdJN1f2xp6TX/61iH7Kvq//GO+v3HO+/ - DBR9cB7kGfw6lP/R9hPTp97hq5/VP2+OVIy6fmH90vU1akxybGI8fUJ7Yn3yzpTFN+S3 - x9POM3QzDd8Js7jZ3rmL85LzEwtZi4ZLsKWaZfwK/8rX1bwfNmvMawPrmT9tNzg2xjZL - fxG25Lbh2x07GbtOe2J7e7/jb4caJLPC9FGcoKynlqYpx3LTZTDQM8Yc2mLGs0ywWbC3 - cChw3scd5r7G8+uoO28vv6JAkSBGyFe4R1RSLFV8QVJHqkB6XUZPNlduRkFGMVKpRYVC - 1UgtSb1Tk1xLU/u8zkPdST0WfT2DEMMSo37jHRN+U1OzEPObFq8tv52isBaxOWmLt0s6 - /eBMh/20I8qJ21nRxcoV7xbrnu9R7dnhNXp21YfMlxUv6KdI0Pe3JroHBAReCLoWnHYu - L6Q4tDzs0fma8OcRLyNfXXgd1Xix8VJj9KuYl5frrjyNrYqruFp27U58fkJmYkpSXPKF - lOBUn+suN2zSjNM1M+QyRbJ4splyyHN2cpfyxm6+y28pqC0su5VblHA7vNj3zukSw7uK - pYL32MowZevlE/f7Hrz8635FzsMrjwIqHR4bVMlW8zyhfrJZM17b/bT2WdHzhBdBdfb1 - ui/FGpgbdl9Nve5qfNyU3RzZ4tKq+0aojaZtub2/o6ozvSuw27xHshfbO/f2Td/t/kgo - +uIDmIEvH54Mxg85fJT6hP40OHzvc+iI3ijz6OSXyq+RY3rjjOMjE3cnCVOyU7vfmqbj - Zgy/Y7+/nb0+ZzZPN9+9cG1Re3F3qXr57ArnSu9q1A/RHx/XotcF13t++m/Qb1Rs6m9O - /Arbwm4VbUtvN+6Y7ozunt1d2wvbjz8CLoxQRVqiPNGRZBmYe+R1FAOUs9RwGkZafqwS - nSm9M0MQY+yhbKZy5mcs7axDbN/Y1zngnBRcTDgubl4eoSOiR8V5Jfgk+EUFBI/xCXII - MQhjhH+JzIp+EmsVr5TIk7ws5SNtdlxGhllmQ3ZQrlY+XYGoaKwkqIxSHlGpVU1V81LX - 0GDTWNFs1yrSDtMx0xU8AT/xUe+R/jUDF0NFI0ajeeOWk4UmoabmZiLmaPMvFk8t06zw - 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 - +YtflJjcnbgXXI65n/3XsYraRzqVfVWnqydqvGsXn/k/X67D1083OLzqb9RqetTC3nrx - zVi7WkdO52K3ek9S70Afe7/du6z33R/ggxJDZz5Gf7o73Pp5fGT7C91XnjGJcaUJ7UmD - KaNvxtMGM7rf1Wbl5kTmcQv0i7DF5aXPy20rj1dv/ohZ8143+Xl8g2MTuTn9q3OrYjtl - x2/XeI+PFP+D89L+mYJC08/Hj4gz1NTa7/73Lr4+QdA5ar/QQlcqvLPxSailh2pTQLC5 - NtRCeQsMuHvp6P3GU65OWgYQPgzV9TBPTWOopYL+7dK7E3XMIMwEYc6zTvomEKaBsLQb - 3tIcwpAmTIMQqEHisEDYwi1A+2+7T5inxanf/EhvPwMSh6SZ4Oqm9XsNsCy8j7EhZCfp - l3oF6u2fUyFcD3Sg9yIReAA3IAIMob2iBVkm9i1/9632+15/xg9YIsB93zMY8gwA3mAK - 8vF18LpEhPbcAePgigMuwA8EAR+IFwSI4uXiM+Lbfzia0JgPVP9/r4MRL+AKMf6n2r6d - NJdvpXtwll+ogpVn91zN3B/Vg3ty/rNig79nh9aA/2P9P4rAC/qusH+ehp4UQEOxy7Mn - oQa+QlLzv0qgWwh01gZA048QSvTy8AzEqUNfE9yEcXp4F1FhnKS4uDz4FwLFZdwKZW5k - c3RyZWFtCmVuZG9iago4MSAwIG9iagozODI3CmVuZG9iago0MiAwIG9iagpbIC9JQ0NC - YXNlZCA4MCAwIFIgXQplbmRvYmoKMzIgMCBvYmoKPDwgL0xlbmd0aCA4MiAwIFIgL0Z1 - bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAxMzY1IF0gL0RvbWFp - bgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRlciAvRmxhdGVEZWNv - ZGUgPj4Kc3RyZWFtCngBtcIHe+FgAADgf1h7hCR27L2JcT/vOq821dZsjRat2cU98nCE - JD6Je5938Sux+DeZWGzGF8nVeTJ+9ER8vhmbJ2I/rMdjP+vfcZz9GP4dw79Io18xNj/x - 6N7IJ37cDzyyHN0f/oiCnkXDq5HwjGJoFjl8GgmRhkNTisFpmOkkHNwdCk6oByYh6uNQ - gHowMKbuHwdJR0H/gQH/iLpvFFgdBnyH+31D2t6h3/sO3ud9p/nm8xzR63mjOvB6iO6B - F7jHPSDve9xbXX0PcLerv95zu/Y6e26wLmfP5Xxl6nh1AX1xOgDaX5xMu0571wHY1nVQ - 7zhsq3ZbB6i1Y9/dtltJbdY2UEvbttmyWXZbLS3Q5pZ1+ZmuxfwM9sliYmo2PQFsmk1E - Y9NM32RsMm6YjFuxhom+EWswrhsxmoa6kTFmqNOsYQbG+hrG2KCv7a0a9GB1VQN9va66 - XtHrjqyt6GnqtBXio07LquZRR1OreeBa/aAlvdeqlzXcq+41q2WNalOtKnONltXo3U4V - esdtSYUSkZKKHEVKbBdRZCtcRMkRuHj8AgLvVRYQclhZAJ6HlYwVeXirUpEHmFMqAEI5 - 5boCyh2aVUDA5VnFOiTP0sxAclZlGYgol2X2puUyDqVpOVEmTW9NyaScS1IyolSSkkpu - T1x8KxH/+Q9vJKIb8Slfi0XrwmvRaV6JhLuFgituL4UC6gL+JasXAj5TPv+Czzv2OZ93 - OI93vnwG+DfvDPBfRln2AAplbmRzdHJlYW0KZW5kb2JqCjgyIDAgb2JqCjU2NAplbmRv - YmoKMzMgMCBvYmoKPDwgL0xlbmd0aCA4MyAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRz - UGVyU2FtcGxlIDggL1NpemUgWyAxMzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBb - IDAgMSAwIDEgMCAxIF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBtcLX - VQIAAATBbrdasygqKAYwI+Ysawt83M0bxVUuMfIPI38x8gcjvzH1CyM/MfIDI98x8g1T - XzHyBSOfMfIJIx8xdYGRDxg5x8h7jLzD1FuMvMHIa4y8wshLTJ1h4xQbL7DxHEvPsPEU - GydYeoKNx9g4xsYRlh5h4yE2HmDpEBv3sXEPSwfYuIuNO9i4jaVb2LiJjRtYuo6Na7jy - f2QmMXoKZW5kc3RyZWFtCmVuZG9iago4MyAwIG9iagoxNjMKZW5kb2JqCjM0IDAgb2Jq - Cjw8IC9MZW5ndGggODQgMCBSIC9GdW5jdGlvblR5cGUgMCAvQml0c1BlclNhbXBsZSA4 - IC9TaXplIFsgMTM2NSBdIC9Eb21haW4KWyAwIDEgXSAvUmFuZ2UgWyAwIDEgMCAxIDAg - MSBdIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AbXC11UCAAAEwW63WrMo - KigGMCPmLGsLfNzNG8VVLjHyDyN/MfIHI78x9QsjPzHyAyPfMfINU18x8gUjnzHyCSMf - MXWBkQ8YOcfIe4y8w9RbjLzByGuMvMLIS0ydYeMUGy+w8RxLz7DxFBsnWHqCjcfYOMbG - EZYeYeMhNh5g6RAb97FxD0sH2LiLjTvYuI2lW9i4iY0bWLqOjWu48n9kJjF6CmVuZHN0 - cmVhbQplbmRvYmoKODQgMCBvYmoKMTYzCmVuZG9iagozNSAwIG9iago8PCAvTGVuZ3Ro - IDg1IDAgUiAvRnVuY3Rpb25UeXBlIDAgL0JpdHNQZXJTYW1wbGUgOCAvU2l6ZSBbIDEz - NjUgXSAvRG9tYWluClsgMCAxIF0gL1JhbmdlIFsgMCAxIDAgMSAwIDEgXSAvRmlsdGVy - IC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAG1wgd74WAAAOB/WHuEJHbsvYlxP+86rzbV - 1myNFq3ZxT3ycIQkPol7n3fxK7H4N5lYbMYXydV5Mn70RHy+GZsnYj+sx2M/699xnP0Y - /h3Dv0ijXzE2P/Ho3sgnftwPPLIc3R/+iIKeRcOrkfCMYmgWOXwaCZGGQ1OKwWmY6SQc - 3B0KTqgHJiHq41CAejAwpu4fB0lHQf+BAf+Ium8UWB0GfIf7fUPa3qHf+w7e532n+ebz - HNHreaM68HqI7oEXuMc9IO973FtdfQ9wt6u/3nO79jp7brAuZ8/lfGXqeHUBfXE6ANpf - nEy7TnvXAdjWdVDvOGyrdlsHqLVj3922W0lt1jZQS9u22bJZdlstLdDmlnX5ma7F/Az2 - yWJiajY9AWyaTURj00zfZGwybpiMW7GGib4RazCuGzGahrqRMWao06xhBsb6GsbYoK/t - rRr0YHVVA329rrpe0euOrK3oaeq0FeKjTsuq5lFHU6t54Fr9oCW916qXNdyr7jWrZY1q - U60qc42W1ejdThV6x21JhRKRkoocRUpsF1FkK1xEyRG4ePwCAu9VFhByWFkAnoeVjBV5 - eKtSkQeYUyoAQjnlugLKHZpVQMDlWcU6JM/SzEByVmUZiCiXZfam5TIOpWk5USZNb03J - pJxLUjKiVJKSSm5PXHwrEf/5D28kohvxKV+LRevCa9FpXomEu4WCK24vhQLqAv4lqxcC - PlM+/4LPO/Y5n3c4j3e+fAb4N+8M8F9GWfYACmVuZHN0cmVhbQplbmRvYmoKODUgMCBv - YmoKNTY0CmVuZG9iagozIDAgb2JqCjw8IC9UeXBlIC9QYWdlcyAvTWVkaWFCb3ggWzAg - MCA4MDMgNzMzXSAvQ291bnQgMSAvS2lkcyBbIDIgMCBSIF0gPj4KZW5kb2JqCjg2IDAg - b2JqCjw8IC9UeXBlIC9DYXRhbG9nIC9QYWdlcyAzIDAgUiAvVmVyc2lvbiAvMS40ID4+ - CmVuZG9iago4NyAwIG9iago8PCAvTGVuZ3RoIDg4IDAgUiAvTGVuZ3RoMSAxMTQ0MCAv - RmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAG9enl8VEW2/6m795JO73unu9Pp - 7nT2hSTEBNLGrEDCJpAgwQQIBAQNi9GoMFFgkKgoIovgcwZ1WAdpQoQOjD7GQRHH5zaK - u0+f6KDPfPTNc5kR0v07dTtEcHz+/MPP5Kb2unVPfc+pc05VFxAAUEMPsBCeu6St84bF - nSux5gUAop/btcJzz6djH8b8BwDs4vmdC5boPvjT8wD8BAClesHi7vnsqNjfAJKbACwv - d7S3zfufU/vnAwTTcYziDqxQporFWG7FclrHkhU3L58m4fvBNVi+efENc9um9U7aguUL - WB67pO3mTukO5T8A0sux7Lm+bUl77e1dv8LyLCyndt6wfAUncBVY7sHy7M5l7Z1/WH19 - PpY/QPpewjqCD/1TY1aQcz8ZJToDMCx2436y64818iCIkkKpUidpIFmr0xuMYDJbrDa7 - w/lj3X/pOtfPGJA/AVr+SUjne8DO5YIbIP4WhrdpGpsW/4Q/BdrYkvj/sGU42AANTKyi - HE7APbADDoIAezCfDrNhG5wmi2CAzIJ+OENSIAdlhoMoTIAXSDz+CsyHx7D/CngaNsMh - xD8dloAJWzcQf/wWLIcxPwfWxB+BNBgNv4YnoRRH3QCD8b3xw9g6BabBPtiP7/+Z+JhD - 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 - kRgUqgi5JyrAWnNXhbVCP1ZXWlP1f0WtcsvFWHZ3fzyyEldkC97RiOxzNeN1GMzEXc0X - u+K54f/nb8WN2KG9MpNa3sNdnYvmy9d7fNXtrXjLJ3JXF1636pnj8Rxa1Dl8dynQOmdu - B71f0tYe6fS1V0UW+ao8h7rk92j1Jc3zaXOXr+oQzK++uunQ/HB7VV9XuKuaXnM6PKdy - Wctl31o/8q1llT/yrUo62DL6rTnyez/4VgttnkO/1UK/1UK/NSc8R/4WhaB64dTK5StQ - OvEKEF7BSZ8aqZ88swlvujVXRckuei/oRvh/mmM40QplbmRzdHJlYW0KZW5kb2JqCjg4 - IDAgb2JqCjc2NTEKZW5kb2JqCjg5IDAgb2JqCjw8IC9UeXBlIC9Gb250RGVzY3JpcHRv - ciAvQXNjZW50IDc3MCAvQ2FwSGVpZ2h0IDcyNyAvRGVzY2VudCAtMjMwIC9GbGFncyAz - MgovRm9udEJCb3ggWy05NTEgLTQ4MSAxNDQ1IDExMjJdIC9Gb250TmFtZSAvWE9CWkRJ - K0hlbHZldGljYSAvSXRhbGljQW5nbGUgMAovU3RlbVYgOTggL01heFdpZHRoIDE1MDAg - L1N0ZW1IIDg1IC9YSGVpZ2h0IDUzMSAvRm9udEZpbGUyIDg3IDAgUiA+PgplbmRvYmoK - OTAgMCBvYmoKWyAyNzggMCAwIDAgMCAwIDAgMCAzMzMgMzMzIDAgMCAwIDMzMyAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMAowIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDU1NiAwIDU1 - NiA1NTYKNTAwIDU1NiA1NTYgMjc4IDU1NiA1NTYgMjIyIDAgNTAwIDIyMiA4MzMgNTU2 - IDU1NiA1NTYgMCAzMzMgNTAwIDI3OCA1NTYgNTAwCjcyMiA1MDAgNTAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAKMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAg - MCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMAowIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAw - IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNTAwIF0KZW5kb2Jq - CjI3IDAgb2JqCjw8IC9UeXBlIC9Gb250IC9TdWJ0eXBlIC9UcnVlVHlwZSAvQmFzZUZv - bnQgL1hPQlpESStIZWx2ZXRpY2EgL0ZvbnREZXNjcmlwdG9yCjg5IDAgUiAvV2lkdGhz - IDkwIDAgUiAvRmlyc3RDaGFyIDMyIC9MYXN0Q2hhciAyMjIgL0VuY29kaW5nIC9NYWNS - b21hbkVuY29kaW5nCj4+CmVuZG9iagoxIDAgb2JqCjw8IC9UaXRsZSAoVW50aXRsZWQp - IC9BdXRob3IgKEFydmlkIE5vcmJlcmcpIC9DcmVhdG9yIChPbW5pR3JhZmZsZSkgL1By - b2R1Y2VyCihNYWMgT1MgWCAxMC41LjcgUXVhcnR6IFBERkNvbnRleHQpIC9DcmVhdGlv - bkRhdGUgKEQ6MjAwOTA1MjUwMjIxMzJaMDAnMDAnKQovTW9kRGF0ZSAoRDoyMDA5MDUy - NTAyMjEzMlowMCcwMCcpID4+CmVuZG9iagp4cmVmCjAgOTEKMDAwMDAwMDAwMCA2NTUz - NSBmIAowMDAwMDkxMzEzIDAwMDAwIG4gCjAwMDAwMDE2MDkgMDAwMDAgbiAKMDAwMDA4 - MjUzMiAwMDAwMCBuIAowMDAwMDAwMDIyIDAwMDAwIG4gCjAwMDAwMDE1ODkgMDAwMDAg - biAKMDAwMDAwMTcxMyAwMDAwMCBuIAowMDAwMDY0MzU4IDAwMDAwIG4gCjAwMDAwMDI2 - MzUgMDAwMDAgbiAKMDAwMDAwMzU5NSAwMDAwMCBuIAowMDAwMDA0NjQ0IDAwMDAwIG4g - CjAwMDAwMDU0NjYgMDAwMDAgbiAKMDAwMDAwNzY3MSAwMDAwMCBuIAowMDAwMDA4MjM4 - IDAwMDAwIG4gCjAwMDAwMDgyNTggMDAwMDAgbiAKMDAwMDAwOTIyMCAwMDAwMCBuIAow - MDAwMDAzNjE0IDAwMDAwIG4gCjAwMDAwMDQ2MjQgMDAwMDAgbiAKMDAwMDAwNjk1NSAw - MDAwMCBuIAowMDAwMDA3NjUxIDAwMDAwIG4gCjAwMDAwMDkyNDAgMDAwMDAgbiAKMDAw - MDAwOTkzNiAwMDAwMCBuIAowMDAwMDA1NDg2IDAwMDAwIG4gCjAwMDAwMDYzNDggMDAw - MDAgbiAKMDAwMDAwNjM2OCAwMDAwMCBuIAowMDAwMDA2OTM1IDAwMDAwIG4gCjAwMDAw - NTU0MzAgMDAwMDAgbiAKMDAwMDA5MTEzOCAwMDAwMCBuIAowMDAwMDAyMDMzIDAwMDAw - IG4gCjAwMDAwMDIxNzggMDAwMDAgbiAKMDAwMDAwMjMzNSAwMDAwMCBuIAowMDAwMDAy - NDkwIDAwMDAwIG4gCjAwMDAwODAzNDYgMDAwMDAgbiAKMDAwMDA4MTA5MyAwMDAwMCBu - IAowMDAwMDgxNDM5IDAwMDAwIG4gCjAwMDAwODE3ODUgMDAwMDAgbiAKMDAwMDA2MzQw - NiAwMDAwMCBuIAowMDAwMDA5OTU2IDAwMDAwIG4gCjAwMDAwMTM2MjYgMDAwMDAgbiAK - MDAwMDA1OTQxOCAwMDAwMCBuIAowMDAwMDMzMTIzIDAwMDAwIG4gCjAwMDAwMzY4ODkg - MDAwMDAgbiAKMDAwMDA4MDMwOSAwMDAwMCBuIAowMDAwMDI5NTc0IDAwMDAwIG4gCjAw - MDAwMzMxMDIgMDAwMDAgbiAKMDAwMDA2ODM0NSAwMDAwMCBuIAowMDAwMDEzNjQ3IDAw - MDAwIG4gCjAwMDAwMTcxNzMgMDAwMDAgbiAKMDAwMDA3MjMzMyAwMDAwMCBuIAowMDAw - MDE3MTk0IDAwMDAwIG4gCjAwMDAwMjE2ODMgMDAwMDAgbiAKMDAwMDA1MDU3NyAwMDAw - MCBuIAowMDAwMDI1Mzk1IDAwMDAwIG4gCjAwMDAwMjk1NTMgMDAwMDAgbiAKMDAwMDA3 - NjMyMSAwMDAwMCBuIAowMDAwMDM2OTEwIDAwMDAwIG4gCjAwMDAwNDI4MDggMDAwMDAg - biAKMDAwMDAyMTcwNCAwMDAwMCBuIAowMDAwMDI1Mzc0IDAwMDAwIG4gCjAwMDAwNTQ1 - NjUgMDAwMDAgbiAKMDAwMDA0MjgyOSAwMDAwMCBuIAowMDAwMDQ2NjA1IDAwMDAwIG4g - CjAwMDAwNDY2MjYgMDAwMDAgbiAKMDAwMDA1MDU1NiAwMDAwMCBuIAowMDAwMDUwNjE0 - IDAwMDAwIG4gCjAwMDAwNTQ1NDQgMDAwMDAgbiAKMDAwMDA1NDYwMiAwMDAwMCBuIAow - MDAwMDU1NDEwIDAwMDAwIG4gCjAwMDAwNTU0NjcgMDAwMDAgbiAKMDAwMDA1OTM5NyAw - MDAwMCBuIAowMDAwMDU5NDU1IDAwMDAwIG4gCjAwMDAwNjMzODUgMDAwMDAgbiAKMDAw - MDA2MzQ0MyAwMDAwMCBuIAowMDAwMDY0MzM4IDAwMDAwIG4gCjAwMDAwNjQzOTQgMDAw - MDAgbiAKMDAwMDA2ODMyNCAwMDAwMCBuIAowMDAwMDY4MzgyIDAwMDAwIG4gCjAwMDAw - NzIzMTIgMDAwMDAgbiAKMDAwMDA3MjM3MCAwMDAwMCBuIAowMDAwMDc2MzAwIDAwMDAw - IG4gCjAwMDAwNzYzNTggMDAwMDAgbiAKMDAwMDA4MDI4OCAwMDAwMCBuIAowMDAwMDgx - MDczIDAwMDAwIG4gCjAwMDAwODE0MTkgMDAwMDAgbiAKMDAwMDA4MTc2NSAwMDAwMCBu - IAowMDAwMDgyNTEyIDAwMDAwIG4gCjAwMDAwODI2MTUgMDAwMDAgbiAKMDAwMDA4MjY3 - OSAwMDAwMCBuIAowMDAwMDkwNDIxIDAwMDAwIG4gCjAwMDAwOTA0NDIgMDAwMDAgbiAK - MDAwMDA5MDY3OCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDkxIC9Sb290IDg2IDAg - UiAvSW5mbyAxIDAgUiAvSUQgWyA8OTE5N2U4MDU2OWI0YjU2N2NlNzdmZWVlZWIyZTE4 - M2Q+Cjw5MTk3ZTgwNTY5YjRiNTY3Y2U3N2ZlZWVlYjJlMTgzZD4gXSA+PgpzdGFydHhy - ZWYKOTE1MjAKJSVFT0YKMSAwIG9iago8PC9BdXRob3IgKEFydmlkIE5vcmJlcmcpL0Ny - ZWF0aW9uRGF0ZSAoRDoyMDA5MDUyNTAyMDAwMFopL0NyZWF0b3IgKE9tbmlHcmFmZmxl - IDUuMS4xKS9Nb2REYXRlIChEOjIwMDkwNTI1MDIyMTAwWikvUHJvZHVjZXIgKE1hYyBP - UyBYIDEwLjUuNyBRdWFydHogUERGQ29udGV4dCkvVGl0bGUgKHdyaXRlX2Rpc2tfYnVm - ZmVycy5ncmFmZmxlKT4+CmVuZG9iagp4cmVmCjEgMQowMDAwMDkzNDk4IDAwMDAwIG4g - CnRyYWlsZXIKPDwvSUQgWzw5MTk3ZTgwNTY5YjRiNTY3Y2U3N2ZlZWVlYjJlMTgzZD4g - PDkxOTdlODA1NjliNGI1NjdjZTc3ZmVlZWViMmUxODNkPl0gL0luZm8gMSAwIFIgL1By - ZXYgOTE1MjAgL1Jvb3QgODYgMCBSIC9TaXplIDkxPj4Kc3RhcnR4cmVmCjkzNzEwCiUl - RU9GCg== - - QuickLookThumbnail - - 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 - AvMEeAXvB2kI4wplC+INVw7TEFIRyhNFFL8WOhe1GSsaoBwZHZAfCCB9Ie8jYyTUJkYn - sikgKo0r9i1gLsUwLDGOMu80TjWpNwI4WzmwOwQ8VT2kPvBAO0GDQsdEDEVORo1Hy0kG - SkFLeUywTeVPGVBMUXpSqlPYVQRWMFdbWIJZqVrQW/ZdG149X2FggWGhYsBj3mT7Zhhn - M2hOaWhqf2uWbK1twm7Xb+tw/XIPcx90L3U+dkt3WHhkeW56eHuAfIh9j36Uf5mAnIGe - gqCDoYShhaCGnoebiJiJlIqPi4mMg418jnOPa5BikViSTpNDlDiVLJYglxSYB5j5meqa - 3JvNnL6drZ6dn4ugeqFnolWjQqQupRqmBqbxp9uoxamvqpirgKxorVCuN68dsAOw6LHN - srKzlrR5tVy2P7chuAK45LnEuqS7hLxjvUG+H779v9nAtsGRwmvDRcQfxPfFz8amx3zI - UskmyfrKzcuezG7NPs4MztrPptBx0TvSBNLN05PUWNUd1eDWotdi2CLY4dme2lrbFtvQ - 3IndQd353q/fZOAZ4M3hgOIy4uTjleRF5PblpeZU5wTns+hi6RHpwOpw6yDr0OyB7TLt - 5O6Y70zwAfC38W7yJ/Lh85z0WfUX9db2l/da+B344vmo+nD7OPwC/Mv9lv5h/y///wAA - AVsC8wRbBdgHVgjSCkgLvg00DqsQIRGWExIUhBX7F24Y5BpUG8YdOh6sIBwhiiL6JGYl - 0Cc7KKIqCCttLM8uMi+RMO4yRjOfNPY2STebOOk6Njt+PMY+Cj9NQI5BzEMJRENFe0aw - R+VJGEpJS3hMpk3TTv1QJ1FRUnhTnVTCVedXClgtWU9abluOXK1dy17oYAVhIWI7Y1Zk - bmWHZp5ntWjLad9q82wHbRluKm87cEpxWHJmc3N0f3WKdpR3nHikeat6sXu3fLt9v37B - f8KAw4HDgsODwYS/hb2GuYe0iK+Jqoqli56Ml42QjoiPf5B2kWySY5NZlE6VQ5Y4ly2Y - IpkWmgma/ZvwnOKd1J7Gn7igqqGbooyjfKRtpVymTKc7qCqpGqoIqvar5KzSrb+urK+a - sIaxc7Jfs0u0N7Uitg22+bfjuM65uLqiu4y8db1evke/L8AXwP7B5sLNw7PEmcV+xmPH - R8gryQ7J8crTy7TMlc10zlPPMtAQ0OzRyNKj037UWNUw1gjW4Ne22IvZX9oz2wbb2Nyp - 3XneSd8X3+XgsuF+4knjFOPe5KflcOY35v7nxOiI6UzqEOrR65LsUu0R7c7uiu9F7/7w - tfFq8h7y0POA9C702vWD9ir2z/dx+BL4r/lK+eT6evsQ+6L8NPzD/VH93/5q/vX/ev// - AAABAwIsAz8EVQVoBn4HlgipCb0K1gvrDQIOFA8nEDwRURJiE3UUhxWZFqsXuhjKGdka - 5xv2HQIeCh8WIB8hJSIrIy4kMCUxJjEnLigqKSYqHSsTLAgs+i3rLtsvxzCyMZwyhTNs - NFI1NjYXNvg32Di4OZU6cjtOPCo9BT3ePrg/kUBpQUJCGkLyQ8pEokV6RlJHKkgCSNpJ - sUqKS2NMO00UTe1Oxk+gUHlRU1ItUwZT4FS5VZRWbldIWCJY/FnWWrBbilxlXT1eF17x - X8pgo2F8YlZjLmQHZN9lt2aOZ2ZoPWkUaetqwmuYbG5tRG4Zbu5vxHCYcW1yQHMUc+h0 - u3WNdmB3MngEeNZ5qHp5e0p8G3zsfbx+jX9dgC6A/oHPgp+DcIRBhRKF44a0h4eIWIkq - if2K0YukjHiNTI4hjvaPzJCikXmSUJMolACU2ZWyloyXZphBmRyZ+JrVm7Gcj51tnkuf - KqAKoOqhzKKto4+kcqVWpjqnH6gFqOup06q7q6Ssjq15rmWvUrBAsS+yH7MRtAS0+LXu - tuW33bjXudO60LvQvNK91r7cv+XA8cH/wxDEJcU9xlnHeMicycTK88wlzV7OnM/g0SvS - fdPV1TjWoNgR2YvbDdyX3izfyeFv4x/k1+aa6GTqN+wR7fPv3fHN88X1w/e9+cD7yv3c - //8AAG5kaW4AAAAAAAAGNgAAlxAAAFckAABTogAAh5oAAChVAAAWqAAAUA0AAFQ5AAJZ - mQACXrgAAWZmAAMBAAACAAAAAgAFAAsAEgAbACYAMgBAAE8AXwBxAIQAmQCvAMYA3gD4 - ARMBLwFNAWsBiwGsAc4B8gIXAj0CZAKMArYC4AMMAzkDaAOXA8gD+gQtBGIEmATPBQcF - QQV8BbgF9gY1BnUGtwb6Bz8HhQfNCBYIYQitCPsJSgmbCe4KQgqYCvALSQukDAEMYAzA - DSINhg3sDlMOvQ8oD5UQBBB0EOcRWxHREkoSxBM/E70UPRS+FUEVxxZOFtcXYhfuGH0Z - DhmgGjUayxtkG/4cmx05HdkefB8gH8YgbyEaIcYidSMmI9kkjiVGJf8muyd5KDko/CnB - KogrUiweLO0tvi6RL2cwPzEaMfgy2DO6NJ81hzZxN144TTk/OjM7KjwjPR8+Hj8eQCJB - KEIwQztESEVYRmpHfkiVSa9Ky0vpTQpOLk9UUH1RqFLWVAZVOVZvV6dY4logW2Bco13p - XzJgfWHLYxxkcGXHZyBofGncaz5so24Lb3Zw43JUc8h1P3a5eDd5t3s7fMJ+TX/cgW6D - BISehjyH3omEiy+M346TkE2SC5PPlZiXZ5k8mxac957eoMyiwKS7pryoxarUrOuvCLEt - s1i1irfDugO8Sb6UwOXDPMWXx/XKWMy8zyPRitPy1lrYwNsk3Ybf5uJC5Jvm8elD65Lt - 3/Ap8nD0tvb5+T/7g/3F//8AAAACAAUACwATABwAJwAzAEEAUABhAHMAhwCcALIAygDj - AP0BGQE1AVMBcwGTAbUB2AH8AiICSQJxApoCxALwAx0DSwN7A6sD3QQRBEUEewSyBOsF - JQVgBZ0F2wYaBlsGnQbhBycHbge2CAAITAiZCOgJOQmLCeAKNQqNCucLQgufC/4MXwzB - DSYNjA31Dl8Oyw85D6kQGxCPEQQRfBH2EnES7hNtE+8UchT2FX0WBhaQFxwXqxg7GM0Z - YRn3Go4bKBvDHGEdAB2iHkUe6x+SIDsg5yGVIkQi9iOqJGAlGCXTJpAnTygQKNMpmSph - Kywr+SzILZoubi9EMB0w+THXMrczmjR/NWc2UTc9OCw5HjoSOwg8ADz7Pfk++D/6QP5C - BUMORBlFJkY2R0hIXElySopLpUzCTeFPA1AnUU1SdVOfVMxV+1ctWGBZllrOXAldRV6E - X8ZhCWJPY5dk4mYuZ31ozmoia3dsz24qb4Zw5XJGc6p1EHZ4d+N5UHrAfDN9qH8fgJmC - F4OWhRmGn4goibSLQ4zVjmuQBJGhk0GU5ZaNmDmZ6ZucnVSfEKDQopWkXqYrp/2p1Kuv - rY+vc7Fds0u1Prc1uTK7M706v0bBV8NuxYvHr8nZzArOQ9CE0s7VJNeE2fDcat7z4Y3k - OOb26crss++z8sn19/ky/IL//wAAAAMACQATACAAMABDAFgAcACLAKgAxwDpAQ0BNAFd - AYgBtQHlAhcCSwKCAroC9QMzA3IDtAP4BD4EhwTTBSAFcAXDBhgGcAbLBygHiAfrCFEI - uQklCZQKBgp8CvQLcAvwDHMM+Q2DDhAOoQ82D84QaREIEasSURL6E6YUVhUJFb8WeBc0 - F/IYtBl4Gj8bCBvUHKMddB5HHx0f9SDQIawijCNtJFElOCYhJwwn+SjqKdwq0SvJLMMt - vy6/L8AwxTHMMtYz4jTxNgM3GDgwOUo6ZzuHPKo9zz74QCNBUkKDQ7dE7kYpR2ZIpknq - SzBMek3GTxVQZ1G9UxRUb1XNVy1Yj1n0W1xcxV4xX59hD2KAY/RlaWbfaFhp0WtMbMlu - R2/GcUZyx3RKdc53U3jZemB76H1xfvuAh4ITg6CFL4a+iE6J3otwjQKOlZAokbyTUZTm - lnuYEJmmmzyc0Z5nn/2hkqMnpLymUKfkqXarCKyZrimvuLFGstK0XLXkt2u477pxu/G9 - br7owF/B08NExLHGG8eByOLKQMuazO/OQc+O0NbSG9Nb1JjVztcC2DHZXdqD26fcx93j - 3vzgEeEi4jHjPORF5UvmTedO6EvpR+pA6zjsLe0g7hLvAe/x8N3xyfKz8530hPVr9lD3 - Nfgd+QT56PrM+6/8kP1v/k7/Jv//AABkZXNjAAAAAAAAAApDb2xvciBMQ0QAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAPAAAADG5iTk8AAAASAAAAxHN2 - U0UAAAAQAAAA1mZpRkkAAAAQAAAA5mRhREsAAAAcAAAA9npoQ04AAAAMAAABEmZyRlIA - AAAWAAABHmphSlAAAAAOAAABNGVuVVMAAAASAAABQnB0QlIAAAAYAAABVGVzRVMAAAAS - AAABbHpoVFcAAAAOAAABfmtvS1IAAAAMAAABjGRlREUAAAAQAAABmG5sTkwAAAAWAAAB - qGl0SVQAAAAUAAABvgBGAGEAcgBnAGUALQBMAEMARABGAOQAcgBnAC0ATABDAEQAVgDk - AHIAaQAtAEwAQwBEAEwAQwBEAC0AZgBhAHIAdgBlAHMAawDmAHIAbV9pgnIAIABMAEMA - RABMAEMARAAgAGMAbwB1AGwAZQB1AHIwqzDpMPwAIABMAEMARABDAG8AbABvAHIAIABM - AEMARABMAEMARAAgAGMAbwBsAG8AcgBpAGQAbwBMAEMARAAgAGMAbwBsAG8Acl9pgnJt - smZ2mG95OlZozuy37AAgAEwAQwBEAEYAYQByAGIALQBMAEMARABLAGwAZQB1AHIAZQBu - AC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAaQAAbW1vZAAAAAAAAAYQAACcXgAAAADA - JqAAAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlLCBJbmMuLCAy - MDA5AA== - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - FitInWindow - - Frame - {{16, 8}, {990, 770}} - ListView - - OutlineWidth - 142 - RightSidebar - - Sidebar - - SidebarWidth - 138 - VisibleRegion - {{-145.785, 1.10725}, {1096.18, 731.893}} - Zoom - 0.90313780307769775 - ZoomValues - - - Canvas 1 - 0.0 - 1 - - - - saveQuickLookFiles - YES - - diff --git a/libtorrent_utp/docs/write_disk_buffers.png b/libtorrent_utp/docs/write_disk_buffers.png deleted file mode 100644 index c3f4637ba0955b86ebc33238fbedf744209d47fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50619 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 z 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®z5Bf$!M+ck?0f(UDi+`jeftm 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 diff --git a/libtorrent_utp/docs/ziptorrent_thumb.gif b/libtorrent_utp/docs/ziptorrent_thumb.gif deleted file mode 100644 index 11f0d99f2745fdeeeb97866459768896f78cb5ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6827 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 I0D$)Y06UXIumAu6 diff --git a/libtorrent_utp/docs/zyxel.png b/libtorrent_utp/docs/zyxel.png deleted file mode 100644 index e8264fe495376b36121301c9f94ba72d10a4e482..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14612 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 KMNUMnLSTZ{W*Qy< diff --git a/libtorrent_utp/examples/Jamfile b/libtorrent_utp/examples/Jamfile deleted file mode 100644 index 2a0ea3b4f..000000000 --- a/libtorrent_utp/examples/Jamfile +++ /dev/null @@ -1,28 +0,0 @@ -import modules ; - -BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; - -use-project /torrent : .. ; - -if $(BOOST_ROOT) -{ - use-project /boost : $(BOOST_ROOT) ; -} - -project client_test - : requirements - multi /torrent//torrent - : default-build - static - ; - -exe client_test : client_test.cpp ; - -exe simple_client : simple_client.cpp ; -exe dump_torrent : dump_torrent.cpp ; -exe make_torrent : make_torrent.cpp ; -exe enum_if : enum_if.cpp ; -exe connection_tester : connection_tester.cpp ; -exe fragmentation_test : fragmentation_test.cpp ; -exe upnp_test : upnp_test.cpp ; - diff --git a/libtorrent_utp/examples/Makefile.am b/libtorrent_utp/examples/Makefile.am deleted file mode 100644 index 0c422e523..000000000 --- a/libtorrent_utp/examples/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -example_programs = \ - client_test \ - dump_torrent \ - enum_if \ - make_torrent \ - simple_client \ - utp_test - -if ENABLE_EXAMPLES -bin_PROGRAMS = $(example_programs) -endif - -EXTRA_PROGRAMS = $(example_programs) -EXTRA_DIST = Jamfile - -client_test_SOURCES = client_test.cpp -#client_test_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -dump_torrent_SOURCES = dump_torrent.cpp -#dump_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -make_torrent_SOURCES = make_torrent.cpp -#make_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -simple_client_SOURCES = simple_client.cpp -#simple_client_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -enum_if_SOURCES = enum_if.cpp -#enum_if_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - -AM_CPPFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ - -#AM_LDFLAGS = ${LDLAGS} -L./ @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ -#AM_LDFLAGS = $(LDFLAGS) @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @OPENSSL_LDFLAGS@ @OPENSSL_LIBS@ -#AM_LDFLAGS = @OPENSSL_LDFLAGS@ diff --git a/libtorrent_utp/examples/client_test.cpp b/libtorrent_utp/examples/client_test.cpp deleted file mode 100644 index 1355ec196..000000000 --- a/libtorrent_utp/examples/client_test.cpp +++ /dev/null @@ -1,1839 +0,0 @@ -/* - -Copyright (c) 2003, 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" - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/extensions/metadata_transfer.hpp" -#include "libtorrent/extensions/ut_metadata.hpp" -#include "libtorrent/extensions/ut_pex.hpp" -#include "libtorrent/extensions/smart_ban.hpp" - -#include "libtorrent/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/identify_client.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/ip_filter.hpp" -#include "libtorrent/magnet_uri.hpp" -#include "libtorrent/bitfield.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/peer_info.hpp" -#include "libtorrent/socket_io.hpp" // print_address -#include "libtorrent/time.hpp" - -using boost::bind; - -#ifdef _WIN32 - -#if defined(_MSC_VER) -# define for if (false) {} else for -#endif - -#include -#include - -bool sleep_and_input(char* c, int sleep) -{ - for (int i = 0; i < sleep * 2; ++i) - { - if (_kbhit()) - { - *c = _getch(); - return true; - } - Sleep(500); - } - return false; -}; - -void clear_home() -{ - CONSOLE_SCREEN_BUFFER_INFO si; - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleScreenBufferInfo(h, &si); - COORD c = {0, 0}; - DWORD n; - FillConsoleOutputCharacter(h, ' ', si.dwSize.X * si.dwSize.Y, c, &n); - SetConsoleCursorPosition(h, c); -} - -#else - -#include -#include - -#include -#include -#include - -#define ANSI_TERMINAL_COLORS - -struct set_keypress -{ - set_keypress() - { - termios new_settings; - tcgetattr(0,&stored_settings); - new_settings = stored_settings; - // Disable canonical mode, and set buffer size to 1 byte - new_settings.c_lflag &= (~ICANON); - new_settings.c_cc[VTIME] = 0; - new_settings.c_cc[VMIN] = 1; - tcsetattr(0,TCSANOW,&new_settings); - } - ~set_keypress() { tcsetattr(0,TCSANOW,&stored_settings); } - termios stored_settings; -}; - -bool sleep_and_input(char* c, int sleep) -{ - // sets the terminal to single-character mode - // and resets when destructed - set_keypress s; - libtorrent::ptime start = libtorrent::time_now_hires(); - int ret = 0; -retry: - fd_set set; - FD_ZERO(&set); - FD_SET(0, &set); - timeval tv = {sleep, 0}; - ret = select(1, &set, 0, 0, &tv); - if (ret > 0) - { - *c = getc(stdin); - return true; - } - if (errno == EINTR) - { - if (total_milliseconds(libtorrent::time_now_hires() - start) < sleep * 1000) - goto retry; - return false; - } - - if (ret < 0 && errno != 0 && errno != ETIMEDOUT) - fprintf(stderr, "select failed: %s\n", strerror(errno)); - - libtorrent::sleep(500); - return false; -} - -void clear_home() -{ - puts("\033[2J\033[0;0H"); -} - -#endif - -bool print_trackers = false; -bool print_peers = false; -bool print_log = false; -bool print_downloads = false; -bool print_piece_bar = false; -bool print_file_progress = false; -bool show_pad_files = false; -bool show_dht_status = false; -bool sequential_download = false; -bool print_utp_stats = false; - -bool print_ip = true; -bool print_as = false; -bool print_timers = false; -bool print_block = false; -bool print_peer_rate = false; -bool print_fails = false; -bool print_send_bufs = true; - -enum { - torrents_all = 0, - torrents_downloading = 1, - torrents_not_paused = 2, - torrents_seeding = 3, - torrents_paused = 4 -}; - -int torrent_filter = torrents_not_paused; - -struct torrent_entry -{ - torrent_entry(libtorrent::torrent_handle h) : handle(h) {} - libtorrent::torrent_handle handle; - libtorrent::torrent_status status; -}; - -typedef std::multimap handles_t; - -bool show_torrent(torrent_entry const& te) -{ - using libtorrent::torrent_status; - torrent_status const& st = te.status; - - switch (torrent_filter) - { - case torrents_all: return true; - case torrents_downloading: - return !st.paused - && st.state != torrent_status::seeding - && st.state != torrent_status::finished; - case torrents_not_paused: return !st.paused; - case torrents_seeding: - return !st.paused - && (st.state == torrent_status::seeding - || st.state == torrent_status::finished); - case torrents_paused: return st.paused; - } - return true; -} - -FILE* g_log_file = 0; - -int active_torrent = 0; - -char const* esc(char const* code) -{ -#ifdef ANSI_TERMINAL_COLORS - // 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; -#else - return ""; -#endif -} - -std::string to_string(int v, int width) -{ - char buf[100]; - snprintf(buf, sizeof(buf), "%*d", width, v); - return buf; -} - -std::string& to_string(float v, int width, int precision = 3) -{ - // this is a silly optimization - // to avoid copying of strings - enum { num_strings = 20 }; - static std::string buf[num_strings]; - static int round_robin = 0; - std::string& ret = buf[round_robin]; - ++round_robin; - if (round_robin >= num_strings) round_robin = 0; - ret.resize(20); - int size = snprintf(&ret[0], 20, "%*.*f", width, precision, v); - ret.resize((std::min)(size, width)); - return ret; -} - -std::string add_suffix(float val, char const* suffix = 0) -{ - std::string ret; - if (val == 0) - { - ret.resize(4 + 2, ' '); - if (suffix) ret.resize(4 + 2 + strlen(suffix), ' '); - return ret; - } - - const char* prefix[] = {"kB", "MB", "GB", "TB"}; - const int num_prefix = sizeof(prefix) / sizeof(const char*); - for (int i = 0; i < num_prefix; ++i) - { - val /= 1000.f; - if (std::fabs(val) < 1000.f) - { - ret = to_string(val, 4); - ret += prefix[i]; - if (suffix) ret += suffix; - return ret; - } - } - ret = to_string(val, 4); - ret += "PB"; - if (suffix) ret += suffix; - return ret; -} - -std::string const& piece_bar(libtorrent::bitfield const& p, int width) -{ -#ifdef ANSI_TERMINAL_COLORS - static const char* lookup[] = - { - // black, blue, cyan, white - "40", "44", "46", "47" - }; - - const int table_size = sizeof(lookup) / sizeof(lookup[0]); -#else - static const char char_lookup[] = - { ' ', '.', ':', '-', '+', '*', '#'}; - - const int table_size = sizeof(char_lookup) / sizeof(char_lookup[0]); -#endif - - double piece_per_char = p.size() / double(width); - static std::string bar; - bar.clear(); - bar.reserve(width * 6); - bar += "["; - if (p.size() == 0) - { - for (int i = 0; i < width; ++i) bar += ' '; - bar += "]"; - return bar; - } - - // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character - double piece = 0; - for (int i = 0; i < width; ++i, piece += piece_per_char) - { - int num_pieces = 0; - int num_have = 0; - int end = (std::max)(int(piece + piece_per_char), int(piece) + 1); - for (int k = int(piece); k < end; ++k, ++num_pieces) - if (p[k]) ++num_have; - int color = int(std::ceil(num_have / float(num_pieces) * (table_size - 1))); -#ifdef ANSI_TERMINAL_COLORS - bar += esc(lookup[color]); - bar += " "; -#else - bar += char_lookup[color]; -#endif - } -#ifdef ANSI_TERMINAL_COLORS - bar += esc("0"); -#endif - bar += "]"; - return bar; -} - -std::string const& progress_bar(int progress, int width, char const* code = "33") -{ - static std::string bar; - bar.clear(); - bar.reserve(width + 10); - - int progress_chars = (progress * width + 500) / 1000; - bar = esc(code); - std::fill_n(std::back_inserter(bar), progress_chars, '#'); - std::fill_n(std::back_inserter(bar), width - progress_chars, '-'); - bar += esc("0"); - return bar; -} - -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_peer_info(std::string& out, std::vector const& peers) -{ - using namespace libtorrent; - if (print_ip) out += "IP "; -#ifndef TORRENT_DISABLE_GEO_IP - if (print_as) out += "AS "; -#endif - out += "down (total | peak ) up (total | peak ) sent-req recv flags source "; - if (print_fails) out += "fail hshf "; - if (print_send_bufs) out += "rq sndb quota rcvb q-bytes "; - if (print_timers) out += "inactive wait timeout q-time "; - out += "disk rtt "; - if (print_block) out += "block-progress "; -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - out += "country "; -#endif - if (print_peer_rate) out += "peer-rate est.rec.rate "; - out += "client \n"; - - char str[500]; - for (std::vector::const_iterator i = peers.begin(); - i != peers.end(); ++i) - { - if (i->flags & (peer_info::handshake | peer_info::connecting | peer_info::queued)) - continue; - - if (print_ip) - { - snprintf(str, sizeof(str), "%-30s %-22s", (print_endpoint(i->ip) + - (i->connection_type == peer_info::bittorrent_utp ? " [uTP]" : "")).c_str() - , print_endpoint(i->local_endpoint).c_str()); - out += str; - } - -#ifndef TORRENT_DISABLE_GEO_IP - if (print_as) - { - error_code ec; - snprintf(str, sizeof(str), "%-42s ", i->inet_as_name.c_str()); - out += str; - } -#endif - - snprintf(str, sizeof(str) - , "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c " - , esc("32"), add_suffix(i->down_speed, "/s").c_str() - , add_suffix(i->total_download).c_str(), add_suffix(i->download_rate_peak, "/s").c_str() - , esc("31"), add_suffix(i->up_speed, "/s").c_str(), add_suffix(i->total_upload).c_str() - , add_suffix(i->upload_rate_peak, "/s").c_str(), esc("0") - - , i->download_queue_length - , i->target_dl_queue_length - , i->upload_queue_length - - , (i->flags & peer_info::interesting)?'I':'.' - , (i->flags & peer_info::choked)?'C':'.' - , (i->flags & peer_info::remote_interested)?'i':'.' - , (i->flags & peer_info::remote_choked)?'c':'.' - , (i->flags & peer_info::supports_extensions)?'e':'.' - , (i->flags & peer_info::local_connection)?'l':'r' - , (i->flags & peer_info::seed)?'s':'.' - , (i->flags & peer_info::on_parole)?'p':'.' - , (i->flags & peer_info::optimistic_unchoke)?'O':'.' - , (i->read_state == peer_info::bw_limit)?'r': - (i->read_state == peer_info::bw_network)?'R':'.' - , (i->write_state == peer_info::bw_limit)?'w': - (i->write_state == peer_info::bw_network)?'W':'.' - , (i->flags & peer_info::snubbed)?'S':'.' - , (i->flags & peer_info::upload_only)?'U':'D' -#ifndef TORRENT_DISABLE_ENCRYPTION - , (i->flags & peer_info::rc4_encrypted)?'E': - (i->flags & peer_info::plaintext_encrypted)?'e':'.' -#else - , '.' -#endif - , (i->flags & peer_info::holepunched)?'h':'.' - - , (i->source & peer_info::tracker)?'T':'_' - , (i->source & peer_info::pex)?'P':'_' - , (i->source & peer_info::dht)?'D':'_' - , (i->source & peer_info::lsd)?'L':'_' - , (i->source & peer_info::resume_data)?'R':'_' - , (i->source & peer_info::incoming)?'I':'_'); - out += str; - - if (print_fails) - { - snprintf(str, sizeof(str), "%3d %3d " - , i->failcount, i->num_hashfails); - out += str; - } - if (print_send_bufs) - { - snprintf(str, sizeof(str), "%2d %6d (%s) %5d %6d (%s) %6d " - , i->requests_in_buffer, i->used_send_buffer, add_suffix(i->send_buffer_size).c_str() - , i->send_quota, i->used_receive_buffer, add_suffix(i->receive_buffer_size).c_str() - , i->queue_bytes); - out += str; - } - if (print_timers) - { - snprintf(str, sizeof(str), "%8d %4d %7d %6d " - , total_seconds(i->last_active) - , total_seconds(i->last_request) - , i->request_timeout - , total_seconds(i->download_queue_time)); - out += str; - } - snprintf(str, sizeof(str), "%s %4d " - , add_suffix(i->pending_disk_bytes).c_str() - , i->rtt); - out += str; - - if (print_block) - { - if (i->downloading_piece_index >= 0) - { - out += progress_bar( - i->downloading_progress * 1000 / i->downloading_total, 14); - } - else - { - out += progress_bar(0, 14); - } - } - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - if (i->country[0] == 0) - { - out += " .."; - } - else - { - snprintf(str, sizeof(str), " %c%c", i->country[0], i->country[1]); - out += str; - } -#endif - if (print_peer_rate) - { - bool unchoked = (i->flags & peer_info::choked) == 0; - - snprintf(str, sizeof(str), " %s %s" - , add_suffix(i->remote_dl_rate, "/s").c_str() - , unchoked ? add_suffix(i->estimated_reciprocation_rate, "/s").c_str() : " "); - out += str; - } - out += " "; - - if (i->flags & peer_info::handshake) - { - out += esc("31"); - out += " waiting for handshake"; - out += esc("0"); - out += "\n"; - } - else if (i->flags & peer_info::connecting) - { - out += esc("31"); - out += " connecting to peer"; - out += esc("0"); - out += "\n"; - } - else if (i->flags & peer_info::queued) - { - out += esc("33"); - out += " queued"; - out += esc("0"); - out += "\n"; - } - else - { - out += " "; - out += i->client; - out += "\n"; - } - } -} - -int listen_port = 6881; -float preferred_ratio = 0.f; -int allocation_mode = libtorrent::storage_mode_sparse; -std::string save_path("."); -int torrent_upload_limit = 0; -int torrent_download_limit = 0; -std::string monitor_dir; -std::string bind_to_interface = ""; -std::string outgoing_interface = ""; -int poll_interval = 5; -int max_connections_per_torrent = 50; - -bool share_mode = false; - -using boost::bind; - -// monitored_dir is true if this torrent is added because -// it was found in the directory that is monitored. If it -// is, it should be remembered so that it can be removed -// if it's no longer in that directory. -void add_torrent(libtorrent::session& ses - , handles_t& handles - , std::string const& torrent - , float preferred_ratio - , int allocation_mode - , std::string const& save_path - , bool monitored_dir - , int torrent_upload_limit - , int torrent_download_limit) -{ - using namespace libtorrent; - - boost::intrusive_ptr t; - error_code ec; - t = new torrent_info(torrent.c_str(), ec); - if (ec) - { - fprintf(stderr, "%s: %s\n", torrent.c_str(), ec.message().c_str()); - return; - } - - printf("%s\n", t->name().c_str()); - - add_torrent_params p; - p.share_mode = share_mode; - lazy_entry resume_data; - - std::string filename = combine_path(save_path, t->name() + ".resume"); - - std::vector buf; - if (load_file(filename.c_str(), buf) == 0) - p.resume_data = &buf; - - p.ti = t; - p.save_path = save_path; - p.storage_mode = (storage_mode_t)allocation_mode; - p.paused = true; - p.duplicate_is_error = false; - p.auto_managed = true; - torrent_handle h = ses.add_torrent(p, ec); - if (ec) - { - fprintf(stderr, "failed to add torrent: %s\n", ec.message().c_str()); - return; - } - - handles.insert(std::pair( - monitored_dir?std::string(torrent):std::string(), h)); - - h.set_max_connections(max_connections_per_torrent); - h.set_max_uploads(-1); - h.set_ratio(preferred_ratio); - h.set_upload_limit(torrent_upload_limit); - h.set_download_limit(torrent_download_limit); - h.use_interface(outgoing_interface.c_str()); -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - h.resolve_countries(true); -#endif -} - -void scan_dir(std::string const& dir_path - , libtorrent::session& ses - , handles_t& handles - , float preferred_ratio - , int allocation_mode - , std::string const& save_path - , int torrent_upload_limit - , int torrent_download_limit) -{ - std::set valid; - - using namespace libtorrent; - - error_code ec; - for (directory i(dir_path, ec); !i.done(); i.next(ec)) - { - std::string file = combine_path(dir_path, i.file()); - if (extension(file) != ".torrent") continue; - - handles_t::iterator k = handles.find(file); - if (k != handles.end()) - { - valid.insert(file); - continue; - } - - // the file has been added to the dir, start - // downloading it. - add_torrent(ses, handles, file, preferred_ratio, allocation_mode - , save_path, true, torrent_upload_limit, torrent_download_limit); - valid.insert(file); - } - - // remove the torrents that are no longer in the directory - - for (handles_t::iterator i = handles.begin(); !handles.empty() && i != handles.end();) - { - if (i->first.empty() || valid.find(i->first) != valid.end()) - { - ++i; - continue; - } - - torrent_handle& h = i->second.handle; - if (!h.is_valid()) - { - handles.erase(i++); - continue; - } - - h.auto_managed(false); - h.pause(); - // the alert handler for save_resume_data_alert - // will save it to disk - if (h.need_save_resume_data()) h.save_resume_data(); - - handles.erase(i++); - } -} - -torrent_entry& get_active_torrent(handles_t const& handles) -{ - if (active_torrent >= handles.size() - || active_torrent < 0) active_torrent = 0; - handles_t::const_iterator i = handles.begin(); - std::advance(i, active_torrent); - return const_cast(i->second); -} - -void print_alert(libtorrent::alert const* a, std::string& str) -{ - using namespace libtorrent; - -#ifdef ANSI_TERMINAL_COLORS - if (a->category() & alert::error_notification) - { - str += esc("31"); - } - else if (a->category() & (alert::peer_notification | alert::storage_notification)) - { - str += esc("33"); - } -#endif - str += "["; - str += time_now_string(); - str += "] "; - str += a->message(); -#ifdef ANSI_TERMINAL_COLORS - str += esc("0"); -#endif - - if (g_log_file) - fprintf(g_log_file, "[%s] %s\n", time_now_string(), a->message().c_str()); -} - -int save_file(std::string const& filename, std::vector& v) -{ - using namespace libtorrent; - - file f; - error_code ec; - if (!f.open(filename, file::write_only, ec)) return -1; - if (ec) return -1; - file::iovec_t b = {&v[0], v.size()}; - size_type written = f.writev(0, &b, 1, ec); - if (written != v.size()) return -3; - if (ec) return -3; - return 0; -} - -void handle_alert(libtorrent::session& ses, libtorrent::alert* a - , handles_t const& handles) -{ - using namespace libtorrent; - - if (torrent_finished_alert* p = alert_cast(a)) - { - p->handle.set_max_connections(max_connections_per_torrent / 2); - - // write resume data for the finished torrent - // the alert handler for save_resume_data_alert - // will save it to disk - torrent_handle h = p->handle; - h.save_resume_data(); - } - else if (save_resume_data_alert* p = alert_cast(a)) - { - torrent_handle h = p->handle; - TORRENT_ASSERT(p->resume_data); - if (p->resume_data) - { - std::vector out; - bencode(std::back_inserter(out), *p->resume_data); - save_file(combine_path(h.save_path(), h.name() + ".resume"), out); - if (std::find_if(handles.begin(), handles.end() - , boost::bind(&torrent_entry::handle, boost::bind(&handles_t::value_type::second, _1)) == h) == handles.end()) - ses.remove_torrent(h); - } - } - else if (save_resume_data_failed_alert* p = alert_cast(a)) - { - torrent_handle h = p->handle; - if (std::find_if(handles.begin(), handles.end() - , boost::bind(&torrent_entry::handle, boost::bind(&handles_t::value_type::second, _1)) == h) == handles.end()) - ses.remove_torrent(h); - } -} - -static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - -int main(int argc, char* argv[]) -{ - if (argc == 1) - { - fprintf(stderr, "usage: client_test [OPTIONS] [TORRENT|MAGNETURL]\n\n" - "OPTIONS:\n" - " -f logs all events to the given file\n" - " -o limits the number of simultaneous\n" - " half-open TCP connections to the\n" - " given number.\n" - " -p sets the listen port\n" - " -r sets the preferred share ratio\n" - " -d limits the download rate\n" - " -u limits the upload rate\n" - " -S limits the upload slots\n" - " -a sets the allocation mode. [compact|full]\n" - " -s sets the save path for downloads\n" - " -U sets per-torrent upload rate\n" - " -D sets per-torrent download rate\n" - " -m sets the .torrent monitor directory\n" - " -b sets IP of the interface to bind the\n" - " listen socket to\n" - " -I sets the IP of the interface to bind\n" - " outgoing peer connections to\n" - " -w sets the retry time for failed web seeds\n" - " -t sets the scan interval of the monitor dir\n" - " -x loads an emule IP-filter file\n" - " -c sets the max number of connections\n" - " -T sets the max number of connections per torrent\n" -#if TORRENT_USE_I2P - " -i the hostname to an I2P SAM bridge to use\n" -#endif - " -C sets the max cache size. Specified in 16kB blocks\n" - " -F sets the UI refresh rate. This is the number of\n" - " seconds between screen refreshes.\n" - " -n announce to trackers in all tiers\n" - " -h allow multiple connections from the same IP\n" - " -A allowed pieces set size\n" - " -R number of blocks per read cache line\n" - " -O Disallow disk job reordering\n" - " -P Use the specified SOCKS5 proxy\n" - " -L Use the specified username and password for the\n" - " proxy specified by -P\n" - " -H Don't start DHT\n" - " -M Disable TCP/uTP bandwidth balancing\n" - " -W Set the max number of peers to keep in the peer list\n" - " -N Do not attempt to use UPnP and NAT-PMP to forward ports\n" - " -Y Rate limit local peers\n" - " -y Disable TCP connections (disable outgoing TCP and reject\n" - " incoming TCP connections)\n" - " -q automatically quit the client after of refreshes\n" - " this is useful for scripting tests\n" - " " - "\n\n" - "TORRENT is a path to a .torrent file\n" - "MAGNETURL is a magnet: url\n") - ; - return 0; - } - - using namespace libtorrent; - session_settings settings; - - settings.user_agent = "client_test/" LIBTORRENT_VERSION; - settings.choking_algorithm = session_settings::auto_expand_choker; - //settings.announce_to_all_trackers = true; - settings.optimize_hashing_for_speed = false; - settings.disk_cache_algorithm = session_settings::largest_contiguous; - settings.volatile_read_cache = false; - - proxy_settings ps; - - int refresh_delay = 1; - bool start_dht = true; - bool start_upnp = true; - int loop_limit = 0; - - std::deque events; - - ptime next_dir_scan = time_now(); - - // the string is the filename of the .torrent file, but only if - // it was added through the directory monitor. It is used to - // be able to remove torrents that were added via the directory - // monitor when they're not in the directory anymore. - handles_t handles; - session ses(fingerprint("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0) - , session::add_default_plugins - , alert::all_categories - & ~(alert::dht_notification - + alert::progress_notification - + alert::debug_notification - + alert::stats_notification)); - - std::vector in; - if (load_file(".ses_state", in) == 0) - { - lazy_entry e; - error_code ec; - if (lazy_bdecode(&in[0], &in[0] + in.size(), e, ec) == 0) - ses.load_state(e); - } - -#ifndef TORRENT_DISABLE_GEO_IP - ses.load_asnum_db("GeoIPASNum.dat"); - ses.load_country_db("GeoIP.dat"); -#endif - - // load the torrents given on the commandline - - std::vector magnet_links; - std::vector torrents; - - for (int i = 1; i < argc; ++i) - { - if (argv[i][0] != '-') - { - // match it against the @ format - if (strlen(argv[i]) > 45 - && is_hex(argv[i], 40) - && string_begins_no_case(argv[i] + 40, "@http")) - { - sha1_hash info_hash; - from_hex(argv[i], 40, (char*)&info_hash[0]); - - add_torrent_params p; - p.share_mode = share_mode; - p.tracker_url = argv[i] + 41; - p.info_hash = info_hash; - p.save_path = save_path; - p.storage_mode = (storage_mode_t)allocation_mode; - p.paused = true; - p.duplicate_is_error = false; - p.auto_managed = true; - magnet_links.push_back(p); - continue; - } - - torrents.push_back(argv[i]); - continue; - } - - // if there's a flag but no argument following, ignore it - if (argc == i) continue; - char const* arg = argv[i+1]; - switch (argv[i][1]) - { - case 'f': g_log_file = fopen(arg, "w+"); break; - case 'o': settings.half_open_limit = atoi(arg); break; - case 'h': settings.allow_multiple_connections_per_ip = true; --i; break; - case 'p': listen_port = atoi(arg); break; - case 'r': - preferred_ratio = atoi(arg); - if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f; - break; - case 'n': settings.announce_to_all_tiers = true; --i; break; - case 'd': settings.download_rate_limit = atoi(arg) * 1000; break; - case 'u': settings.upload_rate_limit = atoi(arg) * 1000; break; - case 'S': settings.unchoke_slots_limit = atoi(arg); break; - case 'a': - if (strcmp(arg, "allocate") == 0) allocation_mode = storage_mode_allocate; - if (strcmp(arg, "compact") == 0) allocation_mode = storage_mode_compact; - break; - case 's': save_path = arg; break; - case 'U': torrent_upload_limit = atoi(arg) * 1000; break; - case 'D': torrent_download_limit = atoi(arg) * 1000; break; - case 'm': monitor_dir = arg; break; - case 'Q': share_mode = true; --i; break; - case 'b': bind_to_interface = arg; break; - case 'w': settings.urlseed_wait_retry = atoi(arg); break; - case 't': poll_interval = atoi(arg); break; - case 'F': refresh_delay = atoi(arg); break; - case 'H': start_dht = false; --i; break; - case 'W': - settings.max_peerlist_size = atoi(arg); - settings.max_paused_peerlist_size = atoi(arg) / 2; - break; - case 'x': - { - FILE* filter = fopen(arg, "r"); - if (filter) - { - ip_filter fil; - unsigned int a,b,c,d,e,f,g,h, flags; - while (fscanf(filter, "%u.%u.%u.%u - %u.%u.%u.%u %u\n", &a, &b, &c, &d, &e, &f, &g, &h, &flags) == 9) - { - address_v4 start((a << 24) + (b << 16) + (c << 8) + d); - address_v4 last((e << 24) + (f << 16) + (g << 8) + h); - if (flags <= 127) flags = ip_filter::blocked; - else flags = 0; - fil.add_rule(start, last, flags); - } - ses.set_ip_filter(fil); - fclose(filter); - } - } - break; - case 'c': settings.connections_limit = atoi(arg); break; - case 'T': max_connections_per_torrent = atoi(arg); break; -#if TORRENT_USE_I2P - case 'i': - { - proxy_settings ps; - ps.hostname = arg; - ps.port = 7656; // default SAM port - ps.type = proxy_settings::i2p_proxy; - ses.set_i2p_proxy(ps); - break; - } -#endif // TORRENT_USE_I2P - case 'C': - settings.cache_size = atoi(arg); - settings.use_read_cache = settings.cache_size > 0; - settings.cache_buffer_chunk_size = settings.cache_size / 100; - break; - case 'A': settings.allowed_fast_set_size = atoi(arg); break; - case 'R': settings.read_cache_line_size = atoi(arg); break; - case 'O': settings.allow_reordered_disk_operations = false; --i; break; - case 'M': settings.mixed_mode_algorithm = session_settings::prefer_tcp; --i; break; - case 'y': settings.enable_outgoing_tcp = false; settings.enable_incoming_tcp = false; --i; break; - case 'P': - { - char* port = (char*) strrchr(arg, ':'); - if (port == 0) - { - fprintf(stderr, "invalid proxy hostname, no port found\n"); - break; - } - *port++ = 0; - ps.hostname = arg; - ps.port = atoi(port); - if (ps.port == 0) { - fprintf(stderr, "invalid proxy port\n"); - break; - } - if (ps.type == proxy_settings::none) - ps.type = proxy_settings::socks5; - } - break; - case 'L': - { - char* pw = (char*) strchr(arg, ':'); - if (pw == 0) - { - fprintf(stderr, "invalid proxy username and password specified\n"); - break; - } - *pw++ = 0; - ps.username = arg; - ps.password = pw; - ps.type = proxy_settings::socks5_pw; - } - break; - case 'I': outgoing_interface = arg; break; - case 'N': start_upnp = false; --i; break; - case 'Y': settings.ignore_limits_on_local_network = false; --i; break; - case 'q': loop_limit = atoi(arg); break; - } - ++i; // skip the argument - } - - ses.start_lsd(); - if (start_upnp) - { - ses.start_upnp(); - ses.start_natpmp(); - } - - ses.set_proxy(ps); - - ses.listen_on(std::make_pair(listen_port, listen_port + 10) - , bind_to_interface.c_str()); - - if (start_dht) - { -#ifndef TORRENT_DISABLE_DHT - settings.use_dht_as_fallback = false; - - ses.add_dht_router(std::make_pair( - std::string("router.bittorrent.com"), 6881)); - ses.add_dht_router(std::make_pair( - std::string("router.utorrent.com"), 6881)); - ses.add_dht_router(std::make_pair( - std::string("router.bitcomet.com"), 6881)); - - ses.start_dht(); - } -#endif - - ses.set_settings(settings); - - for (std::vector::iterator i = magnet_links.begin() - , end(magnet_links.end()); i != end; ++i) - { - error_code ec; - torrent_handle h = ses.add_torrent(*i, ec); - if (ec) - { - fprintf(stderr, "failed to add torrent: %s\n", ec.message().c_str()); - continue; - } - - handles.insert(std::pair(std::string(), h)); - - h.set_max_connections(max_connections_per_torrent); - h.set_max_uploads(-1); - h.set_ratio(preferred_ratio); - h.set_upload_limit(torrent_upload_limit); - h.set_download_limit(torrent_download_limit); - h.use_interface(outgoing_interface.c_str()); - } - - for (std::vector::iterator i = torrents.begin() - , end(torrents.end()); i != end; ++i) - { - // first see if this is a torrentless download - if (std::strstr(i->c_str(), "magnet:") == i->c_str()) - { - add_torrent_params p; - p.share_mode = share_mode; - p.save_path = save_path; - p.storage_mode = (storage_mode_t)allocation_mode; - printf("adding MANGET link: %s\n", i->c_str()); - error_code ec; - torrent_handle h = add_magnet_uri(ses, i->c_str(), p, ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - continue; - } - - handles.insert(std::pair(std::string(), h)); - - h.set_max_connections(max_connections_per_torrent); - h.set_max_uploads(-1); - h.set_ratio(preferred_ratio); - h.set_upload_limit(torrent_upload_limit); - h.set_download_limit(torrent_download_limit); - h.use_interface(outgoing_interface.c_str()); - continue; - } - - // if it's a torrent file, open it as usual - add_torrent(ses, handles, i->c_str(), preferred_ratio - , allocation_mode, save_path, false - , torrent_upload_limit, torrent_download_limit); - } - - // main loop - std::vector peers; - std::vector queue; - - while (loop_limit > 1 || loop_limit == 0) - { - if (loop_limit > 1) --loop_limit; - char c; - while (sleep_and_input(&c, refresh_delay)) - { - if (c == 27) - { - // escape code, read another character -#ifdef _WIN32 - c = _getch(); -#else - c = getc(stdin); -#endif - if (c != '[') break; -#ifdef _WIN32 - c = _getch(); -#else - c = getc(stdin); -#endif - if (c == 68) - { - // arrow left - if (torrent_filter > 0) --torrent_filter; - } - else if (c == 67) - { - // arrow right - if (torrent_filter < torrents_paused) ++torrent_filter; - } - else if (c == 65) - { - // arrow up - int prev = active_torrent; - --active_torrent; - while (active_torrent > 0 && !show_torrent(get_active_torrent(handles))) - --active_torrent; - if (active_torrent < 0) active_torrent = prev; - } - else if (c == 66) - { - // arrow down - int prev = active_torrent; - ++active_torrent; - while (active_torrent < handles.size() && !show_torrent(get_active_torrent(handles))) - ++active_torrent; - if (active_torrent >= handles.size()) active_torrent = prev; - } - } - - if (c == ' ') - { - if (ses.is_paused()) ses.resume(); - else ses.pause(); - } - - if (c == 'm') - { - printf("saving peers for torrents\n"); - - std::vector peers; - for (handles_t::iterator i = handles.begin(); - i != handles.end(); ++i) - { - i->second.handle.get_full_peer_list(peers); - FILE* f = fopen(("peers_" + i->second.handle.name()).c_str(), "w+"); - if (!f) break; - for (std::vector::iterator k = peers.begin() - , end(peers.end()); k != end; ++k) - { - fprintf(f, "%s\t%d\n", print_address(k->ip.address()).c_str() -#ifndef TORRENT_DISABLE_GEO_IP - , ses.as_for_ip(k->ip.address()) -#else - , 0 -#endif - ); - } - } - } - - if (c == 'q') break; - - if (c == 'j' && !handles.empty()) - { - get_active_torrent(handles).handle.force_recheck(); - } - - if (c == 'r' && !handles.empty()) - { - get_active_torrent(handles).handle.force_reannounce(); - } - - if (c == 's' && !handles.empty()) - { - torrent_entry& te = get_active_torrent(handles); - te.handle.set_sequential_download(!te.status.sequential_download); - } - - if (c == 'R') - { - // save resume data for all torrents - for (handles_t::iterator i = handles.begin() - , end(handles.end()); i != end; ++i) - { - if (i->second.status.need_save_resume) - i->second.handle.save_resume_data(); - } - } - - if (c == 'o' && !handles.empty()) - { - torrent_entry& te = get_active_torrent(handles); - int num_pieces = te.handle.get_torrent_info().num_pieces(); - if (num_pieces > 300) num_pieces = 300; - for (int i = 0; i < num_pieces; ++i) - { - te.handle.set_piece_deadline(i, (i+5) * 1000, torrent_handle::alert_when_available); - } - } - - if (c == 'v' && !handles.empty()) - { - torrent_entry& te = get_active_torrent(handles); - te.handle.scrape_tracker(); - } - - if (c == 'p' && !handles.empty()) - { - torrent_entry& te = get_active_torrent(handles); - if (!te.status.auto_managed && te.status.paused) - { - te.handle.auto_managed(true); - } - else - { - te.handle.auto_managed(false); - te.handle.pause(torrent_handle::graceful_pause); - } - // the alert handler for save_resume_data_alert - // will save it to disk - if (te.status.need_save_resume) te.handle.save_resume_data(); - } - - if (c == 'c' && !handles.empty()) - { - torrent_entry& te = get_active_torrent(handles); - te.handle.clear_error(); - } - - // toggle displays - if (c == 't') print_trackers = !print_trackers; - if (c == 'i') print_peers = !print_peers; - if (c == 'l') print_log = !print_log; - if (c == 'd') print_downloads = !print_downloads; - if (c == 'f') print_file_progress = !print_file_progress; - if (c == 'h') show_pad_files = !show_pad_files; - if (c == 'a') print_piece_bar = !print_piece_bar; - if (c == 'g') show_dht_status = !show_dht_status; - if (c == 'u') print_utp_stats = !print_utp_stats; - // toggle columns - if (c == '1') print_ip = !print_ip; - if (c == '2') print_as = !print_as; - if (c == '3') print_timers = !print_timers; - if (c == '4') print_block = !print_block; - if (c == '5') print_peer_rate = !print_peer_rate; - if (c == '6') print_fails = !print_fails; - if (c == '7') print_send_bufs = !print_send_bufs; - } - if (c == 'q') break; - - int terminal_width = 80; - -#ifndef _WIN32 - { - winsize size; - ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size); - terminal_width = size.ws_col; - - if (terminal_width < 64) - terminal_width = 64; - } -#endif - - // loop through the alert queue to see if anything has happened. - std::auto_ptr a; - a = ses.pop_alert(); - std::string now = time_now_string(); - while (a.get()) - { - std::string event_string; - - ::print_alert(a.get(), event_string); - ::handle_alert(ses, a.get(), handles); - - events.push_back(event_string); - if (events.size() >= 20) events.pop_front(); - - a = ses.pop_alert(); - } - - session_status sess_stat = ses.status(); - - std::string out; - out = "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused " - "[a] toggle piece bar [s] toggle download sequential [f] toggle files " - "[j] force recheck [space] toggle session pause [c] clear error [v] scrape [g] show DHT\n" - "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress " - "[5] toggle peer rate [6] toggle failures [7] toggle send buffers [R] save resume data\n"; - - char const* filter_names[] = { "all", "downloading", "non-paused", "seeding", "paused"}; - for (int i = 0; i < sizeof(filter_names)/sizeof(filter_names[0]); ++i) - { - out += '['; - if (torrent_filter == i) out += esc("7"); - out += filter_names[i]; - if (torrent_filter == i) out += esc("0"); - out += ']'; - } - out += '\n'; - - char str[500]; - int torrent_index = 0; - torrent_handle active_handle; - for (handles_t::iterator i = handles.begin(); - i != handles.end(); ++torrent_index) - { - torrent_entry& te = i->second; - if (!te.handle.is_valid()) - { - handles.erase(i++); - continue; - } - else - { - ++i; - } - - te.status = te.handle.status(); - torrent_status const& s = te.status; - - if (!show_torrent(te)) - continue; - -#ifdef ANSI_TERMINAL_COLORS - char const* term = "\x1b[0m"; -#else - char const* term = ""; -#endif - if (active_torrent == torrent_index) - { - term = "\x1b[0m\x1b[7m"; - out += esc("7"); - out += "*"; - } - else - { - out += " "; - } - - int queue_pos = te.status.queue_position; - if (queue_pos == -1) out += "- "; - else - { - snprintf(str, sizeof(str), "%-3d", queue_pos); - out += str; - } - - if (s.paused) out += esc("34"); - else out += esc("37"); - - std::string name = te.handle.name(); - if (name.size() > 40) name.resize(40); - snprintf(str, sizeof(str), "%-40s %s ", name.c_str(), term); - out += str; - - if (!s.error.empty()) - { - out += esc("31"); - out += "error "; - out += s.error; - out += esc("0"); - out += "\n"; - continue; - } - - int seeds = 0; - int downloaders = 0; - - if (s.num_complete >= 0) seeds = s.num_complete; - else seeds = s.list_seeds; - - if (s.num_incomplete >= 0) downloaders = s.num_incomplete; - else downloaders = s.list_peers - s.list_seeds; - - if (s.state != torrent_status::queued_for_checking && s.state != torrent_status::checking_files) - { - snprintf(str, sizeof(str), "%-13s down: (%s%s%s) up: %s%s%s (%s%s%s) swarm: %4d:%4d" - " bw queue: (%d|%d) all-time (Rx: %s%s%s Tx: %s%s%s) seed rank: %x %c%s\n" - , (s.paused && !s.auto_managed)?"paused":(s.paused && s.auto_managed)?"queued":state_str[s.state] - , esc("32"), add_suffix(s.total_download).c_str(), term - , esc("31"), add_suffix(s.upload_rate, "/s").c_str(), term - , esc("31"), add_suffix(s.total_upload).c_str(), term - , downloaders, seeds - , s.up_bandwidth_queue, s.down_bandwidth_queue - , esc("32"), add_suffix(s.all_time_download).c_str(), term - , esc("31"), add_suffix(s.all_time_upload).c_str(), term - , s.seed_rank, te.status.need_save_resume?'S':' ', esc("0")); - out += str; - - if (torrent_index != active_torrent && s.state == torrent_status::seeding) continue; - char const* progress_bar_color = "33"; // yellow - if (s.state == torrent_status::downloading_metadata) - { - progress_bar_color = "35"; // magenta - } - else if (s.current_tracker.empty()) - { - progress_bar_color = "31"; // red - } - else if (sess_stat.has_incoming_connections) - { - progress_bar_color = "32"; // green - } - - snprintf(str, sizeof(str), " %-10s: %s%-11"PRId64"%s Bytes %6.2f%% %s\n" - , s.sequential_download?"sequential":"progress" - , esc("32"), s.total_done, esc("0") - , s.progress_ppm / 10000.f - , progress_bar(s.progress_ppm / 1000, terminal_width - 43, progress_bar_color).c_str()); - out += str; - } - else - { - snprintf(str, sizeof(str), "%-13s %s\n" - , state_str[s.state] - , progress_bar(s.progress_ppm / 1000, terminal_width - 43 - 20, "35").c_str()); - out += str; - } - - if (print_piece_bar && s.state != torrent_status::seeding) - { - out += " "; - out += piece_bar(s.pieces, terminal_width - 7); - out += "\n"; - } - - if (s.state != torrent_status::queued_for_checking && s.state != torrent_status::checking_files) - { - boost::posix_time::time_duration t = s.next_announce; - snprintf(str, sizeof(str) - , " peers: %s%d%s (%s%d%s) seeds: %s%d%s distributed copies: %s%4.2f%s " - "sparse regions: %d download: %s%s%s next announce: %s%02d:%02d:%02d%s " - "tracker: %s%s%s\n" - , esc("37"), s.num_peers, esc("0") - , esc("37"), s.connect_candidates, esc("0") - , esc("37"), s.num_seeds, esc("0") - , esc("37"), s.distributed_copies, esc("0") - , s.sparse_regions - , esc("32"), add_suffix(s.download_rate, "/s").c_str(), esc("0") - , esc("37"), t.hours(), t.minutes(), t.seconds(), esc("0") - , esc("36"), s.current_tracker.c_str(), esc("0")); - out += str; - } - - if (torrent_index != active_torrent) continue; - active_handle = te.handle; - } - - cache_status cs = ses.get_cache_status(); - if (cs.blocks_read < 1) cs.blocks_read = 1; - if (cs.blocks_written < 1) cs.blocks_written = 1; - - snprintf(str, sizeof(str), "==== conns: %d down: %s%s%s (%s%s%s) up: %s%s%s (%s%s%s) " - "tcp/ip: %s%s%s %s%s%s DHT: %s%s%s %s%s%s tracker: %s%s%s %s%s%s ====\n" - , sess_stat.num_peers - , esc("32"), add_suffix(sess_stat.download_rate, "/s").c_str(), esc("0") - , esc("32"), add_suffix(sess_stat.total_download).c_str(), esc("0") - , esc("31"), add_suffix(sess_stat.upload_rate, "/s").c_str(), esc("0") - , esc("31"), add_suffix(sess_stat.total_upload).c_str(), esc("0") - , esc("32"), add_suffix(sess_stat.ip_overhead_download_rate, "/s").c_str(), esc("0") - , esc("31"), add_suffix(sess_stat.ip_overhead_upload_rate, "/s").c_str(), esc("0") - , esc("32"), add_suffix(sess_stat.dht_download_rate, "/s").c_str(), esc("0") - , esc("31"), add_suffix(sess_stat.dht_upload_rate, "/s").c_str(), esc("0") - , esc("32"), add_suffix(sess_stat.tracker_download_rate, "/s").c_str(), esc("0") - , esc("31"), add_suffix(sess_stat.tracker_upload_rate, "/s").c_str(), esc("0")); - out += str; - - snprintf(str, sizeof(str), "==== waste: %s fail: %s unchoked: %d / %d " - "bw queues: %8d (%d) | %8d (%d) cache: w: %"PRId64"%% r: %"PRId64"%% size: %s (%s) / %s dq: %"PRId64" ===\n" - , add_suffix(sess_stat.total_redundant_bytes).c_str() - , add_suffix(sess_stat.total_failed_bytes).c_str() - , sess_stat.num_unchoked, sess_stat.allowed_upload_slots - , sess_stat.up_bandwidth_bytes_queue - , sess_stat.up_bandwidth_queue - , sess_stat.down_bandwidth_bytes_queue - , sess_stat.down_bandwidth_queue - , (cs.blocks_written - cs.writes) * 100 / cs.blocks_written - , cs.blocks_read_hit * 100 / cs.blocks_read - , add_suffix(cs.cache_size * 16 * 1024).c_str() - , add_suffix(cs.read_cache_size * 16 * 1024).c_str() - , add_suffix(cs.total_used_buffers * 16 * 1024).c_str() - , cs.queued_bytes); - out += str; - - snprintf(str, sizeof(str), "==== optimistic unchoke: %d unchoke counter: %d peerlist: %d ====\n" - , sess_stat.optimistic_unchoke_counter, sess_stat.unchoke_counter, sess_stat.peerlist_size); - out += str; - -#ifndef TORRENT_DISABLE_DHT - if (show_dht_status) - { - snprintf(str, sizeof(str), "DHT nodes: %d DHT cached nodes: %d total DHT size: %"PRId64" total observers: %d\n" - , sess_stat.dht_nodes, sess_stat.dht_node_cache, sess_stat.dht_global_nodes - , sess_stat.dht_total_allocations); - out += str; - - for (std::vector::iterator i = sess_stat.active_requests.begin() - , end(sess_stat.active_requests.end()); i != end; ++i) - { - snprintf(str, sizeof(str) - , " %s in flight: %d [limit: %d] timeouts %d responses %d left %d\n" - , i->type, i->outstanding_requests, i->branch_factor, i->timeouts - , i->responses, i->nodes_left); - out += str; - } - } -#endif - - if (print_utp_stats) - { - snprintf(str, sizeof(str), "uTP idle: %d syn: %d est: %d fin: %d wait: %d\n" - , sess_stat.utp_stats.num_idle, sess_stat.utp_stats.num_syn_sent - , sess_stat.utp_stats.num_connected, sess_stat.utp_stats.num_fin_sent - , sess_stat.utp_stats.num_close_wait); - out += str; - } - - if (active_handle.is_valid()) - { - torrent_handle h = active_handle; - torrent_status s = h.status(); - - if ((print_downloads && s.state != torrent_status::seeding) - || print_peers) - h.get_peer_info(peers); - - out += "====== "; - out += h.name(); - out += " ======\n"; - - if (print_peers && !peers.empty()) - print_peer_info(out, peers); - - if (print_trackers) - { - std::vector tr = h.trackers(); - ptime now = time_now(); - for (std::vector::iterator i = tr.begin() - , end(tr.end()); i != end; ++i) - { - snprintf(str, sizeof(str), "%2d %-55s fails: %-3d (%-3d) %s %s %5d \"%s\" %s\n" - , i->tier, i->url.c_str(), i->fails, i->fail_limit, i->verified?"OK ":"- " - , i->updating?"updating" - :!i->will_announce(now)?"" - :to_string(total_seconds(i->next_announce - now), 8).c_str() - , i->min_announce > now ? total_seconds(i->min_announce - now) : 0 - , i->last_error ? i->last_error.message().c_str() : "" - , i->message.c_str()); - out += str; - } - } - - if (print_downloads) - { - - h.get_download_queue(queue); - std::sort(queue.begin(), queue.end(), boost::bind(&partial_piece_info::piece_index, _1) - < boost::bind(&partial_piece_info::piece_index, _2)); - - std::vector pieces; - ses.get_cache_info(h.info_hash(), pieces); - - for (std::vector::iterator i = queue.begin(); - i != queue.end(); ++i) - { - cached_piece_info* cp = 0; - std::vector::iterator cpi = std::find_if(pieces.begin(), pieces.end() - , boost::bind(&cached_piece_info::piece, _1) == i->piece_index); - if (cpi != pieces.end()) cp = &*cpi; - - snprintf(str, sizeof(str), "%5d: [", i->piece_index); - out += str; - for (int j = 0; j < i->blocks_in_piece; ++j) - { - int index = peer_index(i->blocks[j].peer(), peers) % 36; - char chr = '+'; - if (index >= 0) - chr = (index < 10)?'0' + index:'A' + index - 10; - - char const* color = ""; - -#ifdef ANSI_TERMINAL_COLORS - if (cp && cp->blocks[j]) color = esc("36;7"); - else if (i->blocks[j].bytes_progress > 0 - && i->blocks[j].state == block_info::requested) - { - if (i->blocks[j].num_peers > 1) color = esc("1;7"); - else color = esc("33;7"); - chr = '0' + (i->blocks[j].bytes_progress / float(i->blocks[j].block_size) * 10); - } - else if (i->blocks[j].state == block_info::finished) color = esc("32;7"); - else if (i->blocks[j].state == block_info::writing) color = esc("35;7"); - else if (i->blocks[j].state == block_info::requested) color = esc("0"); - else { color = esc("0"); chr = ' '; } -#else - if (cp && cp->blocks[j]) chr = 'c'; - else if (i->blocks[j].state == block_info::finished) chr = '#'; - else if (i->blocks[j].state == block_info::writing) chr = '+'; - else if (i->blocks[j].state == block_info::requested) chr = '-'; - else chr = ' '; -#endif - snprintf(str, sizeof(str), "%s%c", color, chr); - out += str; - } -#ifdef ANSI_TERMINAL_COLORS - out += esc("0"); -#endif - char const* piece_state[4] = {"", " slow", " medium", " fast"}; - snprintf(str, sizeof(str), "]%s", piece_state[i->piece_state]); - out += str; - if (cp) - { - snprintf(str, sizeof(str), " %scache age: %-.1f" - , i->piece_state > 0?"| ":"" - , total_milliseconds(time_now() - cp->last_use) / 1000.f); - out += str; - } - out += "\n"; - } - - for (std::vector::iterator i = pieces.begin() - , end(pieces.end()); i != end; ++i) - { - if (i->kind != cached_piece_info::read_cache) continue; - snprintf(str, sizeof(str), "%5d: [", i->piece); - out += str; - for (std::vector::iterator k = i->blocks.begin() - , end(i->blocks.end()); k != end; ++k) - { - char const* color = ""; - char chr = ' '; -#ifdef ANSI_TERMINAL_COLORS - color = *k?esc("33;7"):esc("0"); -#else - chr = *k?'#':' '; -#endif - snprintf(str, sizeof(str), "%s%c", color, chr); - out += str; - } -#ifdef ANSI_TERMINAL_COLORS - out += esc("0"); -#endif - snprintf(str, sizeof(str), "] cache age: %-.1f\n" - , total_milliseconds(time_now() - i->last_use) / 1000.f); - out += str; - } - out += "___________________________________\n"; - } - - if (print_file_progress - && s.state != torrent_status::seeding - && s.has_metadata) - { - std::vector file_progress; - h.file_progress(file_progress); - torrent_info const& info = h.get_torrent_info(); - for (int i = 0; i < info.num_files(); ++i) - { - bool pad_file = info.file_at(i).pad_file; - if (!show_pad_files && pad_file) continue; - int progress = info.file_at(i).size > 0 - ?file_progress[i] * 1000 / info.file_at(i).size:1000; - - char const* color = (file_progress[i] == info.file_at(i).size) - ?"32":"33"; - - snprintf(str, sizeof(str), "%s %s %-5.2f%% %s %s%s\n", - progress_bar(progress, 100, color).c_str() - , pad_file?esc("34"):"" - , progress / 10.f - , add_suffix(file_progress[i]).c_str() - , filename(info.files().file_path(info.file_at(i))).c_str() - , pad_file?esc("0"):""); - out += str; - } - - out += "___________________________________\n"; - } - - } - - if (print_log) - { - for (std::deque::iterator i = events.begin(); - i != events.end(); ++i) - { - out += "\n"; - out += *i; - } - } - - clear_home(); - puts(out.c_str()); - - if (!monitor_dir.empty() - && next_dir_scan < time_now()) - { - scan_dir(monitor_dir, ses, handles, preferred_ratio - , allocation_mode, save_path, torrent_upload_limit - , torrent_download_limit); - next_dir_scan = time_now() + seconds(poll_interval); - } - } - - // keep track of the number of resume data - // alerts to wait for - int num_resume_data = 0; - ses.pause(); - for (handles_t::iterator i = handles.begin(); - i != handles.end(); ++i) - { - torrent_entry& te = i->second; - if (!te.handle.is_valid()) continue; - te.status = te.handle.status(); - if (te.status.paused) continue; - if (!te.status.has_metadata) continue; - - printf("saving resume data for %s\n", te.handle.name().c_str()); - // save_resume_data will generate an alert when it's done - te.handle.save_resume_data(); - ++num_resume_data; - } - printf("waiting for resume data\n"); - - while (num_resume_data > 0) - { - alert const* a = ses.wait_for_alert(seconds(30)); - if (a == 0) - { - printf(" aborting with %d outstanding " - "torrents to save resume data for\n", num_resume_data); - break; - } - - std::auto_ptr holder = ses.pop_alert(); - - std::string log; - ::print_alert(holder.get(), log); - printf("%s\n", log.c_str()); - - if (alert_cast(a)) - { - --num_resume_data; - continue; - } - - save_resume_data_alert const* rd = alert_cast(a); - if (!rd) continue; - --num_resume_data; - - if (!rd->resume_data) continue; - - torrent_handle h = rd->handle; - std::vector out; - bencode(std::back_inserter(out), *rd->resume_data); - save_file(combine_path(h.save_path(), h.name() + ".resume"), out); - } - printf("saving session state\n"); - { - entry session_state; - ses.save_state(session_state); - - std::vector out; - bencode(std::back_inserter(out), session_state); - save_file(".ses_state", out); - } - - printf("closing session"); - - return 0; -} - diff --git a/libtorrent_utp/examples/connection_tester.cpp b/libtorrent_utp/examples/connection_tester.cpp deleted file mode 100644 index 34ec70054..000000000 --- a/libtorrent_utp/examples/connection_tester.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/peer_id.hpp" -#include "libtorrent/io_service.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/address.hpp" -#include "libtorrent/error_code.hpp" -#include "libtorrent/io.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/thread.hpp" -#include -#include -#include - -using namespace libtorrent; -using namespace libtorrent::detail; // for write_* and read_* - -struct peer_conn -{ - peer_conn(io_service& ios, int num_pieces, int blocks_pp, tcp::endpoint const& ep - , char const* ih) - : s(ios) - , read_pos(0) - , state(handshaking) - // don't request anything from the last piece - // to keep things simple - , pieces(num_pieces - 1) - , block(0) - , blocks_per_piece(blocks_pp) - , info_hash(ih) - , outstanding_requests(0) - { - // build a list of all pieces and request them all! - for (int i = 0; i < pieces.size(); ++i) - pieces[i] = i; - std::random_shuffle(pieces.begin(), pieces.end()); - - s.async_connect(ep, boost::bind(&peer_conn::on_connect, this, _1)); - } - - stream_socket s; - char buffer[17*1024]; - int read_pos; - - enum state_t - { - handshaking, - sending_request, - receiving_message - }; - int state; - std::vector pieces; - int block; - int blocks_per_piece; - char const* info_hash; - int outstanding_requests; - - void on_connect(error_code const& ec) - { - if (ec) - { - fprintf(stderr, "ERROR CONNECT: %s\n", ec.message().c_str()); - return; - } - - char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" - " " // space for info-hash - "aaaaaaaaaaaaaaaaaaaa" // peer-id - "\0\0\0\x01\x02"; // interested - char* h = (char*)malloc(sizeof(handshake)); - memcpy(h, handshake, sizeof(handshake)); - std::memcpy(h + 28, info_hash, 20); - std::generate(h + 48, h + 68, &rand); - boost::asio::async_write(s, libtorrent::asio::buffer(h, sizeof(handshake) - 1) - , boost::bind(&peer_conn::on_handshake, this, h, _1, _2)); - } - - void on_handshake(char* h, error_code const& ec, size_t bytes_transferred) - { - free(h); - if (ec) - { - fprintf(stderr, "ERROR SEND HANDSHAKE: %s\n", ec.message().c_str()); - return; - } - - // read handshake - boost::asio::async_read(s, libtorrent::asio::buffer(buffer, 68) - , boost::bind(&peer_conn::on_handshake2, this, _1, _2)); - } - - void on_handshake2(error_code const& ec, size_t bytes_transferred) - { - if (ec) - { - fprintf(stderr, "ERROR READ HANDSHAKE: %s\n", ec.message().c_str()); - return; - } - - work(); - } - - void write_request() - { - if (pieces.empty()) return; - - int piece = pieces.back(); - - char msg[] = "\0\0\0\xd\x06" - " " // piece - " " // offset - " "; // length - char* m = (char*)malloc(sizeof(msg)); - memcpy(m, msg, sizeof(msg)); - char* ptr = m + 5; - write_uint32(piece, ptr); - write_uint32(block * 16 * 1024, ptr); - write_uint32(16 * 1024, ptr); - error_code ec; - boost::asio::async_write(s, libtorrent::asio::buffer(m, sizeof(msg) - 1) - , boost::bind(&peer_conn::on_req_sent, this, m, _1, _2)); - - ++block; - if (block == blocks_per_piece) - { - block = 0; - pieces.pop_back(); - } - } - - void on_req_sent(char* m, error_code const& ec, size_t bytes_transferred) - { - free(m); - if (ec) - { - fprintf(stderr, "ERROR SEND REQUEST: %s\n", ec.message().c_str()); - return; - } - - ++outstanding_requests; - - work(); - } - - void work() - { - if (pieces.empty() && outstanding_requests == 0) - { - fprintf(stderr, "COMPLETED DOWNLOAD\n"); - return; - } - - // send requests - if (outstanding_requests < 20 && !pieces.empty()) - { - write_request(); - return; - } - - // read message - boost::asio::async_read(s, asio::buffer(buffer, 4) - , boost::bind(&peer_conn::on_msg_length, this, _1, _2)); - } - - void on_msg_length(error_code const& ec, size_t bytes_transferred) - { - if (ec) - { - fprintf(stderr, "ERROR RECEIVE MESSAGE PREFIX: %s\n", ec.message().c_str()); - return; - } - char* ptr = buffer; - unsigned int length = read_uint32(ptr); - if (length > sizeof(buffer)) - { - fprintf(stderr, "ERROR RECEIVE MESSAGE PREFIX: packet too big\n"); - return; - } - boost::asio::async_read(s, asio::buffer(buffer, length) - , boost::bind(&peer_conn::on_message, this, _1, _2)); - } - - void on_message(error_code const& ec, size_t bytes_transferred) - { - if (ec) - { - fprintf(stderr, "ERROR RECEIVE MESSAGE: %s\n", ec.message().c_str()); - return; - } - char* ptr = buffer; - int msg = read_uint8(ptr); - if (msg == 7) --outstanding_requests; - - work(); - } -}; - -int main(int argc, char const* argv[]) -{ - if (argc < 5) - { - fprintf(stderr, "usage: connection_tester number-of-connections destination-ip destination-port torrent-file\n"); - return 1; - } - int num_connections = atoi(argv[1]); - address_v4 addr = address_v4::from_string(argv[2]); - int port = atoi(argv[3]); - tcp::endpoint ep(addr, port); - error_code ec; - torrent_info ti(argv[4], ec); - if (ec) - { - fprintf(stderr, "ERROR LOADING .TORRENT: %s\n", ec.message().c_str()); - return 1; - } - std::list conns; - io_service ios; - for (int i = 0; i < num_connections; ++i) - { - conns.push_back(new peer_conn(ios, ti.num_pieces(), ti.piece_length() / 16 / 1024 - , ep, (char const*)&ti.info_hash()[0])); - libtorrent::sleep(1); - ios.poll_one(); - } - - ios.run(); - - return 0; -} - - diff --git a/libtorrent_utp/examples/dump_torrent.cpp b/libtorrent_utp/examples/dump_torrent.cpp deleted file mode 100644 index 4be08af1a..000000000 --- a/libtorrent_utp/examples/dump_torrent.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - -Copyright (c) 2003, 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/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/lazy_entry.hpp" -#include "libtorrent/magnet_uri.hpp" - -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - - if (argc < 2 || argc > 4) - { - fputs("usage: dump_torrent torrent-file [total-items-limit] [recursion-limit]\n", stderr); - return 1; - } - - int item_limit = 100000; - int depth_limit = 1000; - - if (argc > 2) item_limit = atoi(argv[2]); - if (argc > 3) depth_limit = atoi(argv[3]); - - int size = file_size(argv[1]); - if (size > 10 * 1000000) - { - fprintf(stderr, "file too big (%d), aborting\n", size); - return 1; - } - std::vector buf(size); - int ret = load_file(argv[1], buf); - if (ret != 0) - { - fprintf(stderr, "failed to load file: %d\n", ret); - return 1; - } - lazy_entry e; - error_code ec; - int pos; - printf("decoding. recursion limit: %d total item count limit: %d\n" - , depth_limit, item_limit); - ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec, &pos - , depth_limit, item_limit); - - if (ret != 0) - { - fprintf(stderr, "failed to decode: '%s' at character: %d\n", ec.message().c_str(), pos); - return 1; - } - - printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str()); - - torrent_info t(e, ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - e.clear(); - std::vector().swap(buf); - - // print info about torrent - printf("\n\n----- torrent file info -----\n\n" - "nodes:\n"); - - typedef std::vector > node_vec; - node_vec const& nodes = t.nodes(); - for (node_vec::const_iterator i = nodes.begin(), end(nodes.end()); - i != end; ++i) - { - printf("%s: %d\n", i->first.c_str(), i->second); - } - puts("trackers:\n"); - for (std::vector::const_iterator i = t.trackers().begin(); - i != t.trackers().end(); ++i) - { - printf("%2d: %s\n", i->tier, i->url.c_str()); - } - - char ih[41]; - to_hex((char const*)&t.info_hash()[0], 20, ih); - printf("number of pieces: %d\n" - "piece length: %d\n" - "info hash: %s\n" - "comment: %s\n" - "created by: %s\n" - "magnet link: %s\n" - "name: %s\n" - "number of files: %d\n" - "files:\n" - , t.num_pieces() - , t.piece_length() - , ih - , t.comment().c_str() - , t.creator().c_str() - , make_magnet_uri(t).c_str() - , t.name().c_str() - , t.num_files()); - int index = 0; - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); ++i, ++index) - { - int first = t.map_file(index, 0, 0).piece; - int last = t.map_file(index, (std::max)(size_type(i->size)-1, size_type(0)), 0).piece; - printf(" %11"PRId64" %c%c%c%c [ %4d, %4d ] %7u %s %s %s%s\n" - , i->size - , (i->pad_file?'p':'-') - , (i->executable_attribute?'x':'-') - , (i->hidden_attribute?'h':'-') - , (i->symlink_attribute?'l':'-') - , first, last - , boost::uint32_t(t.files().mtime(*i)) - , t.files().hash(*i) != sha1_hash(0) ? to_hex(t.files().hash(*i).to_string()).c_str() : "" - , t.files().file_path(*i).c_str() - , i->symlink_attribute ? "-> ": "" - , i->symlink_attribute && i->symlink_index != -1 ? t.files().symlink(*i).c_str() : ""); - } - - return 0; -} - diff --git a/libtorrent_utp/examples/enum_if.cpp b/libtorrent_utp/examples/enum_if.cpp deleted file mode 100644 index 846596591..000000000 --- a/libtorrent_utp/examples/enum_if.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - -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. - -*/ - -#include -#include -#include -#include - -using namespace libtorrent; - -int main() -{ - io_service ios; - error_code ec; - address local = guess_local_address(ios); - printf("Local address: %s\n", local.to_string(ec).c_str()); - - address def_gw = get_default_gateway(ios, ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - - printf("Default gateway: %s\n", def_gw.to_string(ec).c_str()); - - printf("=========== Routes ===========\n"); - std::vector routes = enum_routes(ios, ec); - if (ec) - { - printf("%s\n", ec.message().c_str()); - return 1; - } - - printf("%-18s%-18s%-35s%-7sinterface\n", "destination", "network", "gateway", "mtu"); - - for (std::vector::const_iterator i = routes.begin() - , end(routes.end()); i != end; ++i) - { - printf("%-18s%-18s%-35s%-7d%s\n" - , i->destination.to_string(ec).c_str() - , i->netmask.to_string(ec).c_str() - , i->gateway.to_string(ec).c_str() - , i->mtu - , i->name); - } - - printf("========= Interfaces =========\n"); - - std::vector const& net = enum_net_interfaces(ios, ec); - if (ec) - { - printf("%s\n", ec.message().c_str()); - return 1; - } - - printf("%-35s%-18s%-40s%-8sflags\n", "address", "netmask", "name", "mtu"); - - for (std::vector::const_iterator i = net.begin() - , end(net.end()); i != end; ++i) - { - printf("%-35s%-18s%-40s%-8d%s%s%s\n" - , i->interface_address.to_string(ec).c_str() - , i->netmask.to_string(ec).c_str() - , i->name - , i->mtu - , (is_multicast(i->interface_address)?"multicast ":"") - , (is_local(i->interface_address)?"local ":"") - , (is_loopback(i->interface_address)?"loopback ":"") - ); - } -} - diff --git a/libtorrent_utp/examples/fragmentation_test.cpp b/libtorrent_utp/examples/fragmentation_test.cpp deleted file mode 100644 index 73ca35d3b..000000000 --- a/libtorrent_utp/examples/fragmentation_test.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - -Copyright (c) 2010, 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/storage.hpp" -#include "libtorrent/file_pool.hpp" - -#include - -#include - -using namespace libtorrent; - -int main(int argc, char const* argv[]) -{ - if (argc != 3 && argc != 2) - { - fprintf(stderr, "Usage: fragmentation_test torrent-file file-storage-path\n" - " fragmentation_test file\n\n"); - return 1; - } - - if (argc == 2) - { - error_code ec; - file f(argv[1], file::read_only, ec); - if (ec) - { - fprintf(stderr, "Error opening file %s: %s\n", argv[1], ec.message().c_str()); - return 1; - } - size_type off = f.phys_offset(0); - printf("physical offset of file %s: %" PRId64 "\n", argv[1], off); - return 0; - } - - error_code ec; - boost::intrusive_ptr ti(new torrent_info(argv[1], ec)); - - if (ec) - { - fprintf(stderr, "Error while loading torrent file: %s\n", ec.message().c_str()); - return 1; - } - - file_pool fp; - boost::shared_ptr st(default_storage_constructor(ti->files(), 0, argv[2], fp, std::vector())); - - // the first field is the piece index, the second - // one is the physical location of the piece on disk - std::vector > pieces; - - // make sure all the files are there - std::vector > files = get_filesizes(ti->files(), argv[2]); - for (int i = 0; i < ti->num_files(); ++i) - { - if (ti->file_at(i).size == files[i].first) continue; - fprintf(stderr, "Files for this torrent are missing or incomplete: %s was %"PRId64" bytes, expected %"PRId64" bytes\n" - , ti->files().file_path(ti->file_at(i)).c_str(), files[i].first, ti->file_at(i).size); - return 1; - } - - for (int i = 0; i < ti->num_pieces(); ++i) - { - pieces.push_back(std::make_pair(i, st->physical_offset(i, 0))); - if (pieces.back().second == size_type(i) * ti->piece_length()) - { - // this suggests that the OS doesn't support physical offset - // or that the file doesn't exist, or some other file error - fprintf(stderr, "Your operating system or filesystem " - "does not appear to support querying physical disk offset\n"); - return 1; - } - } - - FILE* f = fopen("fragmentation.log", "w+"); - if (f == 0) - { - fprintf(stderr, "error while opening log file: %s\n", strerror(errno)); - return 1; - } - - for (int i = 0; i < ti->num_pieces(); ++i) - { - fprintf(f, "%d %"PRId64"\n", pieces[i].first, pieces[i].second); - } - - fclose(f); - - f = fopen("fragmentation.gnuplot", "w+"); - if (f == 0) - { - fprintf(stderr, "error while opening gnuplot file: %s\n", strerror(errno)); - return 1; - } - - fprintf(f, - "set term png size 1200,800\n" - "set output \"fragmentation.png\"\n" - "set xrange [*:*]\n" - "set xlabel \"piece\"\n" - "set ylabel \"drive offset\"\n" - "set key box\n" - "set title \"fragmentation for '%s'\"\n" - "set tics nomirror\n" - "plot \"fragmentation.log\" using 1:2 with dots lt rgb \"#e07070\" notitle axis x1y1, x=0\n" - , ti->name().c_str()); - - fclose(f); - - system("gnuplot fragmentation.gnuplot"); -} - - diff --git a/libtorrent_utp/examples/make_torrent.cpp b/libtorrent_utp/examples/make_torrent.cpp deleted file mode 100644 index 4cb939c81..000000000 --- a/libtorrent_utp/examples/make_torrent.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/storage.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/create_torrent.hpp" -#include "libtorrent/file.hpp" - -#include - -using namespace libtorrent; - -// do not include files and folders whose -// name starts with a . -bool file_filter(std::string const& f) -{ - if (filename(f)[0] == '.') return false; - fprintf(stderr, "%s\n", f.c_str()); - return true; -} - -void print_progress(int i, int num) -{ - fprintf(stderr, "\r%d/%d", i+1, num); -} - -void print_usage() -{ - fputs("usage: make_torrent FILE [OPTIONS]\n" - "\n" - "Generates a torrent file from the specified file\n" - "or directory and writes it to standard out\n\n" - "OPTIONS:\n" - "-m generate a merkle hash tree torrent.\n" - " merkle torrents require client support\n" - "-f include sha-1 file hashes in the torrent\n" - " this helps supporting mixing sources from\n" - " other networks\n" - "-w url adds a web seed to the torrent with\n" - " the specified url\n" - "-t url adds the specified tracker to the\n" - " torrent\n" - "-p bytes enables padding files. Files larger\n" - " than bytes will be piece-aligned\n" - "-s bytes specifies a piece size for the torrent\n" - " This has to be a multiple of 16 kiB\n" - "-l Don't follow symlinks, instead encode them as\n" - " links in the torrent file\n" - "-o file specifies the output filename of the torrent file\n" - " If this is not specified, the torrent file is\n" - " printed to the standard out, except on windows\n" - " where the filename defaults to a.torrent\n" - , stderr); -} - -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - - char const* creator_str = "libtorrent"; - - if (argc < 2) - { - print_usage(); - return 1; - } - -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - std::vector web_seeds; - std::vector trackers; - int pad_file_limit = -1; - int piece_size = 0; - int flags = 0; - - std::string outfile; -#ifdef TORRENT_WINDOWS - // don't ever write binary data to the console on windows - // it will just be interpreted as text and corrupted - outfile = "a.torrent"; -#endif - - for (int i = 2; i < argc; ++i) - { - if (argv[i][0] != '-') - { - print_usage(); - return 1; - } - - switch (argv[i][1]) - { - case 'w': - ++i; - web_seeds.push_back(argv[i]); - break; - case 't': - ++i; - trackers.push_back(argv[i]); - break; - case 'p': - ++i; - pad_file_limit = atoi(argv[i]); - flags |= create_torrent::optimize; - break; - case 's': - ++i; - piece_size = atoi(argv[i]); - break; - case 'm': - flags |= create_torrent::merkle; - break; - case 'o': - ++i; - outfile = argv[i]; - break; - case 'f': - flags |= create_torrent::calculate_file_hashes; - break; - case 'l': - flags |= create_torrent::symlinks; - break; - default: - print_usage(); - return 1; - } - } - - file_storage fs; - file_pool fp; - std::string full_path = libtorrent::complete(argv[1]); - - add_files(fs, full_path, file_filter, flags); - if (fs.num_files() == 0) - { - fputs("no files specified.\n", stderr); - return 1; - } - - create_torrent t(fs, piece_size, pad_file_limit, flags); - for (std::vector::iterator i = trackers.begin() - , end(trackers.end()); i != end; ++i) - t.add_tracker(*i); - - for (std::vector::iterator i = web_seeds.begin() - , end(web_seeds.end()); i != end; ++i) - t.add_url_seed(*i); - - error_code ec; - set_piece_hashes(t, parent_path(full_path) - , boost::bind(&print_progress, _1, t.num_pieces()), ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - - fprintf(stderr, "\n"); - t.set_creator(creator_str); - - // create the torrent and print it to stdout - std::vector torrent; - bencode(back_inserter(torrent), t.generate()); - FILE* output = stdout; - if (!outfile.empty()) - output = fopen(outfile.c_str(), "wb+"); - fwrite(&torrent[0], 1, torrent.size(), output); - - if (output != stdout) - fclose(output); - -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - fprintf(stderr, "%s\n", e.what()); - } -#endif - - return 0; -} - diff --git a/libtorrent_utp/examples/simple_client.cpp b/libtorrent_utp/examples/simple_client.cpp deleted file mode 100644 index 4f52c7a7d..000000000 --- a/libtorrent_utp/examples/simple_client.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - -Copyright (c) 2003, 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/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/session.hpp" - -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - - if (argc != 2) - { - fputs("usage: ./simple_client torrent-file\n" - "to stop the client, press return.\n", stderr); - return 1; - } - - session s; - s.listen_on(std::make_pair(6881, 6889)); - add_torrent_params p; - p.save_path = "./"; - error_code ec; - p.ti = new torrent_info(argv[1], ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - s.add_torrent(p, ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - - // wait for the user to end - char a; - scanf("%c\n", &a); - return 0; -} - diff --git a/libtorrent_utp/examples/upnp_test.cpp b/libtorrent_utp/examples/upnp_test.cpp deleted file mode 100644 index 6bec75a2c..000000000 --- a/libtorrent_utp/examples/upnp_test.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - -Copyright (c) 2003, 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/session.hpp" -#include "libtorrent/alert_types.hpp" - -void print_alert(libtorrent::alert const* a) -{ - using namespace libtorrent; - - if (alert_cast(a)) - { - printf("%s","\x1b[32m"); - } - else if (alert_cast(a)) - { - printf("%s","\x1b[33m"); - } - - printf("[%s] %s\n", time_now_string(), a->message().c_str()); - printf("%s", "\x1b[0m"); -} - -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - - if (argc != 1) - { - fputs("usage: ./upnp_test\n", stderr); - return 1; - } - - session s; - s.set_alert_mask(alert::port_mapping_notification); - - for (;;) - { - alert const* a = s.wait_for_alert(seconds(5)); - if (a == 0) - { - s.stop_upnp(); - s.stop_natpmp(); - break; - } - std::auto_ptr holder = s.pop_alert(); - print_alert(holder.get()); - } - - printf("\x1b[1m\n\n===================== done mapping. Now deleting mappings ========================\n\n\n\x1b[0m"); - - for (;;) - { - alert const* a = s.wait_for_alert(seconds(5)); - if (a == 0) break; - std::auto_ptr holder = s.pop_alert(); - print_alert(holder.get()); - } - - - return 0; -} - diff --git a/libtorrent_utp/examples/utp_test.cpp b/libtorrent_utp/examples/utp_test.cpp deleted file mode 100644 index b26003c4a..000000000 --- a/libtorrent_utp/examples/utp_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "libtorrent/error_code.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/socket_type.hpp" -#include "libtorrent/utp_socket_manager.hpp" -#include "libtorrent/utp_stream.hpp" - -using namespace libtorrent; - -void on_connect(error_code const& e) -{ -} - -void on_udp_receive(error_code const& e, udp::endpoint const& ep - , char const* buf, int size) -{ -} - -void on_utp_incoming(void* userdata - , boost::shared_ptr const& utp_sock) -{ -} - -int main(int argc, char* argv[]) -{ - //int rtt, rtt_var; - //int max_window, cur_window; - //int delay_factor, window_factor, scaled_gain; - - /*session s; - s.listen_on(std::make_pair(6881, 6889));*/ - - io_service ios; - connection_queue cc(ios); - udp_socket udp_sock(ios, boost::bind(&on_udp_receive, _1, _2, _3, _4), cc); - - void* userdata; - utp_socket_manager utp_sockets(udp_sock, boost::bind(&on_utp_incoming, _1, _2), userdata); - - /*error_code ec; - utp_stream sock(ios, cc); - sock.bind(udp::endpoint(address_v4::any(), 0), ec); - - tcp::endpoint ep(address_v4::from_string("239.192.152.143", ec), 6771); - - sock.async_connect(ep, boost::bind(on_connect, _1));*/ - - return 0; -} diff --git a/libtorrent_utp/include/libtorrent/ConvertUTF.h b/libtorrent_utp/include/libtorrent/ConvertUTF.h deleted file mode 100644 index 64bc326aa..000000000 --- a/libtorrent_utp/include/libtorrent/ConvertUTF.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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_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_EXPORT ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -TORRENT_EXPORT ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -TORRENT_EXPORT ConversionResult ConvertUTF8toUTF32 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); - -TORRENT_EXPORT ConversionResult ConvertUTF32toUTF8 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF32 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF32toUTF16 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); - -#ifdef __cplusplus -} -#endif -/* --------------------------------------------------------------------- */ diff --git a/libtorrent_utp/include/libtorrent/GeoIP.h b/libtorrent_utp/include/libtorrent/GeoIP.h deleted file mode 100644 index 74d5c66c7..000000000 --- a/libtorrent_utp/include/libtorrent/GeoIP.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- 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 */ -#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/libtorrent_utp/include/libtorrent/Makefile.am b/libtorrent_utp/include/libtorrent/Makefile.am deleted file mode 100644 index 9bf0fac2f..000000000 --- a/libtorrent_utp/include/libtorrent/Makefile.am +++ /dev/null @@ -1,135 +0,0 @@ -includedir = @includedir@/libtorrent - -if WITH_SHIPPED_GEOIP -GEOIP_H = GeoIP.h -endif - -nobase_include_HEADERS = \ - address.hpp \ - add_torrent_params.hpp \ - alert.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 \ - 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_io_thread.hpp \ - entry.hpp \ - enum_net.hpp \ - error.hpp \ - error_code.hpp \ - escape_string.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 \ - lazy_entry.hpp \ - lsd.hpp \ - magnet_uri.hpp \ - max.hpp \ - natpmp.hpp \ - parse_url.hpp \ - pch.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 \ - session.hpp \ - session_settings.hpp \ - session_status.hpp \ - settings.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 \ - 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_peer_connection.hpp \ - xml_parse.hpp \ - \ - $(GEOIP_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/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 diff --git a/libtorrent_utp/include/libtorrent/add_torrent_params.hpp b/libtorrent_utp/include/libtorrent/add_torrent_params.hpp deleted file mode 100644 index aec9edf1c..000000000 --- a/libtorrent_utp/include/libtorrent/add_torrent_params.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - -Copyright (c) 2009, 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" - -namespace libtorrent -{ - class torrent_info; - - struct add_torrent_params - { - add_torrent_params(storage_constructor_type sc = default_storage_constructor) - : version(LIBTORRENT_VERSION_NUM) - , tracker_url(0) - , name(0) - , resume_data(0) - , storage_mode(storage_mode_sparse) - , paused(true) - , auto_managed(true) - , duplicate_is_error(false) - , storage(sc) - , userdata(0) - , seed_mode(false) - , override_resume_data(false) - , upload_mode(false) - , file_priorities(0) - , share_mode(false) - {} - - // libtorrent version. Used for forward binary compatibility - int version; - boost::intrusive_ptr ti; - char const* tracker_url; - sha1_hash info_hash; - char const* name; - std::string save_path; - std::vector* resume_data; - storage_mode_t storage_mode; - bool paused; - bool auto_managed; - bool duplicate_is_error; - storage_constructor_type storage; - void* userdata; - bool seed_mode; - bool override_resume_data; - bool upload_mode; - std::vector const* file_priorities; - bool share_mode; - std::string trackerid; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/address.hpp b/libtorrent_utp/include/libtorrent/address.hpp deleted file mode 100644 index f3121c13d..000000000 --- a/libtorrent_utp/include/libtorrent/address.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/alert.hpp b/libtorrent_utp/include/libtorrent/alert.hpp deleted file mode 100644 index 32d9b6a4d..000000000 --- a/libtorrent_utp/include/libtorrent/alert.hpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - -Copyright (c) 2003, 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 -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/ptime.hpp" -#include "libtorrent/config.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/thread.hpp" -#include "libtorrent/io_service_fwd.hpp" - -#ifndef TORRENT_MAX_ALERT_TYPES -#define TORRENT_MAX_ALERT_TYPES 15 -#endif - -namespace libtorrent { - - class TORRENT_EXPORT alert - { - public: - -#ifndef TORRENT_NO_DEPRECATE - // only here for backwards compatibility - enum severity_t { debug, info, warning, critical, fatal, none }; -#endif - - enum category_t - { - error_notification = 0x1, - peer_notification = 0x2, - port_mapping_notification = 0x4, - storage_notification = 0x8, - tracker_notification = 0x10, - debug_notification = 0x20, - status_notification = 0x40, - progress_notification = 0x80, - ip_block_notification = 0x100, - performance_warning = 0x200, - dht_notification = 0x400, - stats_notification = 0x800, - - all_categories = 0xffffffff - }; - - alert(); - virtual ~alert(); - - // a timestamp is automatically created in the constructor - ptime timestamp() const; - - virtual int type() const = 0; - virtual char const* what() const = 0; - virtual std::string message() const = 0; - virtual int category() const = 0; - -#ifndef TORRENT_NO_DEPRECATE - TORRENT_DEPRECATED_PREFIX - severity_t severity() const TORRENT_DEPRECATED { return warning; } -#endif - - virtual std::auto_ptr clone() const = 0; - - private: - ptime m_timestamp; - }; - - class TORRENT_EXPORT alert_manager - { - public: - enum { queue_size_limit_default = 1000 }; - - alert_manager(io_service& ios); - ~alert_manager(); - - void post_alert(const alert& alert_); - bool pending() const; - std::auto_ptr get(); - - 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; - } - - alert const* wait_for_alert(time_duration max_wait); - - void set_alert_mask(int m) - { - mutex::scoped_lock lock(m_mutex); - m_alert_mask = m; - } - - 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&); - - private: - std::deque m_alerts; - mutable mutex m_mutex; - event m_condition; - int m_alert_mask; - size_t m_queue_size_limit; - boost::function)> m_dispatch; - io_service& m_ios; - }; - - 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 - -template -T* alert_cast(alert* a) -{ - if (a->type() == T::alert_type) return static_cast(a); - return 0; -} - -template -T const* alert_cast(alert const* a) -{ - if (a->type() == T::alert_type) return static_cast(a); - return 0; -} - -} // namespace libtorrent - -#endif // TORRENT_ALERT_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/alert_types.hpp b/libtorrent_utp/include/libtorrent/alert_types.hpp deleted file mode 100644 index 212e23aca..000000000 --- a/libtorrent_utp/include/libtorrent/alert_types.hpp +++ /dev/null @@ -1,1190 +0,0 @@ -/* - -Copyright (c) 2003, 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" - -// lines reserved for future includes -// the type-ids of the alert types -// are derived from the line on which -// they are declared - - - - - -namespace libtorrent -{ - struct TORRENT_EXPORT torrent_alert: alert - { - torrent_alert(torrent_handle const& h) - : handle(h) - {} - - const static int alert_type = 1; - virtual std::string message() const; - - torrent_handle handle; - }; - - struct TORRENT_EXPORT peer_alert: torrent_alert - { - peer_alert(torrent_handle const& h, tcp::endpoint const& ip_ - , peer_id const& pid_) - : torrent_alert(h) - , ip(ip_) - , pid(pid_) - {} - - 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; - - tcp::endpoint ip; - peer_id pid; - }; - - struct TORRENT_EXPORT tracker_alert: torrent_alert - { - tracker_alert(torrent_handle const& h - , std::string const& url_) - : torrent_alert(h) - , url(url_) - {} - - 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; - - 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; } - - struct TORRENT_EXPORT read_piece_alert: torrent_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) - {} - - TORRENT_DEFINE_ALERT(read_piece_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const; - - boost::shared_array buffer; - int piece; - int size; - }; - - struct TORRENT_EXPORT file_completed_alert: torrent_alert - { - file_completed_alert(torrent_handle const& h - , int index_) - : torrent_alert(h) - , index(index_) - {} - - TORRENT_DEFINE_ALERT(file_completed_alert); - - const static int static_category = alert::progress_notification; - virtual std::string message() const; - - int index; - }; - - struct TORRENT_EXPORT file_renamed_alert: torrent_alert - { - file_renamed_alert(torrent_handle const& h - , std::string const& name_ - , int index_) - : torrent_alert(h) - , name(name_) - , index(index_) - {} - - TORRENT_DEFINE_ALERT(file_renamed_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const; - - std::string name; - int index; - }; - - struct TORRENT_EXPORT file_rename_failed_alert: torrent_alert - { - file_rename_failed_alert(torrent_handle const& h - , int index_ - , error_code ec_) - : torrent_alert(h) - , index(index_) - , error(ec_) - {} - - TORRENT_DEFINE_ALERT(file_rename_failed_alert); - - const static int static_category = alert::storage_notification; - - virtual std::string message() const; - - int index; - error_code error; - }; - - struct TORRENT_EXPORT performance_alert: torrent_alert - { - enum performance_warning_t - { - outstanding_disk_buffer_limit_reached, - outstanding_request_limit_reached, - upload_limit_too_low, - download_limit_too_low, - send_buffer_watermark_too_low, - too_many_optimistic_unchoke_slots, - bittyrant_with_no_uplimit, - - - - - num_warnings - }; - - performance_alert(torrent_handle const& h - , performance_warning_t w) - : torrent_alert(h) - , warning_code(w) - {} - - TORRENT_DEFINE_ALERT(performance_alert); - - const static int static_category = alert::performance_warning; - - virtual std::string message() const; - - performance_warning_t warning_code; - }; - - struct TORRENT_EXPORT state_changed_alert: torrent_alert - { - state_changed_alert(torrent_handle const& h - , torrent_status::state_t state_ - , torrent_status::state_t prev_state_) - : torrent_alert(h) - , state(state_) - , prev_state(prev_state_) - {} - - TORRENT_DEFINE_ALERT(state_changed_alert); - - const static int static_category = alert::status_notification; - - virtual std::string message() const; - - torrent_status::state_t state; - torrent_status::state_t prev_state; - }; - - struct TORRENT_EXPORT tracker_error_alert: tracker_alert - { - tracker_error_alert(torrent_handle const& h - , int times - , int status - , std::string const& url_ - , error_code const& e - , std::string const& m) - : tracker_alert(h, url_) - , times_in_row(times) - , status_code(status) - , error(e) - , msg(m) - { - TORRENT_ASSERT(!url.empty()); - } - - 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; - }; - - struct TORRENT_EXPORT tracker_warning_alert: tracker_alert - { - tracker_warning_alert(torrent_handle const& h - , std::string const& url_ - , std::string const& msg_) - : tracker_alert(h, url_) - , msg(msg_) - { TORRENT_ASSERT(!url.empty()); } - - TORRENT_DEFINE_ALERT(tracker_warning_alert); - - const static int static_category = alert::tracker_notification | alert::error_notification; - virtual std::string message() const; - - std::string msg; - }; - - struct TORRENT_EXPORT scrape_reply_alert: tracker_alert - { - scrape_reply_alert(torrent_handle const& h - , int incomplete_ - , int complete_ - , std::string const& url_) - : tracker_alert(h, url_) - , incomplete(incomplete_) - , complete(complete_) - { TORRENT_ASSERT(!url.empty()); } - - TORRENT_DEFINE_ALERT(scrape_reply_alert); - - virtual std::string message() const; - - int incomplete; - int complete; - }; - - struct TORRENT_EXPORT scrape_failed_alert: tracker_alert - { - scrape_failed_alert(torrent_handle const& h - , std::string const& url_ - , error_code const& e) - : tracker_alert(h, url_) - , msg(e.message()) - { TORRENT_ASSERT(!url.empty()); } - - scrape_failed_alert(torrent_handle const& h - , std::string const& url_ - , std::string const& msg_) - : tracker_alert(h, url_) - , msg(msg_) - { TORRENT_ASSERT(!url.empty()); } - - TORRENT_DEFINE_ALERT(scrape_failed_alert); - - const static int static_category = alert::tracker_notification | alert::error_notification; - virtual std::string message() const; - - std::string msg; - }; - - struct TORRENT_EXPORT tracker_reply_alert: tracker_alert - { - tracker_reply_alert(torrent_handle const& h - , int np - , std::string const& url_) - : tracker_alert(h, url_) - , num_peers(np) - { TORRENT_ASSERT(!url.empty()); } - - TORRENT_DEFINE_ALERT(tracker_reply_alert); - - virtual std::string message() const; - - int num_peers; - }; - - struct TORRENT_EXPORT dht_reply_alert: tracker_alert - { - dht_reply_alert(torrent_handle const& h - , int np) - : tracker_alert(h, "") - , num_peers(np) - {} - - TORRENT_DEFINE_ALERT(dht_reply_alert); - - virtual std::string message() const; - - int num_peers; - }; - - struct TORRENT_EXPORT tracker_announce_alert: tracker_alert - { - tracker_announce_alert(torrent_handle const& h - , std::string const& url_, int event_) - : tracker_alert(h, url_) - , event(event_) - { TORRENT_ASSERT(!url.empty()); } - - TORRENT_DEFINE_ALERT(tracker_announce_alert); - - virtual std::string message() const; - - int event; - }; - - struct TORRENT_EXPORT hash_failed_alert: torrent_alert - { - hash_failed_alert( - torrent_handle const& h - , int index) - : torrent_alert(h) - , piece_index(index) - { TORRENT_ASSERT(index >= 0);} - - TORRENT_DEFINE_ALERT(hash_failed_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const; - - int piece_index; - }; - - struct TORRENT_EXPORT peer_ban_alert: peer_alert - { - peer_ban_alert(torrent_handle h, tcp::endpoint const& ep - , peer_id const& peer_id) - : peer_alert(h, ep, peer_id) - {} - - TORRENT_DEFINE_ALERT(peer_ban_alert); - - virtual std::string message() const; - }; - - struct TORRENT_EXPORT peer_unsnubbed_alert: peer_alert - { - peer_unsnubbed_alert(torrent_handle h, tcp::endpoint const& ep - , peer_id const& peer_id) - : peer_alert(h, ep, peer_id) - {} - - TORRENT_DEFINE_ALERT(peer_unsnubbed_alert); - - virtual std::string message() const; - }; - - struct TORRENT_EXPORT peer_snubbed_alert: peer_alert - { - peer_snubbed_alert(torrent_handle h, tcp::endpoint const& ep - , peer_id const& peer_id) - : peer_alert(h, ep, peer_id) - {} - - TORRENT_DEFINE_ALERT(peer_snubbed_alert); - - virtual std::string message() const; - }; - - struct TORRENT_EXPORT peer_error_alert: peer_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 = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(peer_error_alert); - - const static int static_category = alert::peer_notification; - virtual std::string message() const - { - return peer_alert::message() + " peer error: " + error.message(); - } - - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT peer_connect_alert: peer_alert - { - peer_connect_alert(torrent_handle h, tcp::endpoint const& ep - , peer_id const& peer_id) - : peer_alert(h, ep, peer_id) - {} - - TORRENT_DEFINE_ALERT(peer_connect_alert); - - const static int static_category = alert::debug_notification; - virtual std::string message() const - { return peer_alert::message() + " connecting to peer"; } - }; - - struct TORRENT_EXPORT peer_disconnected_alert: peer_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 = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(peer_disconnected_alert); - - const static int static_category = alert::debug_notification; - virtual std::string message() const - { return peer_alert::message() + " disconnecting: " + error.message(); } - - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT invalid_request_alert: peer_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) - {} - - TORRENT_DEFINE_ALERT(invalid_request_alert); - - virtual std::string message() const; - - peer_request request; - }; - - struct TORRENT_EXPORT torrent_finished_alert: torrent_alert - { - torrent_finished_alert( - const torrent_handle& h) - : torrent_alert(h) - {} - - TORRENT_DEFINE_ALERT(torrent_finished_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { return torrent_alert::message() + " torrent finished downloading"; } - }; - - struct TORRENT_EXPORT piece_finished_alert: torrent_alert - { - piece_finished_alert( - const torrent_handle& h - , int piece_num) - : torrent_alert(h) - , piece_index(piece_num) - { TORRENT_ASSERT(piece_index >= 0);} - - TORRENT_DEFINE_ALERT(piece_finished_alert); - - const static int static_category = alert::progress_notification; - virtual std::string message() const; - - int piece_index; - }; - - struct TORRENT_EXPORT request_dropped_alert: peer_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);} - - 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; - }; - - struct TORRENT_EXPORT block_timeout_alert: peer_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);} - - 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; - }; - - struct TORRENT_EXPORT block_finished_alert: peer_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);} - - 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; - }; - - struct TORRENT_EXPORT block_downloading_alert: peer_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); } - - 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; - }; - - struct TORRENT_EXPORT unwanted_block_alert: peer_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);} - - TORRENT_DEFINE_ALERT(unwanted_block_alert); - - virtual std::string message() const; - - int block_index; - int piece_index; - }; - - struct TORRENT_EXPORT storage_moved_alert: torrent_alert - { - storage_moved_alert(torrent_handle const& h, std::string const& path_) - : torrent_alert(h) - , path(path_) - {} - - TORRENT_DEFINE_ALERT(storage_moved_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const - { - return torrent_alert::message() + " moved storage to: " - + path; - } - - std::string path; - }; - - struct TORRENT_EXPORT storage_moved_failed_alert: torrent_alert - { - storage_moved_failed_alert(torrent_handle const& h, error_code const& ec_) - : torrent_alert(h) - , error(ec_) - {} - - TORRENT_DEFINE_ALERT(storage_moved_failed_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const - { - return torrent_alert::message() + " storage move failed: " - + error.message(); - } - - error_code error; - }; - - struct TORRENT_EXPORT torrent_deleted_alert: torrent_alert - { - torrent_deleted_alert(torrent_handle const& h, sha1_hash const& ih) - : torrent_alert(h) - { info_hash = ih; } - - TORRENT_DEFINE_ALERT(torrent_deleted_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const - { return torrent_alert::message() + " deleted"; } - - sha1_hash info_hash; - }; - - struct TORRENT_EXPORT torrent_delete_failed_alert: torrent_alert - { - torrent_delete_failed_alert(torrent_handle const& h, error_code const& e) - : torrent_alert(h) - , error(e) - { -#ifndef TORRENT_NO_DEPRECATE - msg = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(torrent_delete_failed_alert); - - const static int static_category = alert::storage_notification - | alert::error_notification; - virtual std::string message() const - { - return torrent_alert::message() + " torrent deletion failed: " - + error.message(); - } - - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT save_resume_data_alert: torrent_alert - { - save_resume_data_alert(boost::shared_ptr const& rd - , torrent_handle const& h) - : torrent_alert(h) - , resume_data(rd) - {} - - TORRENT_DEFINE_ALERT(save_resume_data_alert); - - const static int static_category = alert::storage_notification; - virtual std::string message() const - { return torrent_alert::message() + " resume data generated"; } - - boost::shared_ptr resume_data; - }; - - struct TORRENT_EXPORT save_resume_data_failed_alert: torrent_alert - { - save_resume_data_failed_alert(torrent_handle const& h - , error_code const& e) - : torrent_alert(h) - , error(e) - { -#ifndef TORRENT_NO_DEPRECATE - msg = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(save_resume_data_failed_alert); - - const static int static_category = alert::storage_notification - | alert::error_notification; - virtual std::string message() const - { - return torrent_alert::message() + " resume data was not generated: " - + error.message(); - } - - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT torrent_paused_alert: torrent_alert - { - torrent_paused_alert(torrent_handle const& h) - : torrent_alert(h) - {} - - TORRENT_DEFINE_ALERT(torrent_paused_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { return torrent_alert::message() + " paused"; } - }; - - struct TORRENT_EXPORT torrent_resumed_alert: torrent_alert - { - torrent_resumed_alert(torrent_handle const& h) - : torrent_alert(h) {} - - TORRENT_DEFINE_ALERT(torrent_resumed_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { return torrent_alert::message() + " resumed"; } - }; - - struct TORRENT_EXPORT torrent_checked_alert: torrent_alert - { - torrent_checked_alert(torrent_handle const& h) - : torrent_alert(h) - {} - - TORRENT_DEFINE_ALERT(torrent_checked_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { return torrent_alert::message() + " checked"; } - }; - - struct TORRENT_EXPORT url_seed_alert: torrent_alert - { - url_seed_alert( - torrent_handle const& h - , std::string const& url_ - , error_code const& e) - : torrent_alert(h) - , url(url_) - , msg(e.message()) - {} - - url_seed_alert( - torrent_handle const& h - , std::string const& url_ - , std::string const& msg_) - : torrent_alert(h) - , url(url_) - , msg(msg_) - {} - - TORRENT_DEFINE_ALERT(url_seed_alert); - - const static int static_category = alert::peer_notification | alert::error_notification; - virtual std::string message() const - { - return torrent_alert::message() + " url seed (" - + url + ") failed: " + msg; - } - - std::string url; - std::string msg; - }; - - struct TORRENT_EXPORT file_error_alert: torrent_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 = error.message(); -#endif - } - - 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 - { - return torrent_alert::message() + " file (" + file + ") error: " - + error.message(); - } - - std::string file; - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT metadata_failed_alert: torrent_alert - { - metadata_failed_alert(const torrent_handle& h) - : torrent_alert(h) - {} - - TORRENT_DEFINE_ALERT(metadata_failed_alert); - - const static int static_category = alert::error_notification; - virtual std::string message() const - { return torrent_alert::message() + " invalid metadata received"; } - }; - - struct TORRENT_EXPORT metadata_received_alert: torrent_alert - { - metadata_received_alert( - const torrent_handle& h) - : torrent_alert(h) - {} - - TORRENT_DEFINE_ALERT(metadata_received_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { return torrent_alert::message() + " metadata successfully received"; } - }; - - struct TORRENT_EXPORT udp_error_alert: alert - { - udp_error_alert( - udp::endpoint const& ep - , error_code const& ec) - : endpoint(ep) - , error(ec) - {} - - TORRENT_DEFINE_ALERT(udp_error_alert); - - const static int static_category = alert::error_notification; - virtual std::string message() const - { - error_code ec; - return "UDP error: " + error.message() + " from: " + endpoint.address().to_string(ec); - } - - udp::endpoint endpoint; - error_code error; - }; - - struct TORRENT_EXPORT external_ip_alert: alert - { - external_ip_alert(address const& ip) - : external_address(ip) - {} - - TORRENT_DEFINE_ALERT(external_ip_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const - { - error_code ec; - return "external IP received: " + external_address.to_string(ec); - } - - address external_address; - }; - - struct TORRENT_EXPORT listen_failed_alert: alert - { - listen_failed_alert( - tcp::endpoint const& ep - , error_code const& ec) - : endpoint(ep) - , error(ec) - {} - - TORRENT_DEFINE_ALERT(listen_failed_alert); - - const static int static_category = alert::status_notification | alert::error_notification; - virtual std::string message() const; - - tcp::endpoint endpoint; - error_code error; - }; - - struct TORRENT_EXPORT listen_succeeded_alert: alert - { - listen_succeeded_alert(tcp::endpoint const& ep) - : endpoint(ep) - {} - - TORRENT_DEFINE_ALERT(listen_succeeded_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const; - - tcp::endpoint endpoint; - }; - - struct TORRENT_EXPORT portmap_error_alert: alert - { - portmap_error_alert(int i, int t, error_code const& e) - : mapping(i), map_type(t), error(e) - { -#ifndef TORRENT_NO_DEPRECATE - msg = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(portmap_error_alert); - - const static int static_category = alert::port_mapping_notification - | alert::error_notification; - virtual std::string message() const; - - int mapping; - int map_type; - error_code error; -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT portmap_alert: alert - { - portmap_alert(int i, int port, int t) - : mapping(i), external_port(port), map_type(t) - {} - - TORRENT_DEFINE_ALERT(portmap_alert); - - const static int static_category = alert::port_mapping_notification; - virtual std::string message() const; - - int mapping; - int external_port; - int map_type; - }; - - struct TORRENT_EXPORT portmap_log_alert: alert - { - portmap_log_alert(int t, std::string const& m) - : map_type(t), msg(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; - }; - - struct TORRENT_EXPORT fastresume_rejected_alert: torrent_alert - { - fastresume_rejected_alert(torrent_handle const& h - , error_code const& e) - : torrent_alert(h) - , error(e) - { -#ifndef TORRENT_NO_DEPRECATE - msg = error.message(); -#endif - } - - TORRENT_DEFINE_ALERT(fastresume_rejected_alert); - - const static int static_category = alert::status_notification - | alert::error_notification; - virtual std::string message() const - { return torrent_alert::message() + " fast resume rejected: " + error.message(); } - - error_code error; - -#ifndef TORRENT_NO_DEPRECATE - std::string msg; -#endif - }; - - struct TORRENT_EXPORT peer_blocked_alert: torrent_alert - { - peer_blocked_alert(torrent_handle const& h, address const& ip_) - : torrent_alert(h) - , ip(ip_) - {} - - TORRENT_DEFINE_ALERT(peer_blocked_alert); - - const static int static_category = alert::ip_block_notification; - virtual std::string message() const - { - error_code ec; - return torrent_alert::message() + ": blocked peer: " + ip.to_string(ec); - } - - address ip; - }; - - struct TORRENT_EXPORT dht_announce_alert: alert - { - dht_announce_alert(address const& ip_, int port_ - , sha1_hash const& info_hash_) - : ip(ip_) - , port(port_) - , info_hash(info_hash_) - {} - - 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; - }; - - struct TORRENT_EXPORT dht_get_peers_alert: alert - { - dht_get_peers_alert(sha1_hash const& info_hash_) - : info_hash(info_hash_) - {} - - TORRENT_DEFINE_ALERT(dht_get_peers_alert); - - const static int static_category = alert::dht_notification; - virtual std::string message() const; - - sha1_hash info_hash; - }; - - struct TORRENT_EXPORT stats_alert: torrent_alert - { - 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 - }; - - int transferred[num_channels]; - int interval; - }; - - struct TORRENT_EXPORT cache_flushed_alert: torrent_alert - { - cache_flushed_alert(torrent_handle const& h); - - TORRENT_DEFINE_ALERT(cache_flushed_alert); - - const static int static_category = alert::storage_notification; - }; - - struct TORRENT_EXPORT anonymous_mode_alert: torrent_alert - { - anonymous_mode_alert(torrent_handle const& h - , int kind_, std::string const& str_) - : torrent_alert(h) - , kind(kind_) - , str(str_) - {} - - TORRENT_DEFINE_ALERT(anonymous_mode_alert); - - const static int static_category = alert::error_notification; - virtual std::string message() const; - - enum kind_t - { - tracker_not_anonymous = 0 - }; - - int kind; - std::string str; - }; - - struct TORRENT_EXPORT lsd_peer_alert: peer_alert - { - lsd_peer_alert(torrent_handle const& h - , tcp::endpoint const& ip_) - : peer_alert(h, ip_, peer_id(0)) - {} - - TORRENT_DEFINE_ALERT(lsd_peer_alert); - - const static int static_category = alert::peer_notification; - virtual std::string message() const; - }; - - struct TORRENT_EXPORT trackerid_alert: tracker_alert - { - trackerid_alert(torrent_handle const& h - , std::string const& url_ - , const std::string& id) - : tracker_alert(h, url_) - , trackerid(id) - {} - - TORRENT_DEFINE_ALERT(trackerid_alert); - - const static int static_category = alert::status_notification; - virtual std::string message() const; - - std::string trackerid; - }; - -} - - -#endif diff --git a/libtorrent_utp/include/libtorrent/alloca.hpp b/libtorrent_utp/include/libtorrent/alloca.hpp deleted file mode 100644 index 0eec9f0ff..000000000 --- a/libtorrent_utp/include/libtorrent/alloca.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - -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_ALLOCA - -#include "libtorrent/config.hpp" - -#ifdef TORRENT_WINDOWS - -#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/libtorrent_utp/include/libtorrent/allocator.hpp b/libtorrent_utp/include/libtorrent/allocator.hpp deleted file mode 100644 index 0d481531e..000000000 --- a/libtorrent_utp/include/libtorrent/allocator.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - -Copyright (c) 2009, 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_EXPORT int page_size(); - - struct TORRENT_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* const block); - }; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/assert.hpp b/libtorrent_utp/include/libtorrent/assert.hpp deleted file mode 100644 index f5997bcf4..000000000 --- a/libtorrent_utp/include/libtorrent/assert.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - -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_ASSERT - -#include "libtorrent/config.hpp" - -#if (!defined TORRENT_DEBUG && !TORRENT_PRODUCTION_ASSERTS) || TORRENT_NO_ASSERTS -#define TORRENT_ASSERT(a) do {} while(false) -#define TORRENT_ASSERT_VAL(a, b) do {} while(false) -#else - -#if TORRENT_PRODUCTION_ASSERTS -extern char const* libtorrent_assert_log; -#endif - -#include - -#ifdef __GNUC__ -std::string demangle(char const* name); -#endif - -#if (defined __linux__ || defined __MACH__) && defined __GNUC__ - -#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); -#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()); } } while (false) -#else -#define TORRENT_ASSERT_VAL(x, y) TORRENT_ASSERT(x) -#endif - -#else -#include -#define TORRENT_ASSERT(x) assert(x) -#define TORRENT_ASSERT_VAL(x, y) assert(x) -#endif - -#endif - -#endif - diff --git a/libtorrent_utp/include/libtorrent/aux_/session_impl.hpp b/libtorrent_utp/include/libtorrent/aux_/session_impl.hpp deleted file mode 100644 index fde526ae5..000000000 --- a/libtorrent_utp/include/libtorrent/aux_/session_impl.hpp +++ /dev/null @@ -1,924 +0,0 @@ -/* - -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 - -#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/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.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" - -#ifdef TORRENT_STATS -#include -#endif - -#if TORRENT_COMPLETE_TYPES_REQUIRED -#include "libtorrent/peer_connection.hpp" -#endif - -#ifdef TORRENT_USE_OPENSSL -#include -#endif - -namespace libtorrent -{ - - class upnp; - class natpmp; - class lsd; - struct fingerprint; - class torrent; - class alert; - - namespace dht - { - struct dht_tracker; - } - - namespace aux - { - struct session_impl; - -#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(); - }; - - // this is the link between the main thread and the - // thread started to run the main downloader loop - struct session_impl: boost::noncopyable, initialize_timer - { - - // 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 -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - , std::string const& logpath -#endif - ); - ~session_impl(); - void start(); - -#ifndef TORRENT_DISABLE_EXTENSIONS - void add_extension(boost::function( - torrent*, void*)> ext); -#endif -#ifdef TORRENT_DEBUG - bool has_peer(peer_connection const* p) const - { - return std::find_if(m_connections.begin(), m_connections.end() - , boost::bind(&boost::intrusive_ptr::get, _1) == p) - != m_connections.end(); - } -#endif - void main_thread(); - - void open_listen_port(bool reuse_address); - - // 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); - void on_accept_connection(boost::shared_ptr const& s - , boost::weak_ptr listener, error_code const& e); - void on_socks_accept(boost::shared_ptr const& s - , error_code const& e); - - void incoming_connection(boost::shared_ptr const& s); - -#ifdef TORRENT_DEBUG -#if defined BOOST_HAS_PTHREADS - pthread_t m_network_thread; -#endif - bool is_network_thread() const - { -#if defined BOOST_HAS_PTHREADS - return m_network_thread == pthread_self(); -#endif - return true; - } -#endif - - boost::weak_ptr find_torrent(const sha1_hash& info_hash); - 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); - -#ifndef TORRENT_NO_DEPRECATE - entry dht_state() const; -#endif - void maybe_update_udp_mapping(int nat, int local_port, int external_port); - - void on_dht_announce(error_code const& e); - void on_dht_router_name_lookup(error_code const& e - , tcp::resolver::iterator host); -#endif - -#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, 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); - - bool listen_on( - std::pair const& port_range - , const char* net_interface = 0 - , int flags = 0); - bool is_listening() const; - - torrent_handle add_torrent(add_torrent_params const&, error_code& ec); - - void remove_torrent(torrent_handle const& h, int options); - - std::vector get_torrents(); - - void queue_check_torrent(boost::shared_ptr const& t); - void dequeue_check_torrent(boost::shared_ptr const& t); - - void set_alert_mask(int m); - size_t set_alert_queue_size_limit(size_t queue_size_limit_); - std::auto_ptr pop_alert(); - void set_alert_dispatch(boost::function)> const&); - - 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); - unsigned short listen_port() const; - - void abort(); - - torrent_handle find_torrent_handle(sha1_hash const& info_hash); - - void announce_lsd(sha1_hash const& ih); - - 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; } -#endif - -#if TORRENT_USE_I2P - void set_i2p_proxy(proxy_settings const& s) - { - m_i2p_conn.open(s, boost::bind(&session_impl::on_i2p_open, this, _1)); - open_new_incoming_i2p_connection(); - } - 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 - void load_asnum_dbw(std::wstring file); - void load_country_dbw(std::wstring file); -#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 next_port(); - - void add_redundant_bytes(size_type b) - { - TORRENT_ASSERT(b > 0); - m_total_redundant_bytes += b; - } - - void add_failed_bytes(size_type b) - { - TORRENT_ASSERT(b > 0); - m_total_failed_bytes += b; - } - - std::pair allocate_buffer(int size); - void free_buffer(char* buf, int size); - - char* allocate_disk_buffer(char const* category); - void free_disk_buffer(char* buf); - - void set_external_address(address const& ip); - address const& external_address() const { return m_external_address; } - - // used when posting synchronous function - // calls to session_impl and torrent objects - mutable libtorrent::mutex mut; - mutable libtorrent::condition cond; - -// private: - - void update_connections_limit(); - void update_unchoke_limit(); - 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); - - // 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 - mutex m_send_buffer_mutex; - - // 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 - 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]; - - tracker_manager m_tracker_manager; - torrent_map m_torrents; - 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; - - // 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; - - struct listen_socket_t - { - listen_socket_t(): external_port(0) {} - // 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; - - // the actual socket - boost::shared_ptr sock; - }; - // since we might be listening on multiple interfaces - // we might need more than one listen socket - std::list m_listen_sockets; - - // 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(); - -#if TORRENT_USE_I2P - i2p_connection m_i2p_conn; - boost::shared_ptr m_i2p_listen_socket; -#endif - - listen_socket_t setup_listener(tcp::endpoint ep, int retries - , bool v6_only, bool reuse_address); - - // the settings for the client - session_settings m_settings; - - // the proxy used for bittorrent - proxy_settings m_proxy; - -#ifndef TORRENT_DISABLE_DHT - entry m_dht_state; -#endif - // 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; - - // 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; - - // 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; - - void on_disk_queue(); - void on_tick(error_code const& e); - - 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; - int session_time() const { return total_seconds(time_now() - m_created); } - - ptime m_last_tick; - ptime m_last_second_tick; - - // the last time we went through the peers - // to decide which ones to choke/unchoke - ptime m_last_choke; - - // 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; - - void on_receive_udp(error_code const& e - , udp::endpoint const& ep, char const* buf, int len); - - void on_receive_udp_hostname(error_code const& e - , char const* hostname, char const* buf, int len); - - // this announce timer is used - // by the DHT. - deadline_timer m_dht_announce_timer; -#endif - - // 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; - -#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; - - // 0 is natpmp 1 is upnp - int m_tcp_mapping[2]; - int m_udp_mapping[2]; - - // 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; -#endif - - // 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; -#ifdef TORRENT_DEBUG - void check_invariant() const; -#endif - -#if defined TORRENT_STATS && defined TORRENT_DISK_STATS - void log_buffer_usage(); -#endif - -#if defined TORRENT_STATS - // logger used to write bandwidth usage statistics - std::ofstream m_stats_logger; - int m_second_counter; - // 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 - - // 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; - -#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); - - // 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 m_logpath; - public: - boost::shared_ptr m_logger; - - private: - -#endif -#ifdef TORRENT_UPNP_LOGGING - std::ofstream m_upnp_log; -#endif - address m_external_address; - -#ifndef TORRENT_DISABLE_EXTENSIONS - typedef std::list(torrent*, void*)> > extension_list_t; - - extension_list_t m_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; - - // the main working thread - boost::scoped_ptr m_thread; - }; - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - struct tracker_logger : request_callback - { - tracker_logger(session_impl& ses): m_ses(ses) {} - void tracker_warning(tracker_request const& req - , std::string const& str) - { - debug_log("*** tracker warning: " + 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 - , 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); - } - - void tracker_request_timed_out( - tracker_request const&) - { - debug_log("*** tracker timed out"); - } - - void tracker_request_error(tracker_request const& r - , int response_code, error_code const& ec, const std::string& str - , int retry_interval) - { - char msg[256]; - snprintf(msg, sizeof(msg), "*** tracker error: %d: %s %s" - , response_code, ec.message().c_str(), str.c_str()); - debug_log(msg); - } - - void debug_log(const std::string& line) - { - (*m_ses.m_logger) << time_now_string() << " " << line << "\n"; - } - session_impl& m_ses; - }; -#endif - - } -} - - -#endif - diff --git a/libtorrent_utp/include/libtorrent/bandwidth_limit.hpp b/libtorrent_utp/include/libtorrent/bandwidth_limit.hpp deleted file mode 100644 index 727f0c8a3..000000000 --- a/libtorrent_utp/include/libtorrent/bandwidth_limit.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - -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_BANDWIDTH_CHANNEL_HPP_INCLUDED -#define TORRENT_BANDWIDTH_CHANNEL_HPP_INCLUDED - -#include -#include - -#include "libtorrent/assert.hpp" - -namespace libtorrent { - -// member of peer_connection -struct TORRENT_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); - - // 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/libtorrent_utp/include/libtorrent/bandwidth_manager.hpp b/libtorrent_utp/include/libtorrent/bandwidth_manager.hpp deleted file mode 100644 index ba7223999..000000000 --- a/libtorrent_utp/include/libtorrent/bandwidth_manager.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - -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_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_EXPORT bandwidth_manager -{ - bandwidth_manager(int channel -#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT - , bool log = false -#endif - ); - - void close(); - -#ifdef TORRENT_DEBUG - bool is_queued(bandwidth_socket const* peer) const; -#endif - - int queue_size() const; - int 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 - void 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); - -#ifdef TORRENT_DEBUG - void check_invariant() const; -#endif - - void update_quotas(time_duration const& dt); - - // 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 - int 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/libtorrent_utp/include/libtorrent/bandwidth_queue_entry.hpp b/libtorrent_utp/include/libtorrent/bandwidth_queue_entry.hpp deleted file mode 100644 index 82c4d9dfd..000000000 --- a/libtorrent_utp/include/libtorrent/bandwidth_queue_entry.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - -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_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_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(); - - bandwidth_channel* channel[5]; -}; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/bandwidth_socket.hpp b/libtorrent_utp/include/libtorrent/bandwidth_socket.hpp deleted file mode 100644 index 249f6abe2..000000000 --- a/libtorrent_utp/include/libtorrent/bandwidth_socket.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/bencode.hpp b/libtorrent_utp/include/libtorrent/bencode.hpp deleted file mode 100644 index 9e3d14afa..000000000 --- a/libtorrent_utp/include/libtorrent/bencode.hpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - -Copyright (c) 2003, 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 - - - -/* - * This file declares the following functions: - * - *---------------------------------- - * template - * void libtorrent::bencode(OutIt out, const libtorrent::entry& e); - * - * Encodes a message entry with bencoding into the output - * iterator given. The bencoding is described in the BitTorrent - * protocol description document OutIt must be an OutputIterator - * of type char. This may throw libtorrent::invalid_encoding if - * the entry contains invalid nodes (undefined_t for example). - * - *---------------------------------- - * template - * libtorrent::entry libtorrent::bdecode(InIt start, InIt end); - * - * Decodes the buffer given by the start and end iterators - * and returns the decoded entry. InIt must be an InputIterator - * of type char. May throw libtorrent::invalid_encoding if - * the string is not correctly bencoded. - * - */ - - - - -#include - -#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" - -namespace libtorrent -{ - - struct TORRENT_EXPORT invalid_encoding: std::exception - { - virtual const char* what() const throw() { return "invalid bencoding"; } - }; - - namespace detail - { - template - int write_string(OutIt& out, const std::string& val) - { - for (std::string::const_iterator i = val.begin() - , end(val.end()); i != end; ++i) - *out++ = *i; - return val.length(); - } - - 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; - } - } - - // returns the number of bytes written - 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(out, e.string()); - 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(out, i->first); - // 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; -#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW - ret.integer() = _strtoi64(val.c_str(), &end_pointer, 10); -#else - ret.integer() = strtoll(val.c_str(), &end_pointer, 10); -#endif -#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 = std::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 - } - } - } - - 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/libtorrent_utp/include/libtorrent/bitfield.hpp b/libtorrent_utp/include/libtorrent/bitfield.hpp deleted file mode 100644 index 44dc195e3..000000000 --- a/libtorrent_utp/include/libtorrent/bitfield.hpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - -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_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 - -namespace libtorrent -{ - struct TORRENT_EXPORT bitfield - { - bitfield(): m_bytes(0), m_size(0), m_own(false) {} - bitfield(int bits): m_bytes(0), m_size(0) - { resize(bits); } - bitfield(int bits, bool val): m_bytes(0), m_size(0) - { resize(bits, val); } - bitfield(char const* b, int bits): m_bytes(0), m_size(0) - { assign(b, bits); } - bitfield(bitfield const& rhs): m_bytes(0), m_size(0), m_own(false) - { assign(rhs.bytes(), rhs.size()); } - - void borrow_bytes(char* b, int bits) - { - dealloc(); - m_bytes = (unsigned char*)b; - m_size = bits; - m_own = false; - } - ~bitfield() { dealloc(); } - - void assign(char const* b, int bits) - { resize(bits); std::memcpy(m_bytes, b, (bits + 7) / 8); clear_trailing_bits(); } - - 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; - } - - 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)); - } - - std::size_t size() const { return m_size; } - bool empty() const { return m_size == 0; } - - char const* bytes() const { return (char*)m_bytes; } - - bitfield& operator=(bitfield const& rhs) - { - assign(rhs.bytes(), rhs.size()); - return *this; - } - - 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); } - - 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 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); - } - - void resize(int bits) - { - 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 - { - m_bytes = (unsigned char*)std::malloc(b); - m_own = true; - } - m_size = bits; - clear_trailing_bits(); - } - - void free() { 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/libtorrent_utp/include/libtorrent/broadcast_socket.hpp b/libtorrent_utp/include/libtorrent/broadcast_socket.hpp deleted file mode 100644 index f560357a9..000000000 --- a/libtorrent_utp/include/libtorrent/broadcast_socket.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - -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_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_EXPORT bool is_local(address const& a); - TORRENT_EXPORT bool is_loopback(address const& addr); - TORRENT_EXPORT bool is_multicast(address const& addr); - TORRENT_EXPORT bool is_any(address const& addr); - TORRENT_EXPORT bool is_teredo(address const& addr); - TORRENT_EXPORT int cidr_distance(address const& a1, address const& a2); - - // determines if the operating system supports IPv6 - TORRENT_EXPORT bool supports_ipv6(); - - TORRENT_EXPORT int common_bits(unsigned char const* b1 - , unsigned char const* b2, int n); - - TORRENT_EXPORT address guess_local_address(io_service&); - - typedef boost::function receive_handler_t; - - class TORRENT_EXPORT broadcast_socket - { - public: - broadcast_socket(io_service& ios, udp::endpoint const& multicast_endpoint - , receive_handler_t const& handler, bool loopback = true); - ~broadcast_socket() { close(); } - - void send(char const* buffer, int size, error_code& ec); - 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) {} - socket_entry(boost::shared_ptr const& s - , address_v4 const& mask): socket(s), netmask(mask) {} - boost::shared_ptr socket; - char buffer[1024]; - udp::endpoint remote; - address_v4 netmask; - void close() - { - if (!socket) return; - error_code ec; - socket->close(ec); - } - address_v4 broadcast_address() const - { - error_code ec; - return address_v4::broadcast(socket->local_endpoint(ec).address().to_v4(), netmask); - } - }; - - 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); - - // 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; - - // if set, use IP broadcast as well as IP multicast - // this is off by default because it's expensive in - // terms of bandwidth usage - bool m_ip_broadcast; - - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/bt_peer_connection.hpp b/libtorrent_utp/include/libtorrent/bt_peer_connection.hpp deleted file mode 100644 index 6373cc089..000000000 --- a/libtorrent_utp/include/libtorrent/bt_peer_connection.hpp +++ /dev/null @@ -1,466 +0,0 @@ -/* - -Copyright (c) 2003 - 2006, Arvid Norberg -Copyright (c) 2007, 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_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::weak_ptr t - , boost::shared_ptr s - , tcp::endpoint const& remote - , policy::peer* peerinfo); - - // with this constructor we have been contacted and we still don't - // know which torrent the connection belongs to - bt_peer_connection( - aux::session_impl& ses - , boost::shared_ptr s - , tcp::endpoint const& remote - , policy::peer* peerinfo); - - void start(); - - enum - { - upload_only_msg = 2, - holepunch_msg = 3, - share_mode_msg = 4 - }; - - ~bt_peer_connection(); - -#ifndef TORRENT_DISABLE_ENCRYPTION - bool supports_encryption() const - { return m_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; - - bool supports_holepunch() const { return m_holepunch_id != 0; } - void write_holepunch_msg(int type, tcp::endpoint const& ep, int error); - -#ifndef TORRENT_DISABLE_EXTENSIONS - bool support_extensions() const { return m_supports_extensions; } -#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 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); - void on_holepunch(); - - void on_extended(int received); - - void on_extended_handshake(); - - 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(); -#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(); - -#ifdef TORRENT_DEBUG - 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(buffer::interval& write_buf - , int crypto_field, int pad_size); - - // stream key (info hash of attached torrent) - // secret is the DH shared secret - // initializes m_RC4_handler - void init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key); - -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); - void send_buffer(char const* buf, int size, int flags = 0); - buffer::interval allocate_send_buffer(int size); - template - void append_send_buffer(char* buffer, int size, Destructor const& destructor) - { -#ifndef TORRENT_DISABLE_ENCRYPTION - if (m_rc4_encrypted) - { - TORRENT_ASSERT(send_buffer_size() == m_encrypted_bytes); - m_RC4_handler->encrypt(buffer, size); -#ifdef TORRENT_DEBUG - m_encrypted_bytes += size; - TORRENT_ASSERT(m_encrypted_bytes == send_buffer_size() + size); -#endif - } -#endif - peer_connection::append_send_buffer(buffer, size, destructor, true); - } - void setup_send(); - -private: - - void encrypt_pending_buffer(); - - // 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 - - 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 - - std::string m_client_version; - - // state of on_receive - state m_state; - - static const message_handler m_message_handler[num_supported_messages]; - - // 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_EXTENSIONS - // the message ID for upload only message - // 0 if not supported - int m_upload_only_id; - - // the message ID for holepunch messages - int m_holepunch_id; - - // the message ID for share mode message - // 0 if not supported - int m_share_mode_id; - - char m_reserved_bits[8]; - // this is set to true if the handshake from - // the peer indicated that it supports the - // extension protocol - bool m_supports_extensions:1; -#endif - bool m_supports_dht_port:1; - bool m_supports_fast:1; - -#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; - - // true if rc4, false if plaintext - bool m_rc4_encrypted; - - // used to disconnect peer if sync points are not found within - // the maximum number of bytes - int m_sync_bytes_read; - - // hold information about latest allocated send buffer - // need to check for non zero (begin, end) for operations with this - buffer::interval m_enc_send_buffer; - - // initialized during write_pe1_2_dhkey, and destroyed on - // creation of m_RC4_handler. Cannot reinitialize once - // initialized. - boost::scoped_ptr m_dh_key_exchange; - - // if RC4 is negotiated, this is used for - // encryption/decryption during the entire session. Destroyed - // if plaintext is selected - boost::scoped_ptr m_RC4_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; -#endif // #ifndef TORRENT_DISABLE_ENCRYPTION - -#ifdef TORRENT_DEBUG - // this is set to true when the client's - // bitfield is sent to this peer - bool m_sent_bitfield; - - bool m_in_constructor; - - bool m_sent_handshake; - - // the number of bytes in the send buffer - // that have been encrypted (only used for - // encrypted connections) -public: - int m_encrypted_bytes; -#endif - - }; -} - -#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/buffer.hpp b/libtorrent_utp/include/libtorrent/buffer.hpp deleted file mode 100644 index 33d8b6ba5..000000000 --- a/libtorrent_utp/include/libtorrent/buffer.hpp +++ /dev/null @@ -1,208 +0,0 @@ -/* -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 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); return 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); return 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()); - } - - buffer& operator=(buffer const& b) - { - if (&b == this) return *this; - resize(b.size()); - 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/libtorrent_utp/include/libtorrent/build_config.hpp b/libtorrent_utp/include/libtorrent/build_config.hpp deleted file mode 100644 index a7b596b54..000000000 --- a/libtorrent_utp/include/libtorrent/build_config.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - -Copyright (c) 2010, 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 - -#ifdef TORRENT_DEBUG -#define TORRENT_CFG_DEBUG dbg_ -#else -#define TORRENT_CFG_DEBUG rel_ -#endif - -#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_DISABLE_DHT -#define TORRENT_CFG_DHT nodht_ -#else -#define TORRENT_CFG_DHT dht_ -#endif - -#ifdef TORRENT_DISABLE_POOL_ALLOCATORS -#define TORRENT_CFG_POOL nopools_ -#else -#define TORRENT_CFG_POOL pools_ -#endif - -#ifdef TORRENT_VERBOSE_LOGGING -#define TORRENT_CFG_LOG verboselog_ -#elif defined TORRENT_LOGGING -#define TORRENT_CFG_LOG log_ -#else -#define TORRENT_CFG_LOG nolog_ -#endif - -#ifdef _UNICODE -#define TORRENT_CFG_UNICODE unicode_ -#else -#define TORRENT_CFG_UNICODE ansi_ -#endif - -#ifdef TORRENT_DISABLE_RESOLVE_COUNTRIES -#define TORRENT_CFG_RESOLVE noresolvecountries_ -#else -#define TORRENT_CFG_RESOLVE resolvecountries_ -#endif - -#ifdef TORRENT_NO_DEPRECATE -#define TORRENT_CFG_DEPR nodeprecate_ -#else -#define TORRENT_CFG_DEPR deprecated_ -#endif - -#ifdef TORRENT_DISABLE_FULL_STATS -#define TORRENT_CFG_STATS partialstats_ -#else -#define TORRENT_CFG_STATS fullstats_ -#endif - -#ifdef TORRENT_DISABLE_EXTENSIONS -#define TORRENT_CFG_EXT noext_ -#else -#define TORRENT_CFG_EXT ext_ -#endif - -#define TORRENT_CFG \ - BOOST_PP_CAT(TORRENT_CFG_DEBUG, \ - BOOST_PP_CAT(TORRENT_CFG_TIME, \ - BOOST_PP_CAT(TORRENT_CFG_POOL, \ - BOOST_PP_CAT(TORRENT_CFG_LOG, \ - BOOST_PP_CAT(TORRENT_CFG_RESOLVE, \ - BOOST_PP_CAT(TORRENT_CFG_DEPR, \ - BOOST_PP_CAT(TORRENT_CFG_DHT, \ - TORRENT_CFG_EXT))))))) - -#define TORRENT_CFG_STRING BOOST_PP_STRINGIZE(TORRENT_CFG) - -#endif - diff --git a/libtorrent_utp/include/libtorrent/chained_buffer.hpp b/libtorrent_utp/include/libtorrent/chained_buffer.hpp deleted file mode 100644 index aa46b2d36..000000000 --- a/libtorrent_utp/include/libtorrent/chained_buffer.hpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - -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_CHAINED_BUFFER_HPP_INCLUDED -#define TORRENT_CHAINED_BUFFER_HPP_INCLUDED - -#include -#include -#if BOOST_VERSION < 103500 -#include -#else -#include -#endif -#include -#include - -namespace libtorrent -{ -#if BOOST_VERSION >= 103500 - namespace asio = boost::asio; -#endif - struct chained_buffer - { - chained_buffer(): m_bytes(0), m_capacity(0) {} - - struct buffer_t - { - boost::function free; // destructs the buffer - char* buf; // the first byte of the buffer - int size; // the total size of the buffer - - char* start; // the first byte to send/receive in 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) - { - 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(); - } - } - - template - void append_buffer(char* buffer, int s, int used_size, D 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 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 - bool append(char const* buf, int s) - { - char* insert = allocate_appendix(s); - if (insert == 0) return false; - std::memcpy(insert, buf, s); - return true; - } - - // tries to allocate memory from the end - // of the last buffer. If there isn't - // enough room, returns 0 - char* 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& 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() - { - for (std::list::iterator i = m_vec.begin() - , end(m_vec.end()); i != end; ++i) - { - i->free(i->buf); - } - } - - private: - - // this is the list of all the buffers we want to - // send - std::list m_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; - - // this is the vector of buffers used when - // invoking the async write call - std::list m_tmp_vec; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/config.hpp b/libtorrent_utp/include/libtorrent/config.hpp deleted file mode 100644 index 6db6be482..000000000 --- a/libtorrent_utp/include/libtorrent/config.hpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - -Copyright (c) 2005, 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 - -#include -#include -#include // for snprintf - -#if defined TORRENT_DEBUG_BUFFERS && !defined TORRENT_DISABLE_POOL_ALLOCATOR -#error TORRENT_DEBUG_BUFFERS only works if you also disable pool allocators -#endif - -#ifndef WIN32 -#define __STDC_FORMAT_MACROS -#include -#endif - -#ifndef PRId64 -#ifdef _WIN32 -#define PRId64 "I64d" -#else -#define PRId64 "lld" -#endif -#endif - - -// ======= GCC ========= - -#if defined __GNUC__ - -# if __GNUC__ >= 3 -# define TORRENT_DEPRECATED __attribute__ ((deprecated)) -# endif - -// GCC pre 4.0 did not have support for the visibility attribute -# if __GNUC__ >= 4 -# if defined(TORRENT_BUILDING_SHARED) || defined(TORRENT_LINKING_SHARED) -# define TORRENT_EXPORT __attribute__ ((visibility("default"))) -# endif -# endif - - -// ======= SUNPRO ========= - -#elif defined __SUNPRO_CC - -# if __SUNPRO_CC >= 0x550 -# if defined(TORRENT_BUILDING_SHARED) || defined(TORRENT_LINKING_SHARED) -# define TORRENT_EXPORT __global -# endif -# endif - -// 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) - -# if defined(TORRENT_BUILDING_SHARED) -# define TORRENT_EXPORT __declspec(dllexport) -# elif defined(TORRENT_LINKING_SHARED) -# define TORRENT_EXPORT __declspec(dllimport) -# endif - -#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 -#define TORRENT_USE_ICONV 0 - -// ==== 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__ -#define TORRENT_USE_ICONV 0 -#endif -#define TORRENT_HAS_FALLOCATE 0 - -// ==== LINUX === -#elif defined __linux__ -#define TORRENT_LINUX - -// ==== MINGW === -#elif defined __MINGW32__ -#define TORRENT_MINGW -#define TORRENT_WINDOWS -#define TORRENT_USE_ICONV 0 -#define TORRENT_USE_RLIMIT 0 - -// ==== WINDOWS === -#elif defined WIN32 -#define TORRENT_WINDOWS -// windows has its own functions to convert -// apple uses utf-8 as its locale, so no conversion -// is necessary -#define TORRENT_USE_ICONV 0 -#define TORRENT_USE_RLIMIT 0 -#define TORRENT_HAS_FALLOCATE 0 - -// ==== SOLARIS === -#elif defined sun || defined __sun -#define TORRENT_SOLARIS -#define TORRENT_COMPLETE_TYPES_REQUIRED 1 - -// ==== BEOS === -#elif defined __BEOS__ || defined __HAIKU__ -#define TORRENT_BEOS -#include // B_PATH_NAME_LENGTH -#define TORRENT_HAS_FALLOCATE 0 -#define TORRENT_USE_MLOCK 0 -#define TORRENT_USE_ICONV 0 -#if __GNUCC__ == 2 -# if defined(TORRENT_BUILDING_SHARED) -# define TORRENT_EXPORT __declspec(dllexport) -# elif defined(TORRENT_LINKING_SHARED) -# define TORRENT_EXPORT __declspec(dllimport) -# endif -#endif -#else -#warning unknown OS, assuming BSD -#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 -#warning unknown platform, assuming the longest path is 255 - -#endif - -#if defined TORRENT_WINDOWS && !defined TORRENT_MINGW - -// 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) - -#include - -inline int snprintf(char* buf, int len, char const* fmt, ...) -{ - va_list lp; - va_start(lp, fmt); - int 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 - -// libiconv presence, not implemented yet -#ifndef TORRENT_USE_ICONV -#define TORRENT_USE_ICONV 1 -#endif - -#ifndef TORRENT_BROKEN_UNIONS -#define TORRENT_BROKEN_UNIONS 0 -#endif - -#if defined UNICODE && !defined BOOST_NO_STD_WSTRING -#define TORRENT_USE_WSTRING 1 -#else -#define TORRENT_USE_WSTRING 0 -#endif // UNICODE - -#ifndef TORRENT_HAS_FALLOCATE -#define TORRENT_HAS_FALLOCATE 1 -#endif - -#ifndef TORRENT_EXPORT -# define TORRENT_EXPORT -#endif - -#ifndef TORRENT_DEPRECATED_PREFIX -#define TORRENT_DEPRECATED_PREFIX -#endif - -#ifndef TORRENT_DEPRECATED -#define TORRENT_DEPRECATED -#endif - -#ifndef TORRENT_COMPLETE_TYPES_REQUIRED -#define TORRENT_COMPLETE_TYPES_REQUIRED 0 -#endif - -#ifndef TORRENT_USE_RLIMIT -#define TORRENT_USE_RLIMIT 1 -#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 - -#if !defined TORRENT_USE_IOSTREAM && !defined BOOST_NO_IOSTREAM -#define TORRENT_USE_IOSTREAM 1 -#else -#define TORRENT_USE_IOSTREAM 0 -#endif - -#ifndef TORRENT_USE_I2P -#define TORRENT_USE_I2P 1 -#endif - -#if !defined(TORRENT_READ_HANDLER_MAX_SIZE) -# define TORRENT_READ_HANDLER_MAX_SIZE 256 -#endif - -#if !defined(TORRENT_WRITE_HANDLER_MAX_SIZE) -# define TORRENT_WRITE_HANDLER_MAX_SIZE 256 -#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(__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 - -#endif // TORRENT_CONFIG_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/connection_queue.hpp b/libtorrent_utp/include/libtorrent/connection_queue.hpp deleted file mode 100644 index 7192e59f8..000000000 --- a/libtorrent_utp/include/libtorrent/connection_queue.hpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - -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_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_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); - void done(int ticket); - void limit(int limit); - int limit() const; - void close(); - int size() const { return m_queue.size(); } - -#ifdef TORRENT_DEBUG - 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(): connecting(false), ticket(0), expires(max_time()), priority(0) {} - // called when the connection is initiated - boost::function on_connect; - // called if done hasn't been called within the timeout - boost::function on_timeout; - bool connecting; - int ticket; - ptime expires; - time_duration timeout; - int 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; - - 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/libtorrent_utp/include/libtorrent/copy_ptr.hpp b/libtorrent_utp/include/libtorrent/copy_ptr.hpp deleted file mode 100644 index 0b2fcadd2..000000000 --- a/libtorrent_utp/include/libtorrent/copy_ptr.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - -Copyright (c) 2010, 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/libtorrent_utp/include/libtorrent/create_torrent.hpp b/libtorrent_utp/include/libtorrent/create_torrent.hpp deleted file mode 100644 index 575db15c2..000000000 --- a/libtorrent_utp/include/libtorrent/create_torrent.hpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - -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_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/file_pool.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 - -namespace libtorrent -{ - class torrent_info; - - struct TORRENT_EXPORT create_torrent - { - enum { - optimize = 1 - , merkle = 2 - , modification_time = 4 - , symlinks = 8 - , calculate_file_hashes = 16 - }; - - create_torrent(file_storage& fs, int piece_size = 0 - , int pad_file_limit = -1, int flags = optimize); - create_torrent(torrent_info const& ti); - entry generate() const; - - file_storage const& files() const { return m_files; } - - void set_comment(char const* str); - void set_creator(char const* str); - void set_hash(int index, sha1_hash const& h); - void set_file_hash(int index, sha1_hash const& h); - void add_url_seed(std::string const& url); - void add_http_seed(std::string const& url); - void add_node(std::pair const& node); - void add_tracker(std::string const& url, int tier = 0); - void set_priv(bool p) { m_private = p; } - - int num_pieces() const { return m_files.num_pieces(); } - int piece_length() const { return m_files.piece_length(); } - int piece_size(int i) const { return m_files.piece_size(i); } - bool priv() const { return m_private; } - - bool should_add_file_hashes() const { return m_calculate_file_hashes; } - - 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; - - // 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 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 i) {} - - int TORRENT_EXPORT get_file_attributes(std::string const& p); - std::string TORRENT_EXPORT get_symlink_path(std::string const& p); - - template - void add_files_impl(file_storage& fs, std::string const& p - , std::string const& l, Pred 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); - } - } - } - } - - 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); - } - - 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; - }; - - template - void set_piece_hashes(create_torrent& t, std::string const& p, Fun f - , error_code& ec) - { - file_pool fp; - boost::scoped_ptr st( - default_storage_constructor(const_cast(t.files()), 0, p, 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); - } - } - -#ifndef BOOST_NO_EXCEPTIONS - 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); - } - - 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); - } -#endif - - inline void set_piece_hashes(create_torrent& t, std::string const& p, error_code& ec) - { - set_piece_hashes(t, p, detail::nop, ec); - } - -#if TORRENT_USE_WSTRING - // wstring versions - - template - void 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); - } - - inline void 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); - } - - template - void set_piece_hashes(create_torrent& t, std::wstring const& p, Fun f - , error_code& ec) - { - file_pool fp; - std::string utf8; - wchar_utf8(p, utf8); - 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); - } - } - -#ifndef BOOST_NO_EXCEPTIONS - template - void 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); - } - - inline void 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 - - inline void set_piece_hashes(create_torrent& t, std::wstring const& p, error_code& ec) - { - set_piece_hashes(t, p, detail::nop, ec); - } - -#endif // TORRENT_USE_WPATH - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/deadline_timer.hpp b/libtorrent_utp/include/libtorrent/deadline_timer.hpp deleted file mode 100644 index 3cfa4ed8f..000000000 --- a/libtorrent_utp/include/libtorrent/deadline_timer.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/debug.hpp b/libtorrent_utp/include/libtorrent/debug.hpp deleted file mode 100644 index edc5f5033..000000000 --- a/libtorrent_utp/include/libtorrent/debug.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - -Copyright (c) 2003, 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 -#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; - - inline void add_outstanding_async(char const* name) - { - async_t& a = _async_ops[name]; - if (a.stack.empty()) - { - void* stack[50]; - int size = backtrace(stack, 50); - char** symbols = backtrace_symbols(stack, size); - - for (int i = 1; i < size; ++i) - { - char str[200]; - snprintf(str, sizeof(str), "%d: %s\n", i, demangle(symbols[i]).c_str()); - a.stack += str; - } - - free(symbols); - } - ++a.refs; - } - - inline void complete_async(char const* name) - { - async_t& a = _async_ops[name]; - TORRENT_ASSERT(a.refs > 0); - --a.refs; - } - - inline int log_async() - { - int ret = 0; - for (std::map::iterator i = _async_ops.begin() - , end(_async_ops.end()); i != end; ++i) - { - if (i->second.refs <= 0) 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 -#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(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(logpath, log_name))); - error_code ec; - if (!exists(dir)) create_directories(dir, ec); - m_filename = combine_path(dir, filename); - - mutex::scoped_lock l(file_mutex); - open(!append); - log_file << "\n\n\n*** starting log ***\n"; - } - -#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/libtorrent_utp/include/libtorrent/disk_buffer_holder.hpp b/libtorrent_utp/include/libtorrent/disk_buffer_holder.hpp deleted file mode 100644 index d5536cedf..000000000 --- a/libtorrent_utp/include/libtorrent/disk_buffer_holder.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - -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_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; - - struct TORRENT_EXPORT disk_buffer_holder - { - disk_buffer_holder(aux::session_impl& ses, char* buf); - disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf); - disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf, int num_blocks); - ~disk_buffer_holder(); - char* release(); - char* get() const { return m_buf; } - void reset(char* buf = 0, int num_blocks = 1); - 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)(); - operator unspecified_bool_type() const - { return m_buf == 0? 0: &disk_buffer_holder::release; } - - private: - disk_buffer_pool& m_disk_pool; - char* m_buf; - int m_num_blocks; - }; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/disk_io_thread.hpp b/libtorrent_utp/include/libtorrent/disk_io_thread.hpp deleted file mode 100644 index b32f26d14..000000000 --- a/libtorrent_utp/include/libtorrent/disk_io_thread.hpp +++ /dev/null @@ -1,473 +0,0 @@ -/* - -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_DISK_IO_THREAD -#define TORRENT_DISK_IO_THREAD - -#if defined TORRENT_DISK_STATS || defined TORRENT_STATS -#include -#endif - -#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" -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR -#include -#endif -#include "libtorrent/session_settings.hpp" -#include "libtorrent/thread.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 - { - int piece; - std::vector blocks; - ptime last_use; - enum kind_t { read_cache = 0, write_cache = 1 }; - kind_t kind; - }; - - struct disk_io_job - { - disk_io_job() - : action(read) - , buffer(0) - , buffer_size(0) - , piece(0) - , offset(0) - , max_cache_line(0) - , cache_min_time(0) - {} - - 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 - , finalize_file - }; - - action_t action; - - char* buffer; - int buffer_size; - boost::intrusive_ptr storage; - // arguments used for read and write - int piece, offset; - // 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; - - // 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::shared_ptr resume_data; - - // the error code from the file operation - error_code error; - - // this is called when operation completes - boost::function callback; - - // the time when this job was issued. This is used to - // keep track of disk I/O congestion - ptime start_time; - }; - - // 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); - - struct cache_status - { - cache_status() - : blocks_written(0) - , writes(0) - , blocks_read(0) - , blocks_read_hit(0) - , reads(0) - , cache_size(0) - , read_cache_size(0) - , total_used_buffers(0) - , average_queue_time(0) - , average_read_time(0) - , job_queue_length(0) - {} - - // the number of 16kB blocks written - size_type blocks_written; - // the number of write operations used - size_type writes; - // (blocks_written - writes) / blocks_written represents the - // "cache hit" ratio in the write cache - // the number of blocks read - - // the number of blocks passed back to the bittorrent engine - size_type blocks_read; - // the number of blocks that was just copied from the read cache - size_type blocks_read_hit; - // the number of read operations used - size_type reads; - - mutable size_type queued_bytes; - - // the number of blocks in the cache (both read and write) - int cache_size; - - // the number of blocks in the cache used for read cache - int read_cache_size; - - // the total number of blocks that are currently in use - // this includes send and receive buffers - mutable int total_used_buffers; - - // times in microseconds - int average_queue_time; - int average_read_time; - int job_queue_length; - }; - - struct TORRENT_EXPORT disk_buffer_pool : boost::noncopyable - { - disk_buffer_pool(int block_size); -#ifdef TORRENT_DEBUG - ~disk_buffer_pool(); -#endif - -#if defined TORRENT_DEBUG || defined 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); - - char* allocate_buffers(int blocks, char const* category); - void free_buffers(char* buf, int blocks); - - 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 - // 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: - std::map m_categories; - std::map m_buf_to_category; - std::ofstream m_log; - private: -#endif -#ifdef TORRENT_DEBUG - int m_magic; -#endif - }; - - // this is a singleton consisting of the thread and a queue - // of disk io jobs - struct TORRENT_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); - void 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; - - void get_cache_info(sha1_hash const& ih - , std::vector& ret) const; - - cache_status status() const; - - void thread_fun(); - -#ifdef TORRENT_DEBUG - 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 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; - // the pointers to the block data - boost::shared_array blocks; - - 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: - - void 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(boost::function const& handler - , 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); - - // 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, int ignore = -1, int options = 0); - void flush_expired_pieces(); - int flush_contiguous_blocks(cached_piece_entry& p - , mutex::scoped_lock& l, int lower_limit = 0); - 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, int 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); - int try_read_from_cache(disk_io_job const& j, bool& hit); - 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 - // and m_abort - mutable mutex m_queue_mutex; - event m_signal; - bool m_abort; - bool m_waiting_to_shutdown; - std::list 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; - - // 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) - sliding_average<512> m_queue_time; - - // average read time for cache misses (in microseconds) - sliding_average<512> m_read_time; - - 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; - - 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; - - // thread for performing blocking disk io operations - thread m_disk_io_thread; - }; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/entry.hpp b/libtorrent_utp/include/libtorrent/entry.hpp deleted file mode 100644 index 6cd7b0bcd..000000000 --- a/libtorrent_utp/include/libtorrent/entry.hpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - -Copyright (c) 2003, 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; - - struct TORRENT_EXPORT type_error: std::runtime_error - { - type_error(const char* error): std::runtime_error(error) {} - }; - - class entry; - - 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; - - enum data_type - { - int_t, - string_t, - list_t, - dictionary_t, - undefined_t - }; - - data_type type() const; - - entry(dictionary_type const&); - entry(string_type const&); - entry(list_type const&); - entry(integer_type const&); - - entry(); - entry(data_type t); - entry(entry const& e); - ~entry(); - - bool operator==(entry const& e) const; - - 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&); - - 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; - - void swap(entry& e); - - // these functions requires that the entry - // is a dictionary, otherwise they will throw - 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 - 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; - -#if (defined TORRENT_VERBOSE_LOGGING || defined TORRENT_DEBUG) && TORRENT_USE_IOSTREAM - void print(std::ostream& os, int indent = 0) const; -#endif - - protected: - - void construct(data_type t); - void copy(const entry& e); - void destruct(); - - private: - -#ifndef TORRENT_DEBUG - data_type m_type; -#else - // 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 - data_type m_type:31; - - 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 bool m_type_queried:1; - protected: -#endif // TORRENT_DEBUG - -#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)]; - }; - -#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM - inline std::ostream& operator<<(std::ostream& os, const entry& e) - { - e.print(os, 0); - return os; - } -#endif - -#ifndef BOOST_NO_EXCEPTIONS - 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/libtorrent_utp/include/libtorrent/enum_net.hpp b/libtorrent_utp/include/libtorrent/enum_net.hpp deleted file mode 100644 index 69937d44c..000000000 --- a/libtorrent_utp/include/libtorrent/enum_net.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - -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_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_EXPORT std::vector enum_net_interfaces(io_service& ios - , error_code& ec); - - TORRENT_EXPORT std::vector enum_routes(io_service& ios, error_code& ec); - - // return (a1 & mask) == (a2 & mask) - TORRENT_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_EXPORT bool in_local_network(io_service& ios, address const& addr - , error_code& ec); - - TORRENT_EXPORT address get_default_gateway(io_service& ios - , error_code& ec); -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/error.hpp b/libtorrent_utp/include/libtorrent/error.hpp deleted file mode 100644 index 1ee4b27fd..000000000 --- a/libtorrent_utp/include/libtorrent/error.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/error_code.hpp b/libtorrent_utp/include/libtorrent/error_code.hpp deleted file mode 100644 index 813c8936f..000000000 --- a/libtorrent_utp/include/libtorrent/error_code.hpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - -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_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 // strdup -#include // free - -namespace libtorrent -{ - - namespace errors - { - enum error_code_enum - { - no_error = 0, - file_collision, - failed_hash_check, - torrent_is_no_dict, - torrent_missing_info, - torrent_info_no_dict, - torrent_missing_piece_length, - torrent_missing_name, - torrent_invalid_name, - torrent_invalid_length, - torrent_file_parse_failed, - torrent_missing_pieces, - torrent_invalid_hashes, - too_many_pieces_in_torrent, - invalid_swarm_metadata, - invalid_bencoding, - no_files_in_torrent, - invalid_escaped_string, - session_is_closing, - duplicate_torrent, - invalid_torrent_handle, - invalid_entry_type, - missing_info_hash_in_uri, - file_too_short, - unsupported_url_protocol, - url_parse_error, - peer_sent_empty_piece, - parse_failed, - invalid_file_tag, - missing_info_hash, - mismatching_info_hash, - invalid_hostname, - invalid_port, - port_blocked, - expected_close_bracket_in_address, - destructing_torrent, - timed_out, - upload_upload_connection, - uninteresting_upload_peer, - invalid_info_hash, - torrent_paused, - invalid_have, - invalid_bitfield_size, - too_many_requests_when_choked, - invalid_piece, - no_memory, - torrent_aborted, - self_connection, - invalid_piece_size, - timed_out_no_interest, - timed_out_inactivity, - timed_out_no_handshake, - timed_out_no_request, - invalid_choke, - invalid_unchoke, - invalid_interested, - invalid_not_interested, - invalid_request, - invalid_hash_list, - invalid_hash_piece, - invalid_cancel, - invalid_dht_port, - invalid_suggest, - invalid_have_all, - invalid_have_none, - invalid_reject, - invalid_allow_fast, - invalid_extended, - invalid_message, - sync_hash_not_found, - invalid_encryption_constant, - no_plaintext_mode, - no_rc4_mode, - unsupported_encryption_mode, - unsupported_encryption_mode_selected, - invalid_pad_size, - invalid_encrypt_handshake, - no_incoming_encrypted, - no_incoming_regular, - duplicate_peer_id, - torrent_removed, - packet_too_large, - reserved, - http_error, - missing_location, - invalid_redirection, - redirecting, - invalid_range, - no_content_length, - banned_by_ip_filter, - too_many_connections, - peer_banned, - stopping_torrent, - too_many_corrupt_pieces, - torrent_not_ready, - peer_not_constructed, - session_closing, - optimistic_disconnect, - torrent_finished, - no_router, - 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, - too_frequent_pex, - reserved109, - reserved110, - reserved111, - reserved112, - reserved113, - reserved114, - reserved115, - reserved116, - reserved117, - reserved118, - reserved119, - -// natpmp errors - unsupported_protocol_version, // 120 - natpmp_not_authorized, - network_failure, - no_resources, - unsupported_opcode, - reserved125, - reserved126, - reserved127, - reserved128, - reserved129, - -// fastresume errors - missing_file_sizes, // 130 - no_files_in_resume_data, - missing_pieces, - mismatching_number_of_files, - mismatching_file_size, - mismatching_file_timestamp, - not_a_dictionary, - invalid_blocks_per_piece, - missing_slots, - too_many_slots, - invalid_slot_list, - invalid_piece_index, - pieces_need_reorder, - reserved143, - reserved144, - reserved145, - reserved146, - reserved147, - reserved148, - reserved149, -// HTTP errors - http_parse_error, // 150 - http_missing_location, - http_failed_decompress, - reserved153, - reserved154, - reserved155, - reserved156, - reserved157, - reserved158, - reserved159, -// i2p errors - no_i2p_router, // 160 - reserved161, - reserved162, - reserved163, - reserved164, - reserved165, - reserved166, - reserved167, - reserved168, - reserved169, - -// tracker errors - scrape_not_available, // 170 - invalid_tracker_response, - invalid_peer_dict, - tracker_failure, - invalid_files_entry, - invalid_hash_entry, - invalid_peers_entry, - invalid_tracker_response_length, - invalid_tracker_transaction_id, - invalid_tracker_action, - reserved180, - reserved181, - reserved182, - reserved183, - reserved184, - reserved185, - reserved186, - reserved187, - reserved188, - reserved189, - -// bdecode errors - expected_string, // 190 - expected_colon, - unexpected_eof, - expected_value, - depth_exceeded, - limit_exceeded, - - error_code_max - }; - } -} - -#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 - -namespace libtorrent -{ - -#if BOOST_VERSION < 103500 - typedef asio::error_code error_code; - inline asio::error::error_category get_posix_category() { return asio::error::system_category; } - inline asio::error::error_category get_system_category() { return asio::error::system_category; } - - boost::system::error_category const& get_libtorrent_category() - { - static ::asio::error::error_category libtorrent_category(20); - return libtorrent_category; - } - -#else - - struct TORRENT_EXPORT libtorrent_error_category : boost::system::error_category - { - virtual const char* name() const; - virtual std::string message(int ev) const; - virtual boost::system::error_condition default_error_condition(int ev) const - { return boost::system::error_condition(ev, *this); } - }; - - inline boost::system::error_category& get_libtorrent_category() - { - static libtorrent_error_category libtorrent_category; - return libtorrent_category; - } - - namespace errors - { - inline boost::system::error_code make_error_code(error_code_enum e) - { - return boost::system::error_code(e, get_libtorrent_category()); - } - } - - using boost::system::error_code; - inline boost::system::error_category const& get_system_category() - { return boost::system::get_system_category(); } - inline boost::system::error_category const& get_posix_category() -#if BOOST_VERSION < 103600 - { return boost::system::get_posix_category(); } -#else - { return boost::system::get_generic_category(); } -#endif // BOOST_VERSION < 103600 -#endif // BOOST_VERSION < 103500 - -#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() - { - if (!m_msg) - { - std::string msg = m_error.message(); - m_msg = strdup(msg.c_str()); - } - - return m_msg; - } - virtual ~libtorrent_exception() throw() { free(m_msg); } - error_code error() const { return m_error; } - private: - error_code m_error; - mutable char* m_msg; - }; -#endif -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/escape_string.hpp b/libtorrent_utp/include/libtorrent/escape_string.hpp deleted file mode 100644 index 0d310fb77..000000000 --- a/libtorrent_utp/include/libtorrent/escape_string.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - -Copyright (c) 2003, 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_EXPORT boost::array::digits10> to_string(size_type n); - TORRENT_EXPORT bool is_alpha(char c); - TORRENT_EXPORT bool is_digit(char c); - TORRENT_EXPORT bool is_print(char c); - TORRENT_EXPORT bool is_space(char c); - TORRENT_EXPORT char to_lower(char c); - - TORRENT_EXPORT int split_string(char const** tags, int buf_size, char* in); - TORRENT_EXPORT bool string_begins_no_case(char const* s1, char const* s2); - TORRENT_EXPORT bool string_equal_no_case(char const* s1, char const* s2); - - TORRENT_EXPORT void url_random(char* begin, char* end); - - TORRENT_EXPORT std::string unescape_string(std::string const& s, error_code& ec); - // replaces all disallowed URL characters by their %-encoding - TORRENT_EXPORT std::string escape_string(const char* str, int len); - // same as escape_string but does not encode '/' - TORRENT_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_EXPORT std::string maybe_url_encode(std::string const& url); - - TORRENT_EXPORT bool need_encoding(char const* str, int len); - - // encodes a string using the base64 scheme - TORRENT_EXPORT std::string base64encode(std::string const& s); - TORRENT_EXPORT std::string base64decode(std::string const& s); - // encodes a string using the base32 scheme - TORRENT_EXPORT std::string base32encode(std::string const& s); - TORRENT_EXPORT std::string base32decode(std::string const& s); - - TORRENT_EXPORT std::string url_has_argument( - std::string const& url, std::string argument, std::string::size_type* out_pos = 0); - - // replaces \ with / - TORRENT_EXPORT void convert_path_to_posix(std::string& path); - - TORRENT_EXPORT std::string read_until(char const*& str, char delim, char const* end); - TORRENT_EXPORT std::string to_hex(std::string const& s); - TORRENT_EXPORT bool is_hex(char const *in, int len); - TORRENT_EXPORT void to_hex(char const *in, int len, char* out); - TORRENT_EXPORT bool from_hex(char const *in, int len, char* out); - -#if defined TORRENT_WINDOWS && TORRENT_USE_WSTRING - TORRENT_EXPORT std::wstring convert_to_wstring(std::string const& s); - TORRENT_EXPORT std::string convert_from_wstring(std::wstring const& s); -#endif - -#if defined TORRENT_WINDOWS || TORRENT_USE_ICONV - TORRENT_EXPORT std::string convert_to_native(std::string const& s); - TORRENT_EXPORT std::string convert_from_native(std::string const& s); -#else - inline std::string const& convert_to_native(std::string const& s) { return s; } - inline std::string const& convert_from_native(std::string const& s) { return s; } -#endif -} - -#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions.hpp b/libtorrent_utp/include/libtorrent/extensions.hpp deleted file mode 100644 index 10294ece6..000000000 --- a/libtorrent_utp/include/libtorrent/extensions.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - -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_EXTENSIONS_HPP_INCLUDED -#define TORRENT_EXTENSIONS_HPP_INCLUDED - -#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" - -namespace libtorrent -{ - struct peer_plugin; - class bt_peer_connection; - struct peer_request; - class peer_connection; - class entry; - struct lazy_entry; - struct disk_buffer_holder; - struct bitfield; - - struct TORRENT_EXPORT torrent_plugin - { - virtual ~torrent_plugin() {} - // throwing an exception closes the connection - // returning a 0 pointer is valid and will not add - // the peer_plugin to the peer_connection - virtual boost::shared_ptr new_connection(peer_connection*) - { return boost::shared_ptr(); } - - virtual void on_piece_pass(int index) {} - virtual void on_piece_failed(int index) {} - - // called aproximately once every second - virtual void tick() {} - - // if true is returned, it means the handler handled the event, - // and no other plugins will have their handlers called, and the - // default behavior will be skipped - virtual bool on_pause() { return false; } - virtual bool on_resume() { return false;} - - // this is called when the initial checking of - // files is completed. - virtual void on_files_checked() {} - }; - - struct TORRENT_EXPORT peer_plugin - { - virtual ~peer_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&) {} - - // 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& 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() - { 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_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& req) - { return false; } - - virtual bool on_piece(peer_request const& piece, disk_buffer_holder& data) - { return false; } - - virtual bool on_cancel(peer_request const& req) - { return false; } - - virtual bool on_reject(peer_request const& req) - { return false; } - - virtual bool on_suggest(int index) - { 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 - // this is not called for web seeds - 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& r) { return false; } - }; - -} - -#endif - -#endif // TORRENT_EXTENSIONS_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions/logger.hpp b/libtorrent_utp/include/libtorrent/extensions/logger.hpp deleted file mode 100644 index 5e5e6cb3a..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/logger.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - -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/libtorrent_utp/include/libtorrent/extensions/lt_trackers.hpp b/libtorrent_utp/include/libtorrent/extensions/lt_trackers.hpp deleted file mode 100644 index bc63cee83..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/lt_trackers.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - -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 - -#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; - boost::shared_ptr TORRENT_EXPORT create_lt_trackers_plugin(torrent*, void*); -} - -#endif // TORRENT_LT_TRACKERS_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions/metadata_transfer.hpp b/libtorrent_utp/include/libtorrent/extensions/metadata_transfer.hpp deleted file mode 100644 index c42136d70..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/metadata_transfer.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - -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 - -#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; - TORRENT_EXPORT boost::shared_ptr create_metadata_plugin(torrent*, void*); -} - -#endif // TORRENT_METADATA_TRANSFER_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions/smart_ban.hpp b/libtorrent_utp/include/libtorrent/extensions/smart_ban.hpp deleted file mode 100644 index 5d7d30c82..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/smart_ban.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - -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 - -#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; - TORRENT_EXPORT boost::shared_ptr create_smart_ban_plugin(torrent*, void*); -} - -#endif // TORRENT_SMART_BAN_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions/ut_metadata.hpp b/libtorrent_utp/include/libtorrent/extensions/ut_metadata.hpp deleted file mode 100644 index 91437b17c..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/ut_metadata.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - -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 - -#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; - TORRENT_EXPORT boost::shared_ptr create_ut_metadata_plugin(torrent*, void*); -} - -#endif // TORRENT_UT_METADATA_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/extensions/ut_pex.hpp b/libtorrent_utp/include/libtorrent/extensions/ut_pex.hpp deleted file mode 100644 index ebf6aa834..000000000 --- a/libtorrent_utp/include/libtorrent/extensions/ut_pex.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - -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 - -#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; - TORRENT_EXPORT boost::shared_ptr create_ut_pex_plugin(torrent*, void*); -} - -#endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED diff --git a/libtorrent_utp/include/libtorrent/file.hpp b/libtorrent_utp/include/libtorrent/file.hpp deleted file mode 100644 index 8a45a5b03..000000000 --- a/libtorrent_utp/include/libtorrent/file.hpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - -Copyright (c) 2003, 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 -#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 -#include // for DIR - -#undef _FILE_OFFSET_BITS - -#endif - -namespace libtorrent -{ - struct file_status - { - size_type file_size; - time_t atime; - time_t mtime; - time_t ctime; - enum { -#if defined TORRENT_WINDOWS - directory = _S_IFDIR, - regular_file = _S_IFREG -#else - fifo = S_IFIFO, - character_special = S_IFCHR, - directory = S_IFDIR, - block_special = S_IFBLK, - regular_file = S_IFREG, - link = S_IFLNK, - socket = S_IFSOCK -#endif - } modes_t; - int mode; - }; - - enum stat_flags_t { dont_follow_links = 1 }; - TORRENT_EXPORT void stat_file(std::string const& f, file_status* s - , error_code& ec, int flags = 0); - TORRENT_EXPORT void rename(std::string const& f - , std::string const& newf, error_code& ec); - TORRENT_EXPORT void create_directories(std::string const& f - , error_code& ec); - TORRENT_EXPORT void create_directory(std::string const& f - , error_code& ec); - TORRENT_EXPORT void remove_all(std::string const& f - , error_code& ec); - TORRENT_EXPORT void remove(std::string const& f, error_code& ec); - TORRENT_EXPORT bool exists(std::string const& f); - TORRENT_EXPORT size_type file_size(std::string const& f); - TORRENT_EXPORT bool is_directory(std::string const& f - , error_code& ec); - TORRENT_EXPORT void copy_file(std::string const& f - , std::string const& newf, error_code& ec); - - TORRENT_EXPORT std::string split_path(std::string const& f); - TORRENT_EXPORT char const* next_path_element(char const* p); - TORRENT_EXPORT std::string extension(std::string const& f); - TORRENT_EXPORT void replace_extension(std::string& f, std::string const& ext); - TORRENT_EXPORT bool is_root_path(std::string const& f); - TORRENT_EXPORT std::string parent_path(std::string const& f); - TORRENT_EXPORT bool has_parent_path(std::string const& f); - TORRENT_EXPORT std::string filename(std::string const& f); - TORRENT_EXPORT std::string combine_path(std::string const& lhs - , std::string const& rhs); - TORRENT_EXPORT std::string complete(std::string const& f); - TORRENT_EXPORT bool is_complete(std::string const& f); - TORRENT_EXPORT std::string current_working_directory(); - - class TORRENT_EXPORT directory : public boost::noncopyable - { - public: - directory(std::string const& path, error_code& ec); - ~directory(); - void next(error_code& ec); - std::string file() const; - bool done() const { return m_done; } - private: -#ifdef TORRENT_WINDOWS - HANDLE m_handle; -#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_EXPORT file: boost::noncopyable, intrusive_ptr_base - { - enum - { - // 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() - read_only = 0, - write_only = 1, - read_write = 2, - rw_mask = read_only | write_only | read_write, - no_buffer = 4, - mode_mask = rw_mask | no_buffer, - sparse = 8, - no_atime = 16, - - attribute_hidden = 0x1000, - attribute_executable = 0x2000, - 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); - - // called when we're done writing to the file. - // On windows this will clear the sparse bit - void finalize(); - - 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); - - 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; -#endif - }; - -} - -#endif // TORRENT_FILE_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/file_pool.hpp b/libtorrent_utp/include/libtorrent/file_pool.hpp deleted file mode 100644 index ddb673cd4..000000000 --- a/libtorrent_utp/include/libtorrent/file_pool.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - -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_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" - -namespace libtorrent -{ - struct file_entry; - struct file_storage; - - struct TORRENT_EXPORT file_pool : boost::noncopyable - { - file_pool(int size = 40): m_size(size), m_low_prio_io(true) {} - - boost::intrusive_ptr open_file(void* st, std::string const& p - , file_entry const& fe, file_storage const& fs, int m, error_code& ec); - void release(void* st); - void release(void* st, int file_index); - void resize(int size); - int size_limit() const { return m_size; } - void set_low_prio_io(bool b) { m_low_prio_io = b; } - - private: - file_pool(file_pool const&); - - void remove_oldest(); - - int m_size; - bool m_low_prio_io; - - struct lru_file_entry - { - lru_file_entry(): last_use(time_now()) {} - 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; - }; -} - -#endif diff --git a/libtorrent_utp/include/libtorrent/file_storage.hpp b/libtorrent_utp/include/libtorrent/file_storage.hpp deleted file mode 100644 index f42c1b20a..000000000 --- a/libtorrent_utp/include/libtorrent/file_storage.hpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - -Copyright (c) 2003-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_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; - - struct TORRENT_EXPORT file_entry - { - friend class file_storage; - file_entry() - : name(0) - , offset(0) - , symlink_index(-1) - , size(0) - , name_len(0) - , pad_file(false) - , hidden_attribute(false) - , executable_attribute(false) - , symlink_attribute(false) - , path_index(-1) - {} - - file_entry(file_entry const& fe); - file_entry& operator=(file_entry const& fe); - - ~file_entry(); - - void set_name(char const* n, int borrow_chars = 0); - std::string filename() const; - - 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 offset of this file inside the torrent - size_type offset:48; - - // index into file_storage::m_symlinks or -1 - // if this is not a symlink - size_type symlink_index:16; - - // the size of this file - size_type size:48; - - // the number of characters in the name. If this is - // 0, name is null terminated and owned by this object - // (i.e. it should be freed in the destructor). If - // the len is > 0, the name pointer doesn not belong - // to this object, and it's not null terminated - size_type name_len:10; - bool pad_file:1; - bool hidden_attribute:1; - bool executable_attribute:1; - bool symlink_attribute:1; - // 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 - int path_index; - }; - - struct TORRENT_EXPORT file_slice - { - int file_index; - size_type offset; - size_type size; - }; - - class TORRENT_EXPORT file_storage - { - friend class torrent_info; - public: - file_storage(); - ~file_storage() {} - - bool is_valid() const { return m_piece_length > 0; } - - enum flags_t - { - pad_file = 1, - attribute_hidden = 2, - attribute_executable = 4, - attribute_symlink = 8 - }; - - void reserve(int num_files); - - void add_file(file_entry const& e, char const* filehash = 0 - , std::string const* symlink = 0, time_t mtime = 0); - - void add_file(std::string const& p, size_type size, int flags = 0 - , std::time_t mtime = 0, std::string const& s_p = ""); - - void rename_file(int index, std::string const& new_filename); - -#if TORRENT_USE_WSTRING - void add_file(std::wstring const& p, size_type size, int flags = 0 - , std::time_t mtime = 0, std::string const& s_p = ""); - void rename_file(int index, std::wstring const& new_filename); - void set_name(std::wstring const& n); -#endif - - std::vector map_block(int piece, size_type offset - , int size) const; - peer_request map_file(int file, size_type offset, int size) const; - - typedef std::vector::const_iterator iterator; - typedef std::vector::const_reverse_iterator reverse_iterator; - - iterator file_at_offset(size_type offset) const; - iterator begin() const { return m_files.begin(); } - iterator end() const { return m_files.end(); } - reverse_iterator rbegin() const { return m_files.rbegin(); } - reverse_iterator rend() const { return m_files.rend(); } - int num_files() const - { return int(m_files.size()); } - - file_entry const& at(int index) const - { - TORRENT_ASSERT(index >= 0 && index < int(m_files.size())); - return m_files[index]; - } - - sha1_hash hash(file_entry const& fe) const; - std::string const& symlink(file_entry const& fe) const; - time_t mtime(file_entry const& fe) const; - int file_index(file_entry const& fe) const; - size_type file_base(file_entry const& fe) const; - void set_file_base(file_entry const& fe, size_type off); - - std::string file_path(file_entry const& fe) const; - - size_type total_size() const { return m_total_size; } - void set_num_pieces(int n) { m_num_pieces = n; } - int num_pieces() const { TORRENT_ASSERT(m_piece_length > 0); return m_num_pieces; } - void set_piece_length(int l) { m_piece_length = l; } - int piece_length() const { TORRENT_ASSERT(m_piece_length > 0); return m_piece_length; } - int piece_size(int index) const; - - void set_name(std::string const& n) { m_name = n; } - const std::string& name() const { return m_name; } - - void swap(file_storage& ti) - { - using std::swap; - swap(ti.m_piece_length, m_piece_length); - swap(ti.m_files, m_files); - swap(ti.m_total_size, m_total_size); - swap(ti.m_num_pieces, m_num_pieces); - swap(ti.m_name, m_name); - } - - // if pad_file_limit >= 0, files larger than - // that limit will be padded, default is to - // not add any padding - void optimize(int pad_file_limit = -1); - - private: - - void update_path_index(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 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 file_entry::path_index - // points into this array - 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/libtorrent_utp/include/libtorrent/fingerprint.hpp b/libtorrent_utp/include/libtorrent/fingerprint.hpp deleted file mode 100644 index 1846c47b1..000000000 --- a/libtorrent_utp/include/libtorrent/fingerprint.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - -Copyright (c) 2003, 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 -{ - - struct fingerprint - { - 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]; - } - - 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/libtorrent_utp/include/libtorrent/gzip.hpp b/libtorrent_utp/include/libtorrent/gzip.hpp deleted file mode 100644 index 2db984c2e..000000000 --- a/libtorrent_utp/include/libtorrent/gzip.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - -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_GZIP_HPP_INCLUDED -#define TORRENT_GZIP_HPP_INCLUDED - -#include "libtorrent/config.hpp" -#include -#include - -namespace libtorrent -{ - - TORRENT_EXPORT bool inflate_gzip( - char const* in, int size - , std::vector& buffer - , int maximum_size - , std::string& error); - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/hasher.hpp b/libtorrent_utp/include/libtorrent/hasher.hpp deleted file mode 100644 index 158ca984f..000000000 --- a/libtorrent_utp/include/libtorrent/hasher.hpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - -Copyright (c) 2003, 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 defined TORRENT_USE_OPENSSL -extern "C" -{ -#include -} -#else -// from sha1.cpp -struct TORRENT_EXPORT SHA_CTX -{ - boost::uint32_t state[5]; - boost::uint32_t count[2]; - boost::uint8_t buffer[64]; -}; - -TORRENT_EXPORT void SHA1_Init(SHA_CTX* context); -TORRENT_EXPORT void SHA1_Update(SHA_CTX* context, boost::uint8_t const* data, boost::uint32_t len); -TORRENT_EXPORT void SHA1_Final(boost::uint8_t* digest, SHA_CTX* context); - -#endif - -namespace libtorrent -{ - class hasher - { - public: - - hasher() - { -#ifdef TORRENT_USE_GCRYPT - gcry_md_open(&m_context, GCRY_MD_SHA1, 0); -#else - SHA1_Init(&m_context); -#endif - } - 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); -#else - SHA1_Init(&m_context); - SHA1_Update(&m_context, reinterpret_cast(data), len); -#endif - } - -#ifdef TORRENT_USE_GCRYPT - hasher(hasher const& h) - { - gcry_md_copy(&m_context, h.m_context); - } - - hasher& operator=(hasher const& h) - { - gcry_md_close(m_context); - gcry_md_copy(&m_context, h.m_context); - return *this; - } -#endif - - void update(std::string const& data) { update(data.c_str(), data.size()); } - void 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); -#else - SHA1_Update(&m_context, reinterpret_cast(data), len); -#endif - } - - sha1_hash final() - { - sha1_hash digest; -#ifdef TORRENT_USE_GCRYPT - gcry_md_final(m_context); - digest.assign((const char*)gcry_md_read(m_context, 0)); -#else - SHA1_Final(digest.begin(), &m_context); -#endif - return digest; - } - - void reset() - { -#ifdef TORRENT_USE_GCRYPT - gcry_md_reset(m_context); -#else - SHA1_Init(&m_context); -#endif - } - -#ifdef TORRENT_USE_GCRYPT - ~hasher() - { - gcry_md_close(m_context); - } -#endif - - private: - -#ifdef TORRENT_USE_GCRYPT - gcry_md_hd_t m_context; -#else - SHA_CTX m_context; -#endif - }; -} - -#endif // TORRENT_HASHER_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/http_connection.hpp b/libtorrent_utp/include/libtorrent/http_connection.hpp deleted file mode 100644 index ba76c6cd3..000000000 --- a/libtorrent_utp/include/libtorrent/http_connection.hpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - -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_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; - -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_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 - , http_connect_handler const& ch = http_connect_handler() - , http_filter_handler const& fh = http_filter_handler()) - : m_sock(ios) - , 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_bottled(bottled) - , m_called(false) -#ifdef TORRENT_USE_OPENSSL - , m_ssl_ctx(ios, asio::ssl::context::sslv23_client) -#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()); -#ifdef TORRENT_USE_OPENSSL - error_code ec; - m_ssl_ctx.set_verify_mode(asio::ssl::context::verify_none, ec); -#endif - } - - 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(); - - 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 const& e, char const* 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_timeout; - ptime m_last_receive; - // 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; - // 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::list m_endpoints; -#ifdef TORRENT_USE_OPENSSL - asio::ssl::context m_ssl_ctx; -#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/libtorrent_utp/include/libtorrent/http_parser.hpp b/libtorrent_utp/include/libtorrent/http_parser.hpp deleted file mode 100644 index 6ee18d3d5..000000000 --- a/libtorrent_utp/include/libtorrent/http_parser.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - -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_HTTP_PARSER_HPP_INCLUDED -#define TORRENT_HTTP_PARSER_HPP_INCLUDED - -#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); - - class TORRENT_EXPORT http_parser - { - public: - http_parser(); - std::string const& header(char const* key) const - { - static std::string empty; - std::map::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; } - - // 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(); - - std::map const& headers() const { return m_header; } - - private: - int 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::map m_header; - buffer::const_interval m_recv_buffer; - int m_body_start_pos; - - bool m_chunked_encoding; - bool m_finished; - }; - -} - -#endif // TORRENT_HTTP_PARSER_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/http_seed_connection.hpp b/libtorrent_utp/include/libtorrent/http_seed_connection.hpp deleted file mode 100644 index 518e7dc03..000000000 --- a/libtorrent_utp/include/libtorrent/http_seed_connection.hpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - -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_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_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 - , std::string const& url - , policy::peer* peerinfo - , std::string const& ext_auth - , web_seed_entry::headers_t const& ext_headers); - - 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 - int 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 - int 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/libtorrent_utp/include/libtorrent/http_stream.hpp b/libtorrent_utp/include/libtorrent/http_stream.hpp deleted file mode 100644 index 8f97d8aa6..000000000 --- a/libtorrent_utp/include/libtorrent/http_stream.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - -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_HTTP_STREAM_HPP_INCLUDED -#define TORRENT_HTTP_STREAM_HPP_INCLUDED - -#include -#include "libtorrent/proxy_base.hpp" -#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/libtorrent_utp/include/libtorrent/http_tracker_connection.hpp b/libtorrent_utp/include/libtorrent/http_tracker_connection.hpp deleted file mode 100644 index 7ee112211..000000000 --- a/libtorrent_utp/include/libtorrent/http_tracker_connection.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - -Copyright (c) 2003, 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_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/libtorrent_utp/include/libtorrent/i2p_stream.hpp b/libtorrent_utp/include/libtorrent/i2p_stream.hpp deleted file mode 100644 index ad1a9e89e..000000000 --- a/libtorrent_utp/include/libtorrent/i2p_stream.hpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - -Copyright (c) 2009, 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 { - - enum i2p_error_code - { - no_error = 0, - parse_failed, - cant_reach_peer, - i2p_error, - invalid_key, - invalid_id, - timeout, - key_not_found, - num_errors - }; - } - -struct TORRENT_EXPORT i2p_error_category : boost::system::error_category -{ - virtual const char* name() const; - virtual std::string message(int ev) const; - virtual boost::system::error_condition default_error_condition(int ev) const - { return boost::system::error_condition(ev, *this); } -}; - -extern i2p_error_category i2p_category; - -class i2p_stream : public proxy_base -{ -public: - - explicit i2p_stream(io_service& io_service) - : proxy_base(io_service) - , m_id(0) - , m_command(cmd_create_session) - , m_state(0) - {} - - 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; -}; - -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); - 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); - - void set_local_endpoint(error_code const& ec, char const* dest); - - // 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; -}; - -} -#endif // TORRENT_USE_I2P - -#endif - diff --git a/libtorrent_utp/include/libtorrent/identify_client.hpp b/libtorrent_utp/include/libtorrent/identify_client.hpp deleted file mode 100644 index e8cb3b930..000000000 --- a/libtorrent_utp/include/libtorrent/identify_client.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - -Copyright (c) 2003, 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 -{ - - TORRENT_EXPORT std::string identify_client(const peer_id& p); - TORRENT_EXPORT boost::optional client_fingerprint(peer_id const& p); - -} - -#endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED diff --git a/libtorrent_utp/include/libtorrent/instantiate_connection.hpp b/libtorrent_utp/include/libtorrent/instantiate_connection.hpp deleted file mode 100644 index a0e95d859..000000000 --- a/libtorrent_utp/include/libtorrent/instantiate_connection.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - -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_INSTANTIATE_CONNECTION -#define TORRENT_INSTANTIATE_CONNECTION - -#include "libtorrent/socket_type.hpp" -#include - -namespace libtorrent -{ - struct proxy_settings; - struct utp_socket_manager; - - bool instantiate_connection(io_service& ios - , proxy_settings const& ps, socket_type& s - , void* ssl_context = 0 - , utp_socket_manager* sm = 0); -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/intrusive_ptr_base.hpp b/libtorrent_utp/include/libtorrent/intrusive_ptr_base.hpp deleted file mode 100644 index c2f4e5b60..000000000 --- a/libtorrent_utp/include/libtorrent/intrusive_ptr_base.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - -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_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) {} - - intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs) - { return *this; } - - friend void intrusive_ptr_add_ref(intrusive_ptr_base const* s) - { - TORRENT_ASSERT(s->m_refs >= 0); - TORRENT_ASSERT(s != 0); - ++s->m_refs; - } - - friend void intrusive_ptr_release(intrusive_ptr_base const* s) - { - TORRENT_ASSERT(s->m_refs > 0); - TORRENT_ASSERT(s != 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/libtorrent_utp/include/libtorrent/invariant_check.hpp b/libtorrent_utp/include/libtorrent/invariant_check.hpp deleted file mode 100644 index 175a9861b..000000000 --- a/libtorrent_utp/include/libtorrent/invariant_check.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// 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/assert.hpp" - -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_) - { - try - { - check_invariant(self); - } - catch (...) - { - TORRENT_ASSERT(false); - } - } - - ~invariant_checker_impl() - { - try - { - check_invariant(self); - } - catch (...) - { - TORRENT_ASSERT(false); - } - } - - T const& self; - }; - - template - invariant_checker_impl make_invariant_checker(T const& x) - { - return invariant_checker_impl(x); - } -} - -#if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS -#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/libtorrent_utp/include/libtorrent/io.hpp b/libtorrent_utp/include/libtorrent/io.hpp deleted file mode 100644 index 552b6c6dd..000000000 --- a/libtorrent_utp/include/libtorrent/io.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - -Copyright (c) 2003, 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 - 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 void write_string(std::string const& str, char*& start) - { - std::memcpy((void*)start, str.c_str(), str.size()); - start += str.size(); - } - - template - void write_string(std::string const& str, OutIt& start) - { - std::copy(str.begin(), str.end(), start); - } - - } -} - -#endif // TORRENT_IO_HPP_INCLUDED diff --git a/libtorrent_utp/include/libtorrent/io_service.hpp b/libtorrent_utp/include/libtorrent/io_service.hpp deleted file mode 100644 index 5867cf54f..000000000 --- a/libtorrent_utp/include/libtorrent/io_service.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/io_service_fwd.hpp b/libtorrent_utp/include/libtorrent/io_service_fwd.hpp deleted file mode 100644 index b69b7afde..000000000 --- a/libtorrent_utp/include/libtorrent/io_service_fwd.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/ip_filter.hpp b/libtorrent_utp/include/libtorrent/ip_filter.hpp deleted file mode 100644 index dfedee9cd..000000000 --- a/libtorrent_utp/include/libtorrent/ip_filter.hpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - -Copyright (c) 2005, 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 -{ - -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 = 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 = 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; - - }; - -} - -struct TORRENT_EXPORT ip_filter -{ - enum access_flags - { - blocked = 1 - }; - - // both addresses MUST be of the same type (i.e. both must - // be either IPv4 or both must be IPv6) - void add_rule(address first, address last, int flags); - 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 - - 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 -}; - -class TORRENT_EXPORT port_filter -{ -public: - - enum access_flags - { - blocked = 1 - }; - - void add_rule(boost::uint16_t first, boost::uint16_t last, int flags); - int access(boost::uint16_t port) const; - -private: - - detail::filter_impl m_filter; - -}; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/kademlia/dht_tracker.hpp b/libtorrent_utp/include/libtorrent/kademlia/dht_tracker.hpp deleted file mode 100644 index 112ac331d..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/dht_tracker.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - -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_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_EXPORT void intrusive_ptr_add_ref(dht_tracker const*); - TORRENT_EXPORT void intrusive_ptr_release(dht_tracker const*); - - struct dht_tracker - { - friend void intrusive_ptr_add_ref(dht_tracker const*); - friend void intrusive_ptr_release(dht_tracker const*); - friend bool send_callback(void* userdata, entry const& e, udp::endpoint const& addr, int flags); - dht_tracker(libtorrent::aux::session_impl& ses, rate_limited_udp_socket& sock - , dht_settings const& settings, entry const* state = 0); - - void start(entry const& bootstrap); - 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; - - void announce(sha1_hash const& ih, int listen_port - , boost::function const&)> f); - - 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 - void on_receive(udp::endpoint const& ep, char const* pkt, int size); - void on_unreachable(udp::endpoint const& ep); - - 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); - - void on_bootstrap(std::vector > const&); - bool send_packet(libtorrent::entry const& e, udp::endpoint const& addr, int send_flags); - - node_impl m_dht; - libtorrent::aux::session_impl& m_ses; - 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/libtorrent_utp/include/libtorrent/kademlia/find_data.hpp b/libtorrent_utp/include/libtorrent/kademlia/find_data.hpp deleted file mode 100644 index af7db2840..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/find_data.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - -Copyright (c) 2006, 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 ----------- - -//TODO: rename this to find_peers -class find_data : public traversal_algorithm -{ -public: - typedef boost::function const&)> data_callback; - typedef boost::function > const&, bool)> nodes_callback; - - void got_peers(std::vector const& peers); - void got_write_token(node_id const& n, std::string const& write_token) - { m_write_tokens[n] = write_token; } - - find_data(node_impl& node, node_id target - , data_callback const& dcallback - , nodes_callback const& ncallback); - - virtual char const* name() const { return "get_peers"; } - - node_id const target() const { return m_target; } - -protected: - - void done(); - observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); - virtual bool invoke(observer_ptr o); - -private: - - data_callback m_data_callback; - nodes_callback m_nodes_callback; - std::map m_write_tokens; - node_id const m_target; - bool m_done:1; - bool m_got_peers:1; -}; - -class find_data_observer : public observer -{ -public: - find_data_observer( - boost::intrusive_ptr const& algorithm - , udp::endpoint const& ep, node_id const& id) - : observer(algorithm, ep, id) - {} - void reply(msg const&); -}; - -} } // namespace libtorrent::dht - -#endif // FIND_DATA_050323_HPP - diff --git a/libtorrent_utp/include/libtorrent/kademlia/logging.hpp b/libtorrent_utp/include/libtorrent/kademlia/logging.hpp deleted file mode 100644 index 9b17ee6b1..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/logging.hpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - -Copyright (c) 2006, 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_(log) - { - if (log_.enabled()) - log_ << time_now_string() << " [" << log.id() << "] "; - } - - ~log_event() - { - if (log_.enabled()) - { - log_ << "\n"; - log_.flush(); - } - } - - 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/libtorrent_utp/include/libtorrent/kademlia/msg.hpp b/libtorrent_utp/include/libtorrent/kademlia/msg.hpp deleted file mode 100644 index 9de919c20..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/msg.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - -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 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; -/* -namespace messages -{ - enum { ping = 0, find_node = 1, get_peers = 2, announce_peer = 3, error = 4 }; - char const* const ids[] = { "ping", "find_node", "get_peers", "announce_peer", "error" }; -} // namespace messages - -struct msg -{ - msg() - : reply(false) - , message_id(-1) - , port(0) - {} - - // true if this message is a reply - bool reply; - // the kind if message - int message_id; - // if this is a reply, a copy of the transaction id - // from the request. If it's a request, a transaction - // id that should be sent back in the reply - std::string transaction_id; - // the node id of the process sending the message - node_id id; - // the address of the process sending or receiving - // the message. - udp::endpoint addr; - // if this is a nodes response, these are the nodes - nodes_t nodes; - - peers_t peers; - - // similar to transaction_id but for write operations. - std::string write_token; - - // the info has for peer_requests, announce_peer - // and responses - node_id info_hash; - - // port for announce_peer messages - int port; - - // ERROR MESSAGES - int error_code; - std::string error_msg; -}; -*/ - -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/libtorrent_utp/include/libtorrent/kademlia/node.hpp b/libtorrent_utp/include/libtorrent/kademlia/node.hpp deleted file mode 100644 index 895088622..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/node.hpp +++ /dev/null @@ -1,309 +0,0 @@ -/* - -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 NODE_HPP -#define NODE_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "libtorrent/socket.hpp" - -namespace libtorrent { - - namespace aux { struct session_impl; } - struct session_status; - -} - -namespace libtorrent { namespace dht -{ - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DECLARE_LOG(node); -#endif - -struct traversal_algorithm; - -struct key_desc_t -{ - char const* name; - int type; - int size; - int flags; - - enum { optional = 1}; -}; - -bool TORRENT_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 -{ - tcp::endpoint addr; - ptime added; -}; - -// this is a group. It contains a set of group members -struct torrent_entry -{ - std::string name; - std::set peers; -}; - -// this is the entry for a torrent that has been published -// in the DHT. -struct TORRENT_EXPORT search_torrent_entry -{ - search_torrent_entry(): total_tag_points(0), total_name_points(0) {} - - // the tags of the torrent. The key of - // this entry is the sha-1 hash of one of - // these tags. The counter is the number of - // times a tag has been included in a publish - // call. The counters are periodically - // decremented by a factor, so that the - // popularity ratio between the tags is - // maintained. The decrement is rounded down. - std::map tags; - - // this is the sum of all values in the tags - // map. It is only an optimization to avoid - // recalculating it constantly - int total_tag_points; - - // the name of the torrent - std::map name; - int total_name_points; - - // increase the popularity counters for this torrent - void publish(std::string const& name, char const* in_tags[], int num_tags); - - // return a score of how well this torrent matches - // the given set of tags. Each word in the string - // (separated by a space) is considered a tag. - // tags with 2 letters or fewer are ignored - int match(char const* tags[], int num_tags) const; - - // this is called once every hour, and will - // decrement the popularity counters of the - // tags. Returns true if this entry should - // be deleted - bool tick(); - - void get_name(std::string& t) const; - void get_tags(std::string& t) const; -}; - -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&) { m_done = true; } -}; - -struct count_peers -{ - int& count; - count_peers(int& c): count(c) {} - void operator()(std::pair const& t) - { - count += t.second.peers.size(); - } -}; - -class node_impl : boost::noncopyable -{ -typedef std::map table_t; -typedef std::map, search_torrent_entry> search_table_t; -public: - node_impl(libtorrent::aux::session_impl& ses - , bool (*f)(void*, entry const&, udp::endpoint const&, int) - , dht_settings const& settings, node_id nid - , void* userdata); - - virtual ~node_impl() {} - - void tick(); - void refresh(node_id const& id, find_data::nodes_callback const& f); - 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 - - void announce(sha1_hash const& info_hash, int listen_port - , boost::function const&)> 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); - -protected: - // is called when a find data request is received. Should - // return false if the data is not stored on this node. If - // the data is stored, it should be serialized into 'data'. - bool lookup_peers(sha1_hash const& info_hash, entry& reply) const; - bool lookup_torrents(sha1_hash const& target, entry& reply - , char* tags) const; - - dht_settings const& m_settings; - - // the maximum number of peers to send in a get_peers - // reply. Ordinary trackers usually limit this to 50. - // 50 => 6 * 50 = 250 bytes + packet overhead - int m_max_peers_reply; - -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: - table_t m_map; - search_table_t m_search_map; - - ptime m_last_tracker_tick; - - // secret random numbers used to create write tokens - int m_secret[2]; - - libtorrent::aux::session_impl& m_ses; - bool (*m_send)(void*, entry const&, udp::endpoint const&, int); - void* m_userdata; -}; - - -} } // namespace libtorrent::dht - -#endif // NODE_HPP - diff --git a/libtorrent_utp/include/libtorrent/kademlia/node_entry.hpp b/libtorrent_utp/include/libtorrent/kademlia/node_entry.hpp deleted file mode 100644 index d636d9234..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/node_entry.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - -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 KADEMLIA_NODE_ENTRY_HPP -#define KADEMLIA_NODE_ENTRY_HPP - -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/address.hpp" - -namespace libtorrent { namespace dht -{ - -struct node_entry -{ - node_entry(node_id const& id_, udp::endpoint ep, bool pinged = false) - : addr(ep.address()) - , port(ep.port()) - , timeout_count(pinged ? 0 : 0xffff) - , id(id_) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - first_seen = time_now(); -#endif - } - - node_entry(udp::endpoint ep) - : addr(ep.address()) - , port(ep.port()) - , timeout_count(0xffff) - , id(0) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - first_seen = time_now(); -#endif - } - - node_entry() - : timeout_count(0xffff) - , id(0) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - first_seen = time_now(); -#endif - } - - bool pinged() const { return timeout_count != 0xffff; } - void set_pinged() { if (timeout_count == 0xffff) timeout_count = 0; } - void timed_out() { if (pinged()) ++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(addr, port); } - bool confirmed() const { return timeout_count == 0; } - - // TODO: replace with a union of address_v4 and address_v6 - address addr; - boost::uint16_t port; - // the number of times this node has failed to - // respond in a row - boost::uint16_t timeout_count; - node_id id; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ptime first_seen; -#endif -}; - -} } // namespace libtorrent::dht - -#endif - diff --git a/libtorrent_utp/include/libtorrent/kademlia/node_id.hpp b/libtorrent_utp/include/libtorrent/kademlia/node_id.hpp deleted file mode 100644 index 48f50151e..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/node_id.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - -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 NODE_ID_HPP -#define NODE_ID_HPP - -#include - -#include -#include "libtorrent/config.hpp" -#include "libtorrent/peer_id.hpp" -#include "libtorrent/assert.hpp" - -namespace libtorrent { namespace dht -{ - -typedef libtorrent::big_number node_id; - -// returns the distance between the two nodes -// using the kademlia XOR-metric -node_id TORRENT_EXPORT distance(node_id const& n1, node_id const& n2); - -// returns true if: distance(n1, ref) < distance(n2, ref) -bool TORRENT_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) -// usefult for finding out which bucket a node belongs to -int TORRENT_EXPORT distance_exp(node_id const& n1, node_id const& n2); - -node_id TORRENT_EXPORT generate_id(); - -} } // namespace libtorrent::dht - -#endif // NODE_ID_HPP - diff --git a/libtorrent_utp/include/libtorrent/kademlia/observer.hpp b/libtorrent_utp/include/libtorrent/kademlia/observer.hpp deleted file mode 100644 index a1cfe1bfe..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/observer.hpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - -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 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_EXPORT void intrusive_ptr_add_ref(observer const*); -TORRENT_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 m_is_v6, m_short_timeout, m_in_constructor, m_was_sent -// 39 1 1 -// 40 - -struct observer : boost::noncopyable -{ - friend TORRENT_EXPORT void intrusive_ptr_add_ref(observer const*); - friend TORRENT_EXPORT void intrusive_ptr_release(observer const*); - - observer(boost::intrusive_ptr const& a - , udp::endpoint const& ep, node_id const& id) - : flags(0) - , m_sent() - , m_refs(0) - , m_algorithm(a) - , m_id(id) - , m_is_v6(false) - , m_short_timeout(false) - , m_done(false) - { - TORRENT_ASSERT(a); -#ifdef TORRENT_DEBUG - m_in_constructor = true; - m_was_sent = false; -#endif - set_target(ep); - } - - 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 m_short_timeout; } - - // 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) { m_id = 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 - }; - unsigned char flags; - -#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; - - bool m_is_v6:1; - bool m_short_timeout:1; - // when true, this observer has reported - // back to the traversal algorithm already - bool m_done:1; - -#ifdef TORRENT_DEBUG -public: - bool m_in_constructor:1; - bool m_was_sent:1; -#endif -}; - -typedef boost::intrusive_ptr observer_ptr; - -} } - -#endif - diff --git a/libtorrent_utp/include/libtorrent/kademlia/refresh.hpp b/libtorrent_utp/include/libtorrent/kademlia/refresh.hpp deleted file mode 100644 index c30476a32..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/refresh.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - -Copyright (c) 2006, 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 refresh : public find_data -{ -public: - typedef find_data::nodes_callback done_callback; - - refresh(node_impl& node, node_id target - , done_callback const& callback); - - virtual char const* name() const; - -protected: - - observer_ptr new_observer(void* ptr, udp::endpoint const& ep, node_id const& id); - virtual bool invoke(observer_ptr o); -}; - -} } // namespace libtorrent::dht - -#endif // REFRESH_050324_HPP - diff --git a/libtorrent_utp/include/libtorrent/kademlia/routing_table.hpp b/libtorrent_utp/include/libtorrent/kademlia/routing_table.hpp deleted file mode 100644 index 3ed4a4f70..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/routing_table.hpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - -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 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; - ptime last_active; -}; - -// 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_EXPORT routing_table -{ -public: - 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); - - // 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(); } - - bool add_node(node_entry const& 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); - - // 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); - - // if any bucket in the routing table needs to be refreshed - // this function will return true and set the target to an - // appropriate target inside that bucket - bool need_refresh(node_id& target) const; - - enum - { - 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); - - int bucket_size(int bucket) - { - int num_buckets = m_buckets.size(); - if (bucket < num_buckets) bucket = num_buckets - 1; - table_t::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; } - - boost::tuple size() const; - size_type num_global_nodes() const; - - // returns true if there are no working nodes - // in the routing table - bool need_bootstrap() 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 - - void touch_bucket(node_id const& target); - -private: - - typedef std::list table_t; - - table_t::iterator find_bucket(node_id const& id); - - // constant called k in paper - int m_bucket_size; - - dht_settings const& m_settings; - - // (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 time need_bootstrap() returned true - mutable ptime m_last_bootstrap; - - // 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; -}; - -} } // namespace libtorrent::dht - -#endif // ROUTING_TABLE_HPP - diff --git a/libtorrent_utp/include/libtorrent/kademlia/rpc_manager.hpp b/libtorrent_utp/include/libtorrent/kademlia/rpc_manager.hpp deleted file mode 100644 index fb8fa6eb3..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/rpc_manager.hpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - -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 RPC_MANAGER_HPP -#define RPC_MANAGER_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "libtorrent/ptime.hpp" - -namespace libtorrent { namespace dht -{ - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DECLARE_LOG(rpc); -#endif - -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&) { m_done = true; } -}; - -class routing_table; - -class rpc_manager -{ -public: - typedef bool (*send_fun)(void* userdata, entry const&, udp::endpoint const&, int); - - rpc_manager(node_id const& our_id - , routing_table& table, send_fun const& sf - , void* userdata); - ~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); - time_duration tick(); - - bool invoke(entry& e, udp::endpoint target - , observer_ptr o); - - void add_our_id(entry& e); - -#ifdef TORRENT_DEBUG - size_t allocation_size() const; - void check_invariant() const; -#endif - - void* allocate_observer(); - void free_observer(void* ptr); - - int num_allocated_observers() const { return m_allocated_observers; } - -private: - - enum { max_transaction_id = 0x10000 }; - - boost::uint32_t calc_connection_id(udp::endpoint addr); - - mutable boost::pool<> m_pool_allocator; - - typedef std::list transactions_t; - transactions_t m_transactions; - - // this is the next transaction id to be used - int m_next_transaction_id; - - send_fun m_send; - void* m_userdata; - node_id m_our_id; - routing_table& m_table; - ptime m_timer; - node_id m_random_number; - int m_allocated_observers; - bool m_destructing; -}; - -} } // namespace libtorrent::dht - -#endif - - diff --git a/libtorrent_utp/include/libtorrent/kademlia/traversal_algorithm.hpp b/libtorrent_utp/include/libtorrent/kademlia/traversal_algorithm.hpp deleted file mode 100644 index b09601747..000000000 --- a/libtorrent_utp/include/libtorrent/kademlia/traversal_algorithm.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - -Copyright (c) 2006, 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* allocate_observer(); - void free_observer(void* ptr); - - virtual char const* name() const { return "traversal_algorithm"; } - virtual void start(); - - node_id const& target() const { return m_target; } - - void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); - - 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) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << " [" << this << "] new traversal process. Target: " << target; -#endif - } - -protected: - - void 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 m_target; - std::vector m_results; - int m_invoke_count; - int m_branch_factor; - int m_responses; - int m_timeouts; -}; - -} } // namespace libtorrent::dht - -#endif // TRAVERSAL_ALGORITHM_050324_HPP - diff --git a/libtorrent_utp/include/libtorrent/lazy_entry.hpp b/libtorrent_utp/include/libtorrent/lazy_entry.hpp deleted file mode 100644 index 1fc5c09bd..000000000 --- a/libtorrent_utp/include/libtorrent/lazy_entry.hpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - -Copyright (c) 2003, 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 "libtorrent/config.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/size_type.hpp" -#include "libtorrent/error_code.hpp" - -#if TORRENT_USE_IOSTREAM -#include -#endif - -namespace libtorrent -{ - struct lazy_entry; - - TORRENT_EXPORT char const* parse_int(char const* start, char const* end - , char delimiter, boost::int64_t& val); - - // return 0 = success - 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); - - struct pascal_string - { - pascal_string(char const* p, int l): len(l), ptr(p) {} - int len; - char const* ptr; - 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; - - struct TORRENT_EXPORT lazy_entry - { - enum entry_type_t - { - none_t, dict_t, list_t, string_t, int_t - }; - - lazy_entry() : m_begin(0), m_len(0), m_type(none_t) - { m_data.start = 0; } - - 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' - } - - size_type int_value() const; - - // string functions - // ================ - - void construct_string(char const* start, int length); - - // the string is not null-terminated! - 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; - } - - pascal_string string_pstr() const - { - TORRENT_ASSERT(m_type == string_t); - return pascal_string(m_data.start, m_size); - } - - std::string string_value() const - { - TORRENT_ASSERT(m_type == string_t); - return std::string(m_data.start, m_size); - } - - int string_length() const - { return m_size; } - - // dictionary functions - // ==================== - - 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; - } - - lazy_entry* dict_append(char const* name); - lazy_entry* dict_find(char const* name); - lazy_entry const* dict_find(char const* name) const - { return const_cast(this)->dict_find(name); } - - std::string dict_find_string_value(char const* name) const; - pascal_string dict_find_pstr(char const* name) const; - size_type dict_find_int_value(char const* name, size_type default_val = 0) const; - lazy_entry const* dict_find_dict(char const* name) const; - lazy_entry const* dict_find_list(char const* name) const; - lazy_entry const* dict_find_string(char const* name) const; - lazy_entry const* dict_find_int(char const* name) const; - - std::pair dict_at(int i) const; - - int dict_size() const - { - TORRENT_ASSERT(m_type == dict_t); - return m_size; - } - - // list functions - // ============== - - 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; - } - - lazy_entry* list_append(); - 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); } - - std::string list_string_value_at(int i) const; - pascal_string list_pstr_at(int i) const; - size_type list_int_value_at(int i, size_type default_val = 0) const; - - int list_size() const - { - TORRENT_ASSERT(m_type == list_t); - return int(m_size); - } - - // end points one byte passed last byte - void set_end(char const* end) - { - TORRENT_ASSERT(end > m_begin); - m_len = end - m_begin; - } - - void clear(); - - // releases ownership of any memory allocated - void release() - { - m_data.start = 0; - m_size = 0; - m_capacity = 0; - m_type = none_t; - } - - ~lazy_entry() - { clear(); } - - // returns pointers into the source buffer where - // this entry has its bencoded data - std::pair data_section() const; - - 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; - }; - - TORRENT_EXPORT std::string print_entry(lazy_entry const& e - , bool single_line = false, int indent = 0); -#if TORRENT_USE_IOSTREAM - TORRENT_EXPORT std::ostream& operator<<(std::ostream& os, lazy_entry const& e); -#endif - -} - - -#endif - diff --git a/libtorrent_utp/include/libtorrent/lsd.hpp b/libtorrent_utp/include/libtorrent/lsd.hpp deleted file mode 100644 index 72c4fcca4..000000000 --- a/libtorrent_utp/include/libtorrent/lsd.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - -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_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); - void close(); - - void use_broadcast(bool b); - -private: - - void resend_announce(error_code const& e, std::string msg); - void on_announce(udp::endpoint const& from, char* buffer - , std::size_t bytes_transferred); -// void setup_receive(); - - peer_callback_t m_callback; - - // current retry count - int m_retry_count; - - // the udp socket used to send and receive - // multicast messages on - broadcast_socket m_socket; - - // used to resend udp packets in case - // they time out - deadline_timer m_broadcast_timer; - - bool m_disabled; -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - std::ofstream m_log; -#endif -}; - -} - - -#endif - diff --git a/libtorrent_utp/include/libtorrent/magnet_uri.hpp b/libtorrent_utp/include/libtorrent/magnet_uri.hpp deleted file mode 100644 index 2e50d6854..000000000 --- a/libtorrent_utp/include/libtorrent/magnet_uri.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - -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_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; - struct session; - - std::string TORRENT_EXPORT make_magnet_uri(torrent_handle const& handle); - std::string TORRENT_EXPORT make_magnet_uri(torrent_info const& info); - -#ifndef BOOST_NO_EXCEPTIONS -#ifndef TORRENT_NO_DEPRECATE - // deprecated in 0.14 - 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; -#endif - - torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri - , add_torrent_params p); -#endif - - torrent_handle TORRENT_EXPORT add_magnet_uri(session& ses, std::string const& uri - , add_torrent_params p, error_code& ec); -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/max.hpp b/libtorrent_utp/include/libtorrent/max.hpp deleted file mode 100644 index 2a7753aa0..000000000 --- a/libtorrent_utp/include/libtorrent/max.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - -Copyright (c) 2009, 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 - }; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/natpmp.hpp b/libtorrent_utp/include/libtorrent/natpmp.hpp deleted file mode 100644 index c90f90b6f..000000000 --- a/libtorrent_utp/include/libtorrent/natpmp.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - -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_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 TORRENT_EXPORT 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 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]; - - // 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/libtorrent_utp/include/libtorrent/packet_buffer.hpp b/libtorrent_utp/include/libtorrent/packet_buffer.hpp deleted file mode 100644 index 1092760dd..000000000 --- a/libtorrent_utp/include/libtorrent/packet_buffer.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - -Copyright (c) 2010, 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 "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. - - // if m_mask is 0xf, m_array has 16 elements - // m_cursor 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_cursor = 9 - // | refers to index 14 - // | | - // V V - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | | | | | | | | | | | | | | | | | m_mask = 0xf - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // ^ - // | - // refers to index 15 - - // whenever the element at the cursor is removed, the - // cursor is bumped to the next occupied element - - class 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; } - - private: - void** m_storage; - std::size_t m_capacity; - std::size_t m_size; - - // This defines the first index that is part of the m_storage. - // The last index is (m_first + (m_capacity - 1)) & 0xffff. - index_type m_first; - index_type m_last; - }; -} - -#endif // TORRENT_PACKET_BUFFER_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/parse_url.hpp b/libtorrent_utp/include/libtorrent/parse_url.hpp deleted file mode 100644 index 8b2698069..000000000 --- a/libtorrent_utp/include/libtorrent/parse_url.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - -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_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 -{ - - TORRENT_EXPORT boost::tuple - parse_url_components(std::string url, error_code& ec); - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/pch.hpp b/libtorrent_utp/include/libtorrent/pch.hpp deleted file mode 100644 index 309a6a3fc..000000000 --- a/libtorrent_utp/include/libtorrent/pch.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - -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. - -*/ - -#ifdef BOOST_BUILD_PCH_ENABLED - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "libtorrent/puff.h" - -#ifdef __OBJC__ -#define Protocol Protocol_ -#endif - -#include -#include -#include -#include -#include - -#ifdef __OBJC__ -#undef Protocol -#endif - -#endif - diff --git a/libtorrent_utp/include/libtorrent/pe_crypto.hpp b/libtorrent_utp/include/libtorrent/pe_crypto.hpp deleted file mode 100644 index 2d4f58c54..000000000 --- a/libtorrent_utp/include/libtorrent/pe_crypto.hpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - -Copyright (c) 2007, 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_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_EXPORT rc4_init(const unsigned char* in, unsigned long len, rc4 *state); -unsigned long TORRENT_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_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; - }; - - class RC4_handler // Non copyable - { - public: - // Input longkeys must be 20 bytes - RC4_handler(sha1_hash const& rc4_local_longkey, - sha1_hash const& rc4_remote_longkey) - { -#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); - gcry_cipher_setkey(m_rc4_incoming, &rc4_remote_longkey[0], 20); - gcry_cipher_setkey(m_rc4_outgoing, &rc4_local_longkey[0], 20); -#elif defined TORRENT_USE_OPENSSL - RC4_set_key(&m_local_key, 20, &rc4_local_longkey[0]); - RC4_set_key(&m_remote_key, 20, &rc4_remote_longkey[0]); -#else - rc4_init(&rc4_remote_longkey[0], 20, &m_rc4_incoming); - rc4_init(&rc4_local_longkey[0], 20, &m_rc4_outgoing); -#endif - - // Discard first 1024 bytes - char buf[1024]; - encrypt(buf, 1024); - decrypt(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) - { - 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) - { - 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 - }; - -} // namespace libtorrent - -#endif // TORRENT_PE_CRYPTO_HPP_INCLUDED -#endif // TORRENT_DISABLE_ENCRYPTION - diff --git a/libtorrent_utp/include/libtorrent/peer.hpp b/libtorrent_utp/include/libtorrent/peer.hpp deleted file mode 100644 index c404a611d..000000000 --- a/libtorrent_utp/include/libtorrent/peer.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - -Copyright (c) 2003, 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_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/libtorrent_utp/include/libtorrent/peer_connection.hpp b/libtorrent_utp/include/libtorrent/peer_connection.hpp deleted file mode 100644 index 8d8d7f27f..000000000 --- a/libtorrent_utp/include/libtorrent/peer_connection.hpp +++ /dev/null @@ -1,1210 +0,0 @@ -/* - -Copyright (c) 2003, 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" - -#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_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); - - // with this constructor we have been contacted and we still don't - // know which torrent the connection belongs to - peer_connection( - aux::session_impl& ses - , boost::shared_ptr s - , tcp::endpoint const& remote - , policy::peer* peerinfo); - - // 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; - } - - 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; } - - 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; - } - - 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 index); - int superseed_piece() const { return m_superseed_piece; } - - // 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(char* 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 - (*m_logger) << time_now_string() << "*** HOLEPUNCH MODE ***\n"; -#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(); - - // free upload. - size_type total_free_upload() const; - void add_free_upload(size_type free_upload); - - // trust management. - void received_valid_data(int index); - void received_invalid_data(int index); - - size_type share_diff() const; - - // a connection is local if it was initiated by us. - // if it was an incoming connection, it is remote - bool is_local() const { return m_active; } - - 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 { return 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, ...); - 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_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); - - 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); - - // 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; - - void 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) - void cancel_request(piece_block const& b); - void send_block_requests(); - - int bandwidth_throttle(int channel) const - { return m_bandwidth_channel[channel].throttle(); } - - void assign_bandwidth(int channel, int amount); - -#ifdef TORRENT_DEBUG - void check_invariant() const; - ptime m_last_choke; -#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 - (*m_logger) << "downloading_piece_progress() dispatched to the base class!\n"; -#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); - virtual buffer::interval allocate_send_buffer(int size); - virtual void setup_send(); - -#if defined TORRENT_STATS && defined 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 = false) - { -#if defined TORRENT_STATS && 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; } - -#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; } - - // upload and download channel state - // enum from peer_info::bw_state - char m_channel_state[2]; - - size_type uploaded_since_unchoke() const - { return m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; } - - size_type downloaded_since_unchoke() const - { return m_statistics.total_payload_download() - m_downloaded_at_last_unchoke; } - - 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() - { - TORRENT_ASSERT(!m_disk_recv_buffer); - TORRENT_ASSERT(m_disk_recv_buffer_size == 0); - if (m_recv_buffer.empty()) return buffer::interval(0,0); - return buffer::interval(&m_recv_buffer[0] - , &m_recv_buffer[0] + m_recv_pos); - } - - std::pair wr_recv_buffers(int bytes); -#endif - - buffer::const_interval receive_buffer() const - { - if (m_recv_buffer.empty()) return buffer::const_interval(0,0); - return buffer::const_interval(&m_recv_buffer[0] - , &m_recv_buffer[0] + m_recv_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; } - - void attach_to_torrent(sha1_hash const& ih); - - bool verify_piece(peer_request const& p) const; - - void update_desired_queue_size(); - - // the bandwidth channels, upload and download - // keeps track of the current quotas - bandwidth_channel m_bandwidth_channel[num_channels]; - - // 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; - - // 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); - - // 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; - - // the average rate of receiving complete piece messages - sliding_average<20> m_piece_rate; - sliding_average<20> m_send_rate; - - void set_timeout(int s) { m_timeout = s; } - -#ifndef TORRENT_DISABLE_EXTENSIONS - typedef std::list > extension_list_t; - extension_list_t m_extensions; -#endif - -#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 - - boost::intrusive_ptr self() - { - TORRENT_ASSERT(!m_in_constructor); - return boost::intrusive_ptr(this); - } - - 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); - void request_upload_bandwidth( - bandwidth_channel* bwc1 - , bandwidth_channel* bwc2 = 0 - , bandwidth_channel* bwc3 = 0 - , bandwidth_channel* bwc4 = 0); - void 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; - - // 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 amount of data this peer has been given - // as free upload. This is distributed from - // peers from which we get free download - // this will be negative on a peer from which - // we get free download, and positive on peers - // that we give the free upload, to keep the balance. - size_type m_free_upload; - - // the total payload download bytes - // at the last unchoke cycle. 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_unchoke; - size_type m_uploaded_at_last_unchoke; - -#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 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; - - // 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; - - // remote peer's id - peer_id m_peer_id; - - // the pieces the other end have - bitfield m_have_piece; - - // the queue of requests we have got - // from this peer - 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; - - // 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; - - // the block we're currently receiving. Or - // (-1, -1) if we're not receiving one - piece_block m_receiving_block; - - // the time when this peer last saw a complete copy - // of this torrent - time_t m_last_seen_complete; - - // 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 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; - - // 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 this is -1, superseeding is not active. If it is >= 0 - // this is the piece that is available to this peer. Only - // this piece 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; - - // bytes downloaded since last second - // timer timeout; used for determining - // approx download rate - int m_remote_bytes_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; - - // 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; - - // 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; - - // the number of request we should queue up - // at the remote end. - boost::uint8_t m_desired_queue_size; - - // the number of piece requests we have rejected - // in a row because the peer is choked. This is - // used to re-send the choked message in case the - // other end keeps requesting pieces while being - // choked, and eventuelly disconnect if it keeps - // requesting too many pieces while being choked - boost::uint8_t m_choke_rejects; - - // counts the number of recursive calls to on_receive_data - // used to limit recursion - boost::uint8_t m_read_recurse:5; - - // 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_active: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; - - // 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 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; - - // this is true if this connection has been added - // to the list of connections that will be closed. - bool m_disconnecting: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; - - // this is set to true once the bitfield is received - bool m_bitfield_received:1; - - // if this is set to true, the client will not - // pick any pieces from this peer - bool m_no_download: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; - - template - struct handler_storage - { -#ifdef TORRENT_DEBUG - handler_storage() - : used(false) - {} - - bool used; -#endif - boost::aligned_storage bytes; - }; - - handler_storage m_read_handler_storage; - handler_storage m_write_handler_storage; - - 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 - ); - } - -#ifdef TORRENT_DEBUG - public: - bool m_in_constructor:1; - bool m_disconnect_started:1; - bool m_initialized:1; - int m_received_in_piece; -#endif - }; -} - -#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/peer_id.hpp b/libtorrent_utp/include/libtorrent/peer_id.hpp deleted file mode 100644 index 3624147ad..000000000 --- a/libtorrent_utp/include/libtorrent/peer_id.hpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - -Copyright (c) 2003, 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 -#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 -{ - - class TORRENT_EXPORT big_number - { - // the number of bytes of the number - enum { number_size = 20 }; - public: - enum { size = number_size }; - - big_number() {} - - static big_number max() - { - big_number ret; - memset(ret.m_number, 0xff, size); - return ret; - } - - static big_number min() - { - big_number ret; - memset(ret.m_number, 0, size); - return ret; - } - - explicit big_number(char const* s) - { - if (s == 0) clear(); - else std::memcpy(m_number, s, size); - } - - explicit big_number(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); - } - - void clear() - { - std::fill(m_number,m_number+number_size,(const unsigned char)(0)); - } - - 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; - } - - bool operator==(big_number const& n) const - { - return std::equal(n.m_number, n.m_number+number_size, m_number); - } - - bool operator!=(big_number const& n) const - { - return !std::equal(n.m_number, n.m_number+number_size, m_number); - } - - bool operator<(big_number 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; - } - - big_number operator~() - { - big_number ret; - for (int i = 0; i< number_size; ++i) - ret.m_number[i] = ~m_number[i]; - return ret; - } - - big_number& operator &= (big_number const& n) - { - for (int i = 0; i< number_size; ++i) - m_number[i] &= n.m_number[i]; - return *this; - } - - big_number& operator |= (big_number const& n) - { - for (int i = 0; i< number_size; ++i) - m_number[i] |= n.m_number[i]; - return *this; - } - - big_number& operator ^= (big_number const& n) - { - for (int i = 0; i< number_size; ++i) - m_number[i] ^= n.m_number[i]; - return *this; - } - - 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; - - 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; } - - std::string to_string() const - { return std::string((char const*)&m_number[0], number_size); } - - private: - - unsigned char m_number[number_size]; - - }; - - typedef big_number peer_id; - typedef big_number sha1_hash; - -#if TORRENT_USE_IOSTREAM - inline std::ostream& operator<<(std::ostream& os, big_number const& peer) - { - char out[41]; - to_hex((char const*)&peer[0], big_number::size, out); - return os << out; - } - - inline std::istream& operator>>(std::istream& is, big_number& 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/libtorrent_utp/include/libtorrent/peer_info.hpp b/libtorrent_utp/include/libtorrent/peer_info.hpp deleted file mode 100644 index b094e387d..000000000 --- a/libtorrent_utp/include/libtorrent/peer_info.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - -Copyright (c) 2003, 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 -{ - struct TORRENT_EXPORT peer_info - { - enum - { - interesting = 0x1, - choked = 0x2, - remote_interested = 0x4, - remote_choked = 0x8, - supports_extensions = 0x10, - local_connection = 0x20, - handshake = 0x40, - connecting = 0x80, - queued = 0x100, - on_parole = 0x200, - seed = 0x400, - optimistic_unchoke = 0x800, - snubbed = 0x1000, - upload_only = 0x2000, - holepunched = 0x4000 -#ifndef TORRENT_DISABLE_ENCRYPTION - , rc4_encrypted = 0x100000, - plaintext_encrypted = 0x200000 -#endif - }; - - unsigned int flags; - - enum peer_source_flags - { - tracker = 0x1, - dht = 0x2, - pex = 0x4, - lsd = 0x8, - resume_data = 0x10, - incoming = 0x20 - }; - - int source; - - // bw_idle: the channel is not used - // bw_limit: the channel is waiting for quota - // bw_network: the channel is waiting for an async write - // for read operation to complete - // bw_disk: the peer is waiting for the disk io thread - // to catch up - enum bw_state { bw_idle, bw_limit, bw_network, bw_disk }; -#ifndef TORRENT_NO_DEPRECATE - enum bw_state_deprecated { bw_torrent = bw_limit, bw_global = bw_limit }; -#endif - - char read_state; - char write_state; - - tcp::endpoint ip; - int up_speed; - int down_speed; - int payload_up_speed; - int payload_down_speed; - size_type total_download; - size_type total_upload; - peer_id pid; - bitfield pieces; - int upload_limit; - int download_limit; - - // time since last request - time_duration last_request; - - // time since last download or upload - 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 - // pending request times out - int request_timeout; - - // the size of the send buffer for this peer, in bytes - int send_buffer_size; - // the number bytes that's actually used of the send buffer - int used_send_buffer; - - int receive_buffer_size; - int used_receive_buffer; - - // the number of failed hashes for this peer - int num_hashfails; - -#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 country[2]; -#endif - -#ifndef TORRENT_DISABLE_GEO_IP - // atonomous system this peer belongs to - std::string inet_as_name; - int inet_as; -#endif - - size_type load_balancing; - - // 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 request messages - // waiting to be sent inside the send buffer - 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; - - // this is the number of requests - // the peer has sent to us - // that we haven't sent yet - int upload_queue_length; - - // the number of times this IP - // has failed to connect - int failcount; - - // the currently downloading piece - // if piece index is -1 all associated - // members are just set to 0 - int downloading_piece_index; - int downloading_block_index; - int downloading_progress; - int downloading_total; - - std::string client; - - enum - { - standard_bittorrent = 0, - web_seed = 1, - http_seed = 2, - bittorrent_utp = 3 - }; - int connection_type; - - // approximate peer download rate - int remote_dl_rate; - - // number of bytes this peer has in - // the disk write queue - int pending_disk_bytes; - - // numbers used for bandwidth limiting - int send_quota; - int receive_quota; - - // estimated rtt to peer, in milliseconds - int rtt; - - // the number of pieces this peer has - int num_pieces; - - // the highest transfer rates seen for this peer - int download_rate_peak; - int upload_rate_peak; - - // the peers progress - float progress; // [0, 1] - int progress_ppm; // [0, 1000000] - - int estimated_reciprocation_rate; - - tcp::endpoint local_endpoint; - }; - - struct TORRENT_EXPORT peer_list_entry - { - enum flags_t - { - banned = 1 - }; - - tcp::endpoint ip; - int flags; - boost::uint8_t failcount; - boost::uint8_t source; - }; - - // defined in policy.cpp - int source_rank(int source_bitmask); -} - -#endif // TORRENT_PEER_INFO_HPP_INCLUDED diff --git a/libtorrent_utp/include/libtorrent/peer_request.hpp b/libtorrent_utp/include/libtorrent/peer_request.hpp deleted file mode 100644 index 801a61770..000000000 --- a/libtorrent_utp/include/libtorrent/peer_request.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - -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_PEER_REQUEST_HPP_INCLUDED -#define TORRENT_PEER_REQUEST_HPP_INCLUDED - -namespace libtorrent -{ - struct TORRENT_EXPORT peer_request - { - int piece; - int start; - int length; - 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/libtorrent_utp/include/libtorrent/piece_block_progress.hpp b/libtorrent_utp/include/libtorrent/piece_block_progress.hpp deleted file mode 100644 index 481ffc971..000000000 --- a/libtorrent_utp/include/libtorrent/piece_block_progress.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - -Copyright (c) 2003, 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 TORRENT_EXPORT 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/libtorrent_utp/include/libtorrent/piece_picker.hpp b/libtorrent_utp/include/libtorrent/piece_picker.hpp deleted file mode 100644 index 5ad89f587..000000000 --- a/libtorrent_utp/include/libtorrent/piece_picker.hpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - -Copyright (c) 2003, 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/session_settings.hpp" -#include "libtorrent/config.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/time.hpp" - -namespace libtorrent -{ - - class torrent; - class peer_connection; - struct bitfield; - - struct TORRENT_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 << 18)); - TORRENT_ASSERT(b_index < (1 << 14)); - } - boost::uint32_t piece_index:18; - boost::uint32_t block_index:14; - - 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_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; - }; - - // 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 - }; - - struct downloading_piece - { - downloading_piece(): last_request(min_time()), finished(0), writing(0), requested(0) {} - piece_state_t state; - ptime last_request; - - // the index of the piece - int index; - // info about each block - // this is a pointer into the m_block_info - // vector owned by the piece_picker - block_info* info; - // 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; - }; - - 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); - void dec_refcount(int index); - - // increases the peer count for the given piece - // (is used when a BITFIELD message is received) - void inc_refcount(bitfield const& bitmask); - // decreases the peer count for the given piece - // (used when a peer disconnects) - void dec_refcount(bitfield const& bitmask); - - // 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(); - void dec_refcount_all(); - - // 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; - } - - // 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) 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; - ptime last_request(int piece) 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; } - - 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; } - -#ifdef TORRENT_DEBUG - // used in debug mode - void verify_priority(int start, int end, int prio) const; - void check_invariant(const torrent* t = 0) const; - void verify_pick(std::vector const& picked - , bitfield const& bits) 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) - , piece_priority(1) - , index(index_) - { - TORRENT_ASSERT(peer_count_ >= 0); - TORRENT_ASSERT(index_ >= 0); - } - - // the number of peers that has this piece - // (availability) - unsigned peer_count : 10; - // is 1 if the piece is marked as being downloaded - unsigned downloading : 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) - unsigned piece_priority : 3; - // index in to the piece_info vector - unsigned index : 18; - - 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. - we_have_index = 0x3ffff, - // the priority value that means the piece is filtered - filter_priority = 0, - // the max number the peer count can hold - max_peer_count = 0x3ff - }; - - 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; } - void filtered(bool f) { piece_priority = f ? filter_priority : 0; } - - // 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; } - - }; - - private: - - BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); - - 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(); - void erase_download_piece(std::vector::iterator i); - - // 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 - 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; - - int m_blocks_per_piece; - int 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; - - // if this is set to true, it means update_pieces() - // has to be called before accessing m_pieces. - mutable bool m_dirty; - public: - - enum { max_pieces = piece_pos::we_have_index - 1 }; - - }; - - inline int piece_picker::blocks_in_piece(int index) const - { - TORRENT_ASSERT(index >= 0); - TORRENT_ASSERT(index < (int)m_piece_map.size()); - if (index+1 == (int)m_piece_map.size()) - return m_blocks_in_last_piece; - else - return m_blocks_per_piece; - } - -} - -#endif // TORRENT_PIECE_PICKER_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/policy.hpp b/libtorrent_utp/include/libtorrent/policy.hpp deleted file mode 100644 index 0f5b0d3a8..000000000 --- a/libtorrent_utp/include/libtorrent/policy.hpp +++ /dev/null @@ -1,572 +0,0 @@ -/* - -Copyright (c) 2003, 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/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; - - // 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() {} - 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: - unsigned int m_val; - }; - - enum - { - // the limits of the download queue size - min_request_queue = 2, - - // the amount of free upload allowed before - // the peer is choked - free_upload_amount = 4 * 16 * 1024 - }; - - void request_a_block(torrent& t, peer_connection& c); - - class TORRENT_EXPORT policy - { - public: - - policy(torrent* t); - - // this is called every 10 seconds to allow - // for peer choking management - void pulse(); - - 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); - -#ifdef TORRENT_DEBUG - bool has_connection(const peer_connection* p); - - void check_invariant() const; -#endif - -// intended struct layout (on 32 bit architectures) -// offset size alignment field -// 0 12 1 prev_amount_upload, prev_amount_download -// 12 4 4 connection -// 16 2 2 last_optimistically_unchoked -// 18 2 2 last_connected -// 20 16 1 addr -// 36 2 2 port -// 38 2 2 upload_rate_limit -// 40 2 2 download_rate_limit -// 42 1 1 hashfails -// 43 1 1 failcount, connectable, optimistically_unchoked, seed -// 44 1 1 fast_reconnects, trust_points -// 45 1 1 source, pe_support, is_v6_addr -// 46 1 1 on_parole, banned, added_to_dht, supports_utp, -// supports_holepunch -// 47 1 1 -// 48 - struct TORRENT_EXPORT peer - { - peer(); - peer(boost::uint16_t port, bool connectable, int src); - - size_type total_download() const; - size_type total_upload() 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. - // 48 bits can fit 256 Terabytes -#ifdef __SUNPRO_CC - unsigned prev_amount_upload:48; - unsigned prev_amount_download:48; -#else - boost::uint64_t prev_amount_upload:48; - boost::uint64_t prev_amount_download:48; -#endif - - // 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 - - // 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; -#ifdef TORRENT_DEBUG - bool in_use:1; -#endif - }; - - struct TORRENT_EXPORT ipv4_peer : peer - { - ipv4_peer(tcp::endpoint const& ip, bool connectable, int src); - ipv4_peer(libtorrent::address const& a); - - address_v4 addr; - }; - -#if TORRENT_USE_I2P - struct TORRENT_EXPORT i2p_peer : peer - { - i2p_peer(char const* destination, bool connectable, int src); - i2p_peer(char const* destination); - ~i2p_peer(); - - char* destination; - }; -#endif - -#if TORRENT_USE_IPV6 - struct TORRENT_EXPORT ipv6_peer : peer - { - ipv6_peer(tcp::endpoint const& ip, bool connectable, int src); - ipv6_peer(libtorrent::address const& a); - - address_v6::bytes_type addr; - }; -#endif - - int num_peers() const { return 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 - , address const& external_ip) 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 should_erase_immediately(peer const& p) const; - - void erase_peers(); - - peers_t m_peers; - - torrent* m_torrent; - - // 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 - } - - inline policy::ipv4_peer::ipv4_peer(libtorrent::address const& a) - : addr(a.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(strdup(dest)) - { -#if TORRENT_USE_IPV6 - is_v6_addr = false; -#endif - is_i2p_addr = true; - } - inline policy::i2p_peer::i2p_peer(char const* dest) - : destination(strdup(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 - } - - inline policy::ipv6_peer::ipv6_peer(libtorrent::address const& a) - : addr(a.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/libtorrent_utp/include/libtorrent/proxy_base.hpp b/libtorrent_utp/include/libtorrent/proxy_base.hpp deleted file mode 100644 index 701de8101..000000000 --- a/libtorrent_utp/include/libtorrent/proxy_base.hpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - -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_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::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_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 - 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(); - } - - 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/libtorrent_utp/include/libtorrent/ptime.hpp b/libtorrent_utp/include/libtorrent/ptime.hpp deleted file mode 100644 index 5429b597a..000000000 --- a/libtorrent_utp/include/libtorrent/ptime.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - -Copyright (c) 2009, 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 time_duration - { - time_duration() {} - 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); } - boost::int64_t diff; - }; - - // libtorrent time type - struct ptime - { - ptime() {} - explicit ptime(boost::uint64_t t): time(t) {} - ptime& operator+=(time_duration rhs) { time += rhs.diff; return *this; } - ptime& operator-=(time_duration rhs) { time -= rhs.diff; return *this; } - boost::uint64_t 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==(ptime lhs, ptime rhs) - { return lhs.time == rhs.time;} - - inline bool is_negative(time_duration dt) { return dt.diff < 0; } - 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 - -namespace libtorrent -{ - TORRENT_EXPORT ptime time_now_hires(); - TORRENT_EXPORT ptime min_time(); - TORRENT_EXPORT ptime max_time(); - - TORRENT_EXPORT char const* time_now_string(); - TORRENT_EXPORT std::string log_time(); - - TORRENT_EXPORT ptime const& time_now(); -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/puff.hpp b/libtorrent_utp/include/libtorrent/puff.hpp deleted file mode 100644 index 5af5abb0c..000000000 --- a/libtorrent_utp/include/libtorrent/puff.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* 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/libtorrent_utp/include/libtorrent/session.hpp b/libtorrent_utp/include/libtorrent/session.hpp deleted file mode 100644 index ecb0ba913..000000000 --- a/libtorrent_utp/include/libtorrent/session.hpp +++ /dev/null @@ -1,445 +0,0 @@ -/* - -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_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/storage.hpp" - -#ifdef _MSC_VER -# include -#endif - -namespace libtorrent -{ - struct torrent_plugin; - class torrent; - struct ip_filter; - class port_filter; - class connection_queue; - class natpmp; - class upnp; - class alert; - - session_settings min_memory_usage(); - session_settings high_performance_seed(); - - void TORRENT_EXPORT TORRENT_CFG(); - - namespace aux - { - // workaround for microsofts - // hardware exceptions that makes - // it hard to debug stuff -#ifdef _MSC_VER - struct eh_initializer - { - eh_initializer() - { - ::_set_se_translator(straight_to_debugger); - } - - static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) - { throw; } - }; -#else - struct eh_initializer {}; -#endif - struct session_impl; - } - - class TORRENT_EXPORT session_proxy - { - friend class session; - public: - 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 = "." -#define TORRENT_LOGPATH_ARG , std::string logpath -#define TORRENT_LOGPATH , logpath -#else -#define TORRENT_LOGPATH_ARG_DEFAULT -#define TORRENT_LOGPATH_ARG -#define TORRENT_LOGPATH -#endif - - class TORRENT_EXPORT session: public boost::noncopyable, aux::eh_initializer - { - public: - - session(fingerprint const& print = fingerprint("LT" - , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0) - , int flags = start_default_features | add_default_plugins - , int alert_mask = alert::error_notification - TORRENT_LOGPATH_ARG_DEFAULT) - { - TORRENT_CFG(); - init(std::make_pair(0, 0), "0.0.0.0", print, flags, alert_mask TORRENT_LOGPATH); - } - - 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, flags, alert_mask TORRENT_LOGPATH); - } - - ~session(); - - enum save_state_flags_t - { - save_settings = 0x001, - save_dht_settings = 0x002, - save_dht_state = 0x004, - save_proxy = 0x008, - save_i2p_proxy = 0x010, - save_encryption_settings = 0x020, - save_as_map = 0x040 - -#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 - }; - void save_state(entry& e, boost::uint32_t flags = 0xffffffff) const; - void load_state(lazy_entry const& e); - - // returns a list of all torrents in this session - std::vector get_torrents() const; - - io_service& get_io_service(); - - // returns an invalid handle in case the torrent doesn't exist - torrent_handle find_torrent(sha1_hash const& info_hash) const; - - // 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); - -#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 - - session_proxy abort() { return session_proxy(m_impl); } - - void pause(); - void resume(); - bool is_paused() const; - - session_status status() const; - cache_status get_cache_status() const; - - void get_cache_info(sha1_hash const& ih - , std::vector& ret) const; - -#ifndef TORRENT_DISABLE_DHT - void start_dht(); - void stop_dht(); - void set_dht_settings(dht_settings const& settings); -#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 - void add_dht_node(std::pair const& node); - void add_dht_router(std::pair const& node); - bool is_dht_running() const; -#endif - -#ifndef TORRENT_DISABLE_ENCRYPTION - void set_pe_settings(pe_settings const& settings); - pe_settings get_pe_settings() const; -#endif - -#ifndef TORRENT_DISABLE_EXTENSIONS - void add_extension(boost::function(torrent*, void*)> ext); -#endif - -#ifndef TORRENT_DISABLE_GEO_IP - int as_for_ip(address const& addr); - void load_asnum_db(char const* file); - void load_country_db(char const* file); -#if TORRENT_USE_WSTRING - void load_country_db(wchar_t const* file); - void load_asnum_db(wchar_t const* file); -#endif -#endif - -#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 - - void set_ip_filter(ip_filter const& f); - ip_filter get_ip_filter() const; - - void set_port_filter(port_filter const& f); - void set_peer_id(peer_id const& pid); - void set_key(int key); - peer_id id() 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_reuse_address = 1 }; - - bool listen_on( - std::pair const& port_range - , const char* net_interface = 0 - , int flags = 0); - - // returns the port we ended up listening on - unsigned short listen_port() const; - - enum options_t - { - none = 0, - delete_files = 1 - }; - - enum session_flags_t - { - add_default_plugins = 1, - start_default_features = 2 - }; - - void remove_torrent(const torrent_handle& h, int options = none); - - void set_settings(session_settings const& s); - session_settings settings(); - - void set_proxy(proxy_settings const& s); - proxy_settings 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; - -#ifndef TORRENT_DISABLE_DHT - TORRENT_DEPRECATED_PREFIX - void set_dht_proxy(proxy_settings const& s) TORRENT_DEPRECATED; - TORRENT_DEPRECATED_PREFIX - proxy_settings dht_proxy() const TORRENT_DEPRECATED; -#endif -#endif // TORRENT_NO_DEPRECATE - -#if TORRENT_USE_I2P - void set_i2p_proxy(proxy_settings const& s); - proxy_settings i2p_proxy() const; -#endif - -#ifndef TORRENT_NO_DEPRECATE - // 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 - - std::auto_ptr pop_alert(); -#ifndef TORRENT_NO_DEPRECATE - TORRENT_DEPRECATED_PREFIX - void set_severity_level(alert::severity_t s) TORRENT_DEPRECATED; -#endif - void set_alert_mask(int m); - size_t set_alert_queue_size_limit(size_t queue_size_limit_); - - alert const* wait_for_alert(time_duration max_wait); - void set_alert_dispatch(boost::function)> const& fun); - - connection_queue& get_connection_queue(); - - // starts/stops UPnP, NATPMP or LSD port mappers - // they are stopped by default - void start_lsd(); - natpmp* start_natpmp(); - upnp* start_upnp(); - - void stop_lsd(); - void stop_natpmp(); - void stop_upnp(); - - private: - - void init(std::pair listen_range, char const* listen_interface - , fingerprint const& id, int flags, int alert_mask TORRENT_LOGPATH_ARG); - - // data shared between the main thread - // and the working thread - boost::shared_ptr m_impl; - }; - -} - -#endif // TORRENT_SESSION_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/session_settings.hpp b/libtorrent_utp/include/libtorrent/session_settings.hpp deleted file mode 100644 index 43a6335e9..000000000 --- a/libtorrent_utp/include/libtorrent/session_settings.hpp +++ /dev/null @@ -1,1072 +0,0 @@ -/* - -Copyright (c) 2003, 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 - -namespace libtorrent -{ - - struct TORRENT_EXPORT proxy_settings - { - proxy_settings() : port(0), type(none) - , proxy_hostnames(true) {} - - std::string hostname; - int port; - - std::string username; - std::string password; - - enum proxy_type - { - // a plain tcp socket is used, and - // the other settings are ignored. - none, - // socks4 server, requires username. - socks4, - // the hostname and port settings are - // used to connect to the proxy. No - // username or password is sent. - socks5, - // the hostname and port are used to - // connect to the proxy. the username - // and password are used to authenticate - // with the proxy server. - socks5_pw, - // the http proxy is only available for - // tracker and web seed traffic - // assumes anonymous access to proxy - http, - // http proxy with basic authentication - // uses username and password - http_pw, - // route through a i2p SAM proxy - i2p_proxy - }; - - proxy_type type; - - // when set to true, hostname are resolved - // through the proxy (if supported) - bool proxy_hostnames; - }; - - struct TORRENT_EXPORT session_settings - { - session_settings(std::string const& user_agent_ = "libtorrent/" - LIBTORRENT_VERSION) - : version(LIBTORRENT_VERSION_NUM) - , user_agent(user_agent_) - , tracker_completion_timeout(60) - , tracker_receive_timeout(40) - , 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(250) - , max_out_request_queue(200) - , 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(10) - , send_redundant_have(false) - , lazy_bitfields(true) - , 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(256 * 1024) - , handshake_timeout(10) -#ifndef TORRENT_DISABLE_DHT - , use_dht_as_fallback(false) -#endif - , free_torrent_hashes(true) - , upnp_ignore_nonrouters(false) - , send_buffer_watermark(100 * 1024) - , send_buffer_watermark_factor(1) -#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(60) - , 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(8) - , active_seeds(5) - , active_dht_limit(88) // don't announce more than once every 40 seconds - , active_tracker_limit(360) // don't announce to trackers more than once every 5 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(120) - , 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 -#ifndef TORRENT_DISABLE_MLOCK - , lock_disk_cache(false) -#endif - , 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(largest_contiguous) - , read_cache_line_size(128) - , 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) - , dht_max_torrents(3000) - , udp_tracker_token_expiry(60) - , volatile_read_cache(false) - , guided_read_cache(true) - , 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) - , default_peer_upload_rate(0) - , default_peer_download_rate(0) - , broadcast_lsd(false) - , enable_outgoing_utp(true) - , enable_incoming_utp(true) - , enable_outgoing_tcp(true) - , enable_incoming_tcp(true) - , max_pex_peers(200) - , ignore_resume_timestamps(false) - , anonymous_mode(false) - , tick_interval(100) - , 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) - , unchoke_slots_limit(8) - , half_open_limit(0) - , connections_limit(200) - , utp_target_delay(75) // 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 - , utp_delayed_ack(0) // milliseconds - , utp_dynamic_sock_buf(true) - , mixed_mode_algorithm(peer_proportional) - , rate_limit_utp(false) - , listen_queue_size(5) - {} - - // libtorrent version. Used for forward binary compatibility - int version; - - // this is the user agent that will be sent to the tracker - // when doing requests. It is used to identify the client. - // It cannot contain \r or \n - std::string user_agent; - - // the number of seconds to wait until giving up on a - // tracker request if it hasn't finished - int tracker_completion_timeout; - - // the number of seconds where no data is received - // from the tracker until it should be considered - // as timed out - 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 - int stop_tracker_timeout; - - // if the content-length is greater than this value - // the tracker connection will be aborted - int tracker_maximum_response_length; - - // 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. - 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. - 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. - int urlseed_timeout; - - // controls the pipelining size of url-seeds - 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; - - // false to not allow multiple connections from the same - // IP address. true will allow it. - bool allow_multiple_connections_per_ip; - - // the number of times we can fail to connect to a peer - // before we stop retrying it. - 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; - - // this is the timeout for a connection attempt. If - // the connect does not succeed within this time, the - // connection is dropped. The time is specified in seconds. - 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. - 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; - - // if this is true, outgoing bitfields will never be fuil. If the - // client is seed, a few bits will be set to 0, and later filled - // in with have messages. This is 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 - int unchoke_interval; - - // the number of seconds between - // optimistic unchokes - int optimistic_unchoke_interval; - - // if this is set, this IP will be reported do the - // tracker in the ip= parameter. - std::string announce_ip; - - // the num want sent to trackers - int num_want; - - // while we have fewer pieces than this, pick - // random pieces instead of rarest first. - int initial_picker_threshold; - - // the number of allowed pieces to send to peers - // that supports the fast extensions - int allowed_fast_set_size; - - // 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 - enum { no_piece_suggestions = 0, suggest_read_cache = 1 }; - 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 and 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. - int max_queued_disk_bytes; - - // 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; - -#ifndef TORRENT_DISABLE_DHT - // while this is true, the dht will not be used unless the - // tracker is online - bool use_dht_as_fallback; -#endif - - // if this is true, the piece hashes will be freed, in order - // to save memory, once the torrent is seeding. This will - // make the get_torrent_info() function to return an incomplete - // torrent object that cannot be passed back to add_torrent() - bool free_torrent_hashes; - - // when this is true, the upnp port mapper will ignore - // any upnp devices that don't have an address that matches - // our currently configured router. - bool upnp_ignore_nonrouters; - - // 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. 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 - // 1. The default is 1. - int send_buffer_watermark_factor; - -#ifndef TORRENT_NO_DEPRECATE - // deprecated in 0.16 - bool auto_upload_slots; - bool auto_upload_slots_rate_based; -#endif - - enum choking_algorithm_t - { - fixed_slots_choker, - auto_expand_choker, - rate_based_choker, - bittyrant_choker - }; - - int choking_algorithm; - - enum seed_choking_algorithm_t - { - round_robin, - fastest_upload, - anti_leech - }; - - // the choking algorithm to use for seeding torrents - int seed_choking_algorithm; - - // if set to true, peers that participate in a failing - // piece is put in parole mode. i.e. They will only - // download whole pieces until they either fail or pass. - // they are taken out of parole mode as soon as they - // participate in a piece that passes. - bool use_parole_mode; - - // the disk write cache, specified in 16 KiB blocks. - // default is 1024 (= 16 MiB). -1 means automatic, which - // adjusts the cache size depending on the amount - // of physical RAM in the machine. - 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. Default is 60 seconds. - int cache_expiry; - - // when true, the disk I/O thread uses the disk - // cache for caching blocks read from disk too - bool use_read_cache; - - // don't implicitly cache pieces in the read cache, - // only cache pieces that are explicitly asked to be - // cached. - bool explicit_read_cache; - - // the number of seconds between refreshes of - // explicit caches - int explicit_cache_interval; - - enum io_buffer_mode_t - { - enable_os_cache = 0, - disable_os_cache_for_aligned_files = 1, - disable_os_cache = 2 - }; - int disk_io_write_mode; - int disk_io_read_mode; - - bool coalesce_reads; - bool coalesce_writes; - - // if != (0, 0), this is the range of ports that - // outgoing connections will be bound to. This - // is useful for users that have routers that - // allow QoS settings based on local port. - std::pair outgoing_ports; - - // the TOS byte of all peer traffic (including - // web seeds) is set to this value. The default - // is the QBSS scavenger service - // http://qbone.internet2.edu/qbss/ - // For unmarked packets, set to 0 - 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 - // versus the 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 the tracker - 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 this is true, torrents that don't have any significant - // transfers are not counted as active when determining which - // auto managed torrents to pause and resume - 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 eaither 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 - float share_ratio_limit; - float seed_time_ratio_limit; - int seed_time_limit; - - // the interval (in seconds) between optimistic disconnects - // if the disconnects happen and how many peers are disconnected - // is controlled by peer_turnover and peer_turnover_cutoff - int peer_turnover_interval; - - // the percentage of peers to disconnect every - // turnoever interval (if we're at the peer limit) - // defaults to 2/50:th - float peer_turnover; - - // when we are connected to more than - // limit * peer_turnover_cutoff peers - // disconnect peer_turnover fraction - // of the peers - float peer_turnover_cutoff; - - // if this is true (default) connections where both - // ends have no utility in keeping the connection open - // are closed. for instance if both ends have completed - // their downloads - bool close_redundant_connections; - - // the number of seconds between scrapes of - // queued torrents (auto managed and paused) - int auto_scrape_interval; - - // the minimum number of seconds between any - // automatic scrape (regardless of torrent) - int auto_scrape_min_interval; - - // the max number of peers in the peer list - // per torrent. This is the peers we know - // about, not necessarily connected to. - int max_peerlist_size; - - // when a torrent is paused, this is the max peer - // list size that's used - int max_paused_peerlist_size; - - // any announce intervals reported from a tracker - // that is lower than this, will be clamped to this - // value. It's specified in seconds - int min_announce_interval; - - // if true, partial pieces are picked before pieces - // that are more rare - 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; - - // this announces to all trackers within the current - // tier. Trackers within a tier are supposed to share - // peers, this could be used for trackers that don't, - // and require the clients to announce to all of them. - bool announce_to_all_trackers; - - // if set to true, multi tracker torrents are treated - // the same way uTorrent treats them. It defaults to - // false in order to comply with the extension definition. - // When this is enabled, one tracker from each tier is - // announced - bool announce_to_all_tiers; - - // when this is set to true, if there is a tracker entry - // with udp:// protocol, it is preferred over the same - // tracker over http://. - bool prefer_udp_trackers; - - // when set to true, a piece has to have been forwarded - // to a third peer before another one is handed out - bool strict_super_seeding; - - // the number of pieces to send to each peer when seeding - // before rotating to a new peer - int seeding_piece_quota; - - // the maximum number of sparse regions before starting - // to prioritize pieces close to other pieces (to maintain - // the number of sparse regions). This is set to 30000 on - // windows because windows vista has a new limit on the - // numbers of sparse regions one file may have - // if it is set to 0 this behavior is disabled - // this is a hack to avoid a terrible bug on windows - // don't use unless you have to, it screws with rarest-first - // piece selection, and reduces swarm performance - int max_sparse_regions; - -#ifndef TORRENT_DISABLE_MLOCK - // if this is set to true, the memory allocated for the - // disk cache will be locked in physical RAM, never to - // be swapped out - bool lock_disk_cache; -#endif - - // the number of times to reject requests while being - // choked before disconnecting a peer for being malicious - int max_rejects; - - // sets the socket send and receive buffer sizes - // 0 means OS default - int recv_socket_buffer_size; - int send_socket_buffer_size; - - // if this is set to false, the hashing will be - // optimized for memory usage instead of the - // number of read operations - bool optimize_hashing_for_speed; - - // if > 0, file checks will have a short - // delay between disk operations, to make it - // less intrusive on the system as a whole - // blocking the disk. This delay is specified - // in milliseconds and the delay will be this - // long per 16kiB block - // the default of 10 ms/16kiB will limit - // the checking rate to 1.6 MiB per second - int file_checks_delay_per_block; - - enum disk_cache_algo_t - { lru, largest_contiguous }; - - disk_cache_algo_t disk_cache_algorithm; - - // the number of blocks that will be read ahead - // when reading a block into the read 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; - - // this is the number of seconds a disk failure - // occurs until libtorrent will re-try. - int optimistic_disk_retry; - - // when set to true, all data downloaded from - // peers will be assumed to be correct, and not - // tested to match the hashes in the torrent - // this is only useful for simulation and - // testing purposes (typically combined with - // disabled_storage) - 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 pieces that a peer can - // suggest to use before we start dropping - // previous suggested piece - int max_suggest_pieces; - - // if set to true, requests that have have not been - // satisfied after the equivalence of the entire - // request queue has been received, will be considered lost - bool drop_skipped_requests; - - // if this is set to true, the disk I/O will be - // run at lower-than-normal priority. This is - // intended to make the machine more responsive - // to foreground tasks, while bittorrent runs - // in the background - bool low_prio_disk; - - // number of seconds between local service announces for - // torrents. Defaults to 5 minutes - int local_service_announce_interval; - - // number of seconds between DHT announces for - // torrents. Defaults to 15 minutes - int dht_announce_interval; - - // this is the max number of torrents the DHT will track - int dht_max_torrents; - - // the number of seconds a connection ID received - // from a UDP tracker is valid for. This is specified - // as 60 seconds - int udp_tracker_token_expiry; - - // if this is set to true, any block read from the - // disk cache will be dropped from the cache immediately - // following. This may be useful if the block is not - // expected to be hit again. It would save some memory - bool volatile_read_cache; - - // if this is set to true, the size of the cache line - // generated by a particular read request depends on the - // rate you're sending to that peer. This optimizes the - // memory usage of the disk read cache by reading - // further ahead for peers that you're uploading at high - // rates to - bool guided_read_cache; - - // this is the default minimum time any read cache line - // is kept in the cache. - int default_cache_min_age; - - // the global number of optimistic unchokes - // 0 means automatic - int num_optimistic_unchoke_slots; - - // if set to true, files won't have their atime updated - // on disk reads. This works on linux - bool no_atime_storage; - - // === BitTyrant unchoker settings == - - // when using BitTyrant choker, this is the default - // assumed reciprocation rate. This is where each peer starts - int default_est_reciprocation_rate; - - // this is the increase of the estimated reciprocation rate - // in percent. We increase by this amount once every unchoke - // interval that we are choked by the other peer and we have - // unchoked them - int increase_est_reciprocation_rate; - - // each unchoke interval that we stay unchoked by the other - // peer, and we have unchoked this peer as well, we decrease - // our estimate of the reciprocation rate, since we might have - // over-estimated it - int decrease_est_reciprocation_rate; - - // if set to true, an incoming connection to a torrent that's - // paused and auto-managed will make the torrent start. - 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; - - // if set to true, libtorrent won't request a piece multiple times - // until every piece is requested - bool strict_end_game_mode; - - // each peer will have these limits set on it - int default_peer_upload_rate; - int default_peer_download_rate; - - // if this is true, the broadcast socket will not only use IP multicast - // but also send the messages on the broadcast address. This is false by - // default in order to avoid flooding networks for no good reason. If - // a network is known not to support multicast, this can be enabled - bool broadcast_lsd; - - // when set to true, libtorrent will try to make outgoing utp connections - bool enable_outgoing_utp; - - // if set to false, libtorrent will reject incoming utp connections - bool enable_incoming_utp; - - // when set to false, no outgoing TCP connections will be made - bool enable_outgoing_tcp; - - // if set to false, libtorrent will reject incoming tcp connections - 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; - - // when set to true, the file modification time is ignored when loading - // resume data. The resume data includes the expected timestamp of each - // file and is typically compared to make sure the files haven't changed - // since the last session - bool ignore_resume_timestamps; - - // when this is true, libtorrent will take actions to make sure any - // privacy sensitive information is leaked out from the client. This - // mode is assumed to be combined with using a proxy for all your - // traffic. With this option, your true IP address will not be exposed - bool anonymous_mode; - - // the number of milliseconds between internal ticks. Should be no - // more than one second (i.e. 1000). - int tick_interval; - - // specifies whether downloads from web seeds is reported to the - // tracker or not. Defaults to on - bool report_web_seed_downloads; - - // this is the target share ratio for share-mode torrents - int share_mode_target; - - // max upload rate in bytes per second for the session - int upload_rate_limit; - - // max download rate in bytes per second for the session - int download_rate_limit; - - // max upload rate in bytes per second for peers on the local - // network, in the session - int local_upload_rate_limit; - - // max download rate in bytes per second for peers on the local - // network, in the session - int local_download_rate_limit; - - // the max number of unchoke slots in the session (might be - // overridden by unchoke algorithm) - int unchoke_slots_limit; - - // the max number of half-open TCP connections - int half_open_limit; - - // the max number of connections in the session - int connections_limit; - - // target delay, milliseconds - int utp_target_delay; - - // max number of bytes to increase cwnd per rtt in uTP - // congestion controller - int utp_gain_factor; - - // the shortest allowed uTP connection timeout in milliseconds - // defaults to 500 milliseconds. The shorter timeout, the - // faster the connection recovers from a loss of an entire window - int utp_min_timeout; - - // the number of SYN packets that are sent before giving up - 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 to send a packet before giving up - int utp_num_resends; - - // initial timeout for uTP SYN packets - int utp_connect_timeout; - - // number of milliseconds of delaying ACKing packets the most - int utp_delayed_ack; - - // set to true if the uTP socket buffer size is allowed to increase - // dynamically based on the NIC MTU setting. This is true by default - // and improves uTP performance for networks with larger frame sizes - // including loopback - bool utp_dynamic_sock_buf; - - 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 - - }; - // the algorithm to use to balance bandwidth between tcp - // connections and uTP connections - int mixed_mode_algorithm; - - // set to true if uTP connections should be rate limited - // defaults to false - bool rate_limit_utp; - - // this is the number passed in to listen(). i.e. - // the number of connections to accept while we're - // not waiting in an accept() call. - int listen_queue_size; - }; - -#ifndef TORRENT_DISABLE_DHT - struct dht_settings - { - dht_settings() - : max_peers_reply(100) - , search_branching(5) -#ifndef TORRENT_NO_DEPRECATE - , service_port(0) -#endif - , max_fail_count(20) - , max_torrent_search_reply(20) - {} - - // the maximum number of peers to send in a - // reply to get_peers - int max_peers_reply; - - // the number of simultanous "connections" when - // searching the DHT. - 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 times a node can fail - // in a row before it is removed from the table. - int max_fail_count; - - // the max number of torrents to return in a - // torrent search query to the DHT - int max_torrent_search_reply; - }; -#endif - -#ifndef TORRENT_DISABLE_ENCRYPTION - - struct pe_settings - { - pe_settings() - : out_enc_policy(enabled) - , in_enc_policy(enabled) - , allowed_enc_level(both) - , prefer_rc4(false) - {} - - enum enc_policy - { - forced, // disallow non encrypted connections - enabled, // allow encrypted and non encrypted connections - disabled // disallow encrypted connections - }; - - enum enc_level - { - plaintext, // use only plaintext encryption - rc4, // use only rc4 encryption - both // allow both - }; - - enc_policy out_enc_policy; - enc_policy in_enc_policy; - - enc_level 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 - -} - -#endif diff --git a/libtorrent_utp/include/libtorrent/session_status.hpp b/libtorrent_utp/include/libtorrent/session_status.hpp deleted file mode 100644 index 188951503..000000000 --- a/libtorrent_utp/include/libtorrent/session_status.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - -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_STATUS_HPP_INCLUDED -#define TORRENT_SESSION_STATUS_HPP_INCLUDED - -#include "libtorrent/config.hpp" -#include "libtorrent/size_type.hpp" -#include - -namespace libtorrent -{ - -#ifndef TORRENT_DISABLE_DHT - - struct dht_lookup - { - char const* type; - int outstanding_requests; - int timeouts; - int responses; - int branch_factor; - int nodes_left; - }; - -#endif - - struct utp_status - { - int num_idle; - int num_syn_sent; - int num_connected; - int num_fin_sent; - int num_close_wait; - }; - - struct TORRENT_EXPORT session_status - { - bool has_incoming_connections; - - int upload_rate; - int download_rate; - size_type total_download; - size_type total_upload; - - int payload_upload_rate; - int payload_download_rate; - size_type total_payload_download; - size_type total_payload_upload; - - int ip_overhead_upload_rate; - int ip_overhead_download_rate; - size_type total_ip_overhead_download; - size_type total_ip_overhead_upload; - - int dht_upload_rate; - int dht_download_rate; - size_type total_dht_download; - size_type total_dht_upload; - - int tracker_upload_rate; - int tracker_download_rate; - size_type total_tracker_download; - size_type total_tracker_upload; - - size_type total_redundant_bytes; - size_type total_failed_bytes; - - int num_peers; - int num_unchoked; - int allowed_upload_slots; - - int up_bandwidth_queue; - int down_bandwidth_queue; - - int up_bandwidth_bytes_queue; - int down_bandwidth_bytes_queue; - - int optimistic_unchoke_counter; - int unchoke_counter; - -#ifndef TORRENT_DISABLE_DHT - int dht_nodes; - int dht_node_cache; - int dht_torrents; - size_type dht_global_nodes; - std::vector active_requests; - int dht_total_allocations; -#endif - - utp_status utp_stats; - - int peerlist_size; - }; - -} - -#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/settings.hpp b/libtorrent_utp/include/libtorrent/settings.hpp deleted file mode 100644 index 872128650..000000000 --- a/libtorrent_utp/include/libtorrent/settings.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - -Copyright (c) 2010, 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; - struct entry; - - enum { std_string = 0, character = 1, short_integer = 2 - , integer = 3, floating_point = 4, boolean = 5}; - - // 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/libtorrent_utp/include/libtorrent/size_type.hpp b/libtorrent_utp/include/libtorrent/size_type.hpp deleted file mode 100644 index ce5e1e1f9..000000000 --- a/libtorrent_utp/include/libtorrent/size_type.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - -Copyright (c) 2003, 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/libtorrent_utp/include/libtorrent/sliding_average.hpp b/libtorrent_utp/include/libtorrent/sliding_average.hpp deleted file mode 100644 index 3bbdb6c15..000000000 --- a/libtorrent_utp/include/libtorrent/sliding_average.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Copyright (c) 2010, 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 - -namespace libtorrent -{ -// a sliding average accumulator. Add samples to it and it -// keeps track of a sliding mean value and an average deviation -// from that average. -template -struct sliding_average -{ - sliding_average(): m_mean(-1), m_average_deviation(-1) {} - - void add_sample(int s) - { - if (m_mean == -1) - { - m_mean = s; - return; - } - int deviation = abs(m_mean - s); - - m_mean = m_mean - m_mean / history_size + s / history_size; - - if (m_average_deviation == -1) - { - m_average_deviation = deviation; - return; - } - m_average_deviation = m_average_deviation - m_average_deviation - / history_size + deviation / history_size; - } - - int mean() const { return m_mean != -1 ? m_mean : 0; } - int avg_deviation() const { return m_average_deviation != -1 ? m_average_deviation : 0; } - -private: - int m_mean; - int m_average_deviation; -}; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/socket.hpp b/libtorrent_utp/include/libtorrent/socket.hpp deleted file mode 100644 index aeaa7e376..000000000 --- a/libtorrent_utp/include/libtorrent/socket.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - -Copyright (c) 2003, 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 - 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 - -#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 - - struct type_of_service - { - 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 - char const* data(Protocol const&) const { return &m_value; } - template - size_t size(Protocol const&) const { return sizeof(m_value); } - char 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/libtorrent_utp/include/libtorrent/socket_io.hpp b/libtorrent_utp/include/libtorrent/socket_io.hpp deleted file mode 100644 index 9edbed077..000000000 --- a/libtorrent_utp/include/libtorrent/socket_io.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - -Copyright (c) 2009, 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 - -namespace libtorrent -{ - TORRENT_EXPORT std::string print_address(address const& addr); - TORRENT_EXPORT std::string print_endpoint(tcp::endpoint const& ep); - TORRENT_EXPORT std::string print_endpoint(udp::endpoint const& ep); - - 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 - } - } - - } - - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/socket_type.hpp b/libtorrent_utp/include/libtorrent/socket_type.hpp deleted file mode 100644 index c6244e9be..000000000 --- a/libtorrent_utp/include/libtorrent/socket_type.hpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - -Copyright (c) 2009, 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 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; - -#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; - -#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 }; }; -#endif - - struct TORRENT_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; - -#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; - - - 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) } - - 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 = max8< - 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) -#else - , 0, 0, 0 -#endif - >::value - }; - - size_type m_data[(storage_size + sizeof(size_type) - 1) / sizeof(size_type)]; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/socket_type_fwd.hpp b/libtorrent_utp/include/libtorrent/socket_type_fwd.hpp deleted file mode 100644 index 26a134274..000000000 --- a/libtorrent_utp/include/libtorrent/socket_type_fwd.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - -Copyright (c) 2009, 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/libtorrent_utp/include/libtorrent/socks5_stream.hpp b/libtorrent_utp/include/libtorrent/socks5_stream.hpp deleted file mode 100644 index 0922199b5..000000000 --- a/libtorrent_utp/include/libtorrent/socks5_stream.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - -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_SOCKS5_STREAM_HPP_INCLUDED -#define TORRENT_SOCKS5_STREAM_HPP_INCLUDED - -#include -#include -#include "libtorrent/proxy_base.hpp" - -namespace libtorrent { - - namespace socks_error { - - 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 - }; - } - -#if BOOST_VERSION < 103500 -typedef asio::error::error_category socks_error_category; -#else - -struct TORRENT_EXPORT socks_error_category : boost::system::error_category -{ - virtual const char* name() const; - virtual std::string message(int ev) const; - virtual boost::system::error_condition default_error_condition(int ev) const - { return boost::system::error_condition(ev, *this); } -}; - -#endif - -extern socks_error_category 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)); - - 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; -}; - -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/ssl_stream.hpp b/libtorrent_utp/include/libtorrent/ssl_stream.hpp deleted file mode 100644 index 8a8a07e95..000000000 --- a/libtorrent_utp/include/libtorrent/ssl_stream.hpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - -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_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 asio::ssl::stream 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; - typedef typename asio::ssl::stream sock_type; - - 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_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 - 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; - } - -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/libtorrent_utp/include/libtorrent/stat.hpp b/libtorrent_utp/include/libtorrent/stat.hpp deleted file mode 100644 index 1d4961e80..000000000 --- a/libtorrent_utp/include/libtorrent/stat.hpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - -Copyright (c) 2003, 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" - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING -#include "libtorrent/debug.hpp" // for logger -#endif - -namespace libtorrent -{ - class TORRENT_EXPORT stat_channel - { - public: - - stat_channel() - : m_counter(0) - , m_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_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_average = 0; - m_total_counter = 0; - } - - private: - - // the accumulator for this second. - int m_counter; - - // sliding average - int m_average; - - // total counters - size_type m_total_counter; - }; - - class TORRENT_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 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() -#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() -#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/libtorrent_utp/include/libtorrent/storage.hpp b/libtorrent_utp/include/libtorrent/storage.hpp deleted file mode 100644 index 2eb18ab81..000000000 --- a/libtorrent_utp/include/libtorrent/storage.hpp +++ /dev/null @@ -1,459 +0,0 @@ -/* - -Copyright (c) 2003, 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 -#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" - -namespace libtorrent -{ - class session; - struct file_pool; - struct disk_io_job; - struct disk_buffer_pool; - - TORRENT_EXPORT std::vector > get_filesizes( - file_storage const& t - , std::string const& p); - - TORRENT_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_EXPORT file_allocation_failed: std::exception - { - file_allocation_failed(const char* error_msg): m_msg(error_msg) {} - virtual const char* what() const throw() { return m_msg.c_str(); } - virtual ~file_allocation_failed() throw() {} - std::string m_msg; - }; - - struct TORRENT_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; - }; - - struct TORRENT_EXPORT storage_interface - { - storage_interface(): m_disk_pool(0), m_settings(0) {} - // create directories and set file sizes - // if allocate_files is true. - // allocate_files is true if allocation mode - // is set to full and sparse files are supported - // false return value indicates an error - virtual bool initialize(bool allocate_files) = 0; - - virtual bool has_any_file() = 0; - - virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs); - virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs); - - // 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; - - virtual size_type physical_offset(int slot, int offset) = 0; - - // returns the end of the sparse region the slot 'start' - // resides in i.e. the next slot with content. If start - // is not in a sparse region, start itself is returned - virtual int sparse_end(int start) const { return start; } - - // non-zero return value indicates an error - virtual bool move_storage(std::string const& save_path) = 0; - - // verify storage dependent fast resume entries - virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) = 0; - - // write storage dependent fast resume entries - virtual bool write_resume_data(entry& rd) const = 0; - - // moves (or copies) the content in src_slot to dst_slot - virtual bool move_slot(int src_slot, int dst_slot) = 0; - - // swaps the data in slot1 and slot2 - virtual bool swap_slots(int slot1, int slot2) = 0; - - // swaps the puts the data in slot1 in slot2, the data in slot2 - // in slot3 and the data in slot3 in slot1 - virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0; - - // this will close all open files that are opened for - // writing. This is called when a torrent has finished - // downloading. - // non-zero return value indicates an error - virtual bool release_files() = 0; - - // this will rename the file specified by index. - virtual bool rename_file(int index, std::string const& new_filename) = 0; - - // this will close all open files and delete them - // non-zero return value indicates an error - virtual bool delete_files() = 0; - - virtual void finalize_file(int file) {} - - disk_buffer_pool* disk_pool() { return m_disk_pool; } - session_settings const& settings() const { return *m_settings; } - - void set_error(std::string const& file, error_code const& ec) const - { - m_error_file = file; - m_error = ec; - } - - error_code const& error() const { return m_error; } - std::string const& error_file() const { return m_error_file; } - void clear_error() { m_error = error_code(); m_error_file.resize(0); } - - mutable error_code m_error; - mutable std::string m_error_file; - - virtual ~storage_interface() {} - - disk_buffer_pool* m_disk_pool; - session_settings* m_settings; - }; - - struct disk_io_thread; - - class TORRENT_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_finalize_file(int file); - - void async_check_fastresume(lazy_entry const* resume_data - , boost::function const& handler); - - void async_check_files(boost::function const& handler); - - int queued_bytes() const; - - 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); - - void 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 - , 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 - }; - - 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; } - int last_operation() const { return m_last_op; } - 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); - - bool compact_allocation() const - { return m_storage_mode == storage_mode_compact; } - -#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); - - 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); - - void finalize_file(int index); - - // 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 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); } - - int move_storage_impl(std::string const& save_path); - - int allocate_slot_for_piece(int piece_index); -#ifdef TORRENT_DEBUG - void check_invariant() const; -#ifdef TORRENT_STORAGE_DEBUG - void debug_log() const; -#endif -#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 - disk_buffer_holder m_scratch_buffer; - disk_buffer_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; - - // the last operation we did (read or write) - int m_last_op; - - // 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/libtorrent_utp/include/libtorrent/storage_defs.hpp b/libtorrent_utp/include/libtorrent/storage_defs.hpp deleted file mode 100644 index e6d3098f4..000000000 --- a/libtorrent_utp/include/libtorrent/storage_defs.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - -Copyright (c) 2003, 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; - - enum storage_mode_t - { - storage_mode_allocate = 0, - storage_mode_sparse, - storage_mode_compact - }; - - typedef boost::function const&)> storage_constructor_type; - - TORRENT_EXPORT storage_interface* default_storage_constructor( - file_storage const&, file_storage const* mapped, std::string const&, file_pool& - , std::vector const&); - - 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/libtorrent_utp/include/libtorrent/thread.hpp b/libtorrent_utp/include/libtorrent/thread.hpp deleted file mode 100644 index cdd7dc15c..000000000 --- a/libtorrent_utp/include/libtorrent/thread.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - -Copyright (c) 2009, 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" - -#if defined TORRENT_WINDOWS || defined TORRENT_CYGWIN -// asio assumes that the windows error codes are defined already -#include -#endif - -#include -#include -#include - -namespace libtorrent -{ - typedef boost::asio::detail::thread thread; - typedef boost::asio::detail::mutex mutex; - typedef boost::asio::detail::event event; - - void sleep(int milliseconds); - - struct condition - { - condition(); - ~condition(); - void wait(mutex::scoped_lock& l); - void signal_all(mutex::scoped_lock& l); - 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; -#else -#error not implemented -#endif - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/time.hpp b/libtorrent_utp/include/libtorrent/time.hpp deleted file mode 100644 index 8a44a65ef..000000000 --- a/libtorrent_utp/include/libtorrent/time.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - -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_TIME_HPP_INCLUDED -#define TORRENT_TIME_HPP_INCLUDED - -#include -#include "libtorrent/config.hpp" -#include "libtorrent/ptime.hpp" -#include -#include - -namespace libtorrent -{ - TORRENT_EXPORT char const* time_now_string(); - TORRENT_EXPORT std::string log_time(); - - TORRENT_EXPORT ptime time_now_hires(); - TORRENT_EXPORT ptime min_time(); - TORRENT_EXPORT ptime max_time(); - -#if defined TORRENT_USE_BOOST_DATE_TIME - - TORRENT_EXPORT time_duration seconds(int s); - TORRENT_EXPORT time_duration milliseconds(int s); - TORRENT_EXPORT time_duration microsec(int s); - TORRENT_EXPORT time_duration minutes(int s); - TORRENT_EXPORT time_duration hours(int s); - - TORRENT_EXPORT int total_seconds(time_duration td); - TORRENT_EXPORT int total_milliseconds(time_duration td); - TORRENT_EXPORT boost::int64_t total_microseconds(time_duration td); - -#elif defined TORRENT_USE_QUERY_PERFORMANCE_TIMER - - namespace aux - { - TORRENT_EXPORT boost::int64_t performance_counter_to_microseconds(boost::int64_t pc); - TORRENT_EXPORT boost::int64_t microseconds_to_performance_counter(boost::int64_t ms); - } - - inline int total_seconds(time_duration td) - { - return int(aux::performance_counter_to_microseconds(td.diff) - / 1000000); - } - inline int total_milliseconds(time_duration td) - { - return int(aux::performance_counter_to_microseconds(td.diff) - / 1000); - } - inline boost::int64_t total_microseconds(time_duration td) - { - return aux::performance_counter_to_microseconds(td.diff); - } - - inline time_duration microsec(boost::int64_t s) - { - return time_duration(aux::microseconds_to_performance_counter(s)); - } - inline time_duration milliseconds(boost::int64_t s) - { - return time_duration(aux::microseconds_to_performance_counter( - s * 1000)); - } - inline time_duration seconds(boost::int64_t s) - { - return time_duration(aux::microseconds_to_performance_counter( - s * 1000000)); - } - inline time_duration minutes(boost::int64_t s) - { - return time_duration(aux::microseconds_to_performance_counter( - s * 1000000 * 60)); - } - inline time_duration hours(boost::int64_t s) - { - return time_duration(aux::microseconds_to_performance_counter( - s * 1000000 * 60 * 60)); - } - -#elif TORRENT_USE_CLOCK_GETTIME || TORRENT_USE_SYSTEM_TIME || TORRENT_USE_ABSOLUTE_TIME - - inline int total_seconds(time_duration td) - { return td.diff / 1000000; } - inline int total_milliseconds(time_duration td) - { return td.diff / 1000; } - inline boost::int64_t total_microseconds(time_duration td) - { return td.diff; } - - inline time_duration microsec(boost::int64_t s) - { return time_duration(s); } - inline time_duration milliseconds(boost::int64_t s) - { return time_duration(s * 1000); } - inline time_duration seconds(boost::int64_t s) - { return time_duration(s * 1000000); } - inline time_duration minutes(boost::int64_t s) - { return time_duration(s * 1000000 * 60); } - 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/libtorrent_utp/include/libtorrent/timestamp_history.hpp b/libtorrent_utp/include/libtorrent/timestamp_history.hpp deleted file mode 100644 index 0ad977d50..000000000 --- a/libtorrent_utp/include/libtorrent/timestamp_history.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Copyright (c) 2009, 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/assert.hpp" - -namespace libtorrent { - -// timestamp history keeps a history of the lowest timestamps we've -// seen in the last 20 minutes -struct 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/libtorrent_utp/include/libtorrent/tommath.h b/libtorrent_utp/include/libtorrent/tommath.h deleted file mode 100644 index 1accb0011..000000000 --- a/libtorrent_utp/include/libtorrent/tommath.h +++ /dev/null @@ -1,584 +0,0 @@ -/* 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/libtorrent_utp/include/libtorrent/tommath_class.h b/libtorrent_utp/include/libtorrent/tommath_class.h deleted file mode 100644 index af50ecf06..000000000 --- a/libtorrent_utp/include/libtorrent/tommath_class.h +++ /dev/null @@ -1,1000 +0,0 @@ -#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/libtorrent_utp/include/libtorrent/tommath_superclass.h b/libtorrent_utp/include/libtorrent/tommath_superclass.h deleted file mode 100644 index 641d5b183..000000000 --- a/libtorrent_utp/include/libtorrent/tommath_superclass.h +++ /dev/null @@ -1,84 +0,0 @@ -/* 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/libtorrent_utp/include/libtorrent/torrent.hpp b/libtorrent_utp/include/libtorrent/torrent.hpp deleted file mode 100644 index 3a9360d39..000000000 --- a/libtorrent_utp/include/libtorrent/torrent.hpp +++ /dev/null @@ -1,1250 +0,0 @@ -/* - -Copyright (c) 2003, 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 - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#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 - -namespace libtorrent -{ -#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; - struct bt_peer_connection; - - namespace aux - { - struct session_impl; - struct piece_checker_data; - } - - // a torrent is a class that holds information - // for a specific download. It updates itself against - // the tracker - class TORRENT_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); - ~torrent(); - -#ifndef TORRENT_DISABLE_ENCRYPTION - sha1_hash const& obfuscated_hash() const - { return m_obfuscated_hash; } -#endif - - sha1_hash const& info_hash() const - { return m_torrent_file->info_hash(); } - - // starts the announce timer - void start(); - -#ifndef TORRENT_DISABLE_EXTENSIONS - void add_extension(boost::shared_ptr); - void add_extension(boost::function(torrent*, void*)> const& ext - , void* userdata); -#endif - -#ifdef TORRENT_DEBUG - 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()) && !super_seeding(); } - - 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); - - struct read_piece_struct - { - boost::shared_array piece_data; - int blocks_left; - bool fail; - }; - 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; } - - 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); - - 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; } - - 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 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; - - void 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 set_piece_deadline(int piece, int t, int flags); - void update_piece_priorities(); - - torrent_status status(boost::uint32_t flags) const; - - 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); - - void set_ratio(float r) - { TORRENT_ASSERT(r >= 0.0f); m_ratio = r; } - - float ratio() const - { return m_ratio; } - - 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; - } - -#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().anonymous_mode; - } -#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) - { - m_web_seeds.push_back(web_seed_entry(url, 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) - { - m_web_seeds.push_back(web_seed_entry(url, type, auth, extra_headers)); - } - - void 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()) m_web_seeds.erase(i); - } - - void disconnect_web_seed(peer_connection* p) - { - std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() - , (boost::bind(&web_seed_entry::connection, _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 - (*m_ses.m_logger) << time_now_string() << " disconnect_web_seed: " << i->url << "\n"; -#endif - TORRENT_ASSERT(i->connection); - i->connection = 0; - } - - void retry_web_seed(peer_connection* p, int retry = 0); - - void remove_web_seed(peer_connection* p) - { - std::list::iterator i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() - , (boost::bind(&web_seed_entry::connection, _1) == p)); - TORRENT_ASSERT(i != m_web_seeds.end()); - if (i == m_web_seeds.end()) return; - m_web_seeds.erase(i); - } - - 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 give_connect_points(int points); - 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, 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); - - // 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; - - // 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(); - void force_tracker_request(ptime); - 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 - { return m_super_seeding; } - - 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 - { - return has_picker()?m_picker->have_piece(index):true; - } - - // called when we learn that we have a piece - // only once per piece - void we_have(int index); - - int num_have() const - { - 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) - { - if (m_picker.get()) - { - TORRENT_ASSERT(!is_seed()); - m_picker->inc_refcount(index); - } -#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) - { - if (m_picker.get()) - { - TORRENT_ASSERT(!is_seed()); - m_picker->inc_refcount(bits); - } -#ifdef TORRENT_DEBUG - else - { - TORRENT_ASSERT(is_seed()); - } -#endif - } - - void peer_has_all() - { - if (m_picker.get()) - { - TORRENT_ASSERT(!is_seed()); - m_picker->inc_refcount_all(); - } -#ifdef TORRENT_DEBUG - else - { - TORRENT_ASSERT(is_seed()); - } -#endif - } - - void peer_lost(int index) - { - if (m_picker.get()) - { - TORRENT_ASSERT(!is_seed()); - m_picker->dec_refcount(index); - } -#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 const& 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); - - // 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); - - void add_redundant_bytes(int b); - 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; - } - - 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; } - - std::vector const& trackers() const - { return m_trackers; } - - void replace_trackers(std::vector const& urls); - void 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 std::string& line); -#endif - - // DEBUG -#ifdef TORRENT_DEBUG - void check_invariant() const; -#endif - -// -------------------------------------------- - // RESOURCE MANAGEMENT - - void add_free_upload(int diff) { m_available_free_upload += diff; } - - 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); - int upload_limit() const; - void set_download_limit(int limit); - int download_limit() const; - - void set_max_uploads(int limit); - int max_uploads() const { return m_max_uploads; } - void set_max_connections(int limit); - int max_connections() const { return m_max_connections; } - - void move_storage(std::string const& save_path); - - // 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); - - int sequence_number() const { return m_sequence_number; } - - bool seed_mode() const { return m_seed_mode; } - void leave_seed_mode(bool seed) - { - if (!m_seed_mode) return; - m_seed_mode = false; - // seed is false if we turned out not - // to be a seed after all - if (!seed) force_recheck(); - m_num_verified = 0; - m_verified.free(); - } - bool all_verified() const - { return 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) - { - 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); - } - - bool add_merkle_nodes(std::map const& n, int piece); - - // this is called once periodically for torrents - // that are not private - void lsd_announce(); - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - static void print_size(logger& l); -#endif - - void update_last_upload() { m_last_upload = 0; } - - 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_aborted(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 queue_torrent_check(); - void dequeue_torrent_check(); - - 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. - // 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_DEBUG - public: -#endif - std::set m_connections; -#ifdef TORRENT_DEBUG - private: -#endif - - // 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; - - 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 - - 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; - bool operator<(time_critical_piece const& rhs) const - { return deadline < rhs.deadline; } - }; - - // this list is sorted by time_critical_piece::deadline - std::list 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; - - // 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; - -#ifndef TORRENT_DISABLE_ENCRYPTION - // this is SHA1("req2" + info-hash), used for - // encrypted hand shakes - sha1_hash m_obfuscated_hash; -#endif - - // the upload/download ratio that each peer - // tries to maintain. - // 0 is infinite - float m_ratio; - - // free download we have got that hasn't - // been distributed yet. - boost::uint32_t m_available_free_upload; - - // 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 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_seen_complete; - time_t m_last_saved_resume; - - // ============================== - // 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; - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - // 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; -#else - unsigned int m_dummy_padding_bits_to_align:2; -#endif - - // 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; - - // this is the deficit counter in the Deficit Round Robin - // used to determine which torrent gets the next - // connection attempt. See: - // http://www.ecs.umass.edu/ece/wolf/courses/ECE697J/papers/DRR.pdf - // The quanta assigned to each torrent depends on the torrents - // priority, whether it's a seed and the number of connected - // peers it has. This has the effect that some torrents - // will have more connection attempts than other. Each - // connection attempt costs 100 points from the deficit - // counter. points are deducted in try_connect_peer and - // increased in give_connect_points. Outside of the - // torrent object, these points are called connect_points. - boost::uint8_t m_deficit_counter; - - // 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; - - // the number of bytes of padding files - unsigned int m_padding:24; - - // the sequence number for this torrent, this is a - // monotonically increasing number for each added torrent - boost::int16_t m_sequence_number; - - // the scrape data from the tracker response, this - // is optional and may be 0xffffff - unsigned int m_complete:24; - - // this is the priority of the torrent. The higher - // the value is, the more bandwidth is assigned to - // the torrent's peers - boost::uint8_t m_priority; - - // the scrape data from the tracker response, this - // is optional and may be 0xffffff - unsigned int m_incomplete:24; - - // progress parts per million (the number of - // millionths of completeness) - unsigned int m_progress_ppm:20; - - // 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; - - // m_num_verified = m_verified.count() - boost::uint16_t m_num_verified; - - // the number of seconds since the last scrape request to - // one of the trackers in this torrent - boost::uint16_t m_last_scrape; - - // the number of seconds since the last piece passed for - // this torrent - boost::uint16_t m_last_download; - - // the number of seconds since the last byte was uploaded - // from this torrent - boost::uint16_t m_last_upload; - - // the scrape data from the tracker response, this - // is optional and may be 0xffffff - unsigned int m_downloaders:24; - - // round-robin index into m_interfaces - mutable boost::uint8_t m_interface_index; - - // 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; - }; -} - -#endif // TORRENT_TORRENT_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/torrent_handle.hpp b/libtorrent_utp/include/libtorrent/torrent_handle.hpp deleted file mode 100644 index 50a2f3da4..000000000 --- a/libtorrent_utp/include/libtorrent/torrent_handle.hpp +++ /dev/null @@ -1,684 +0,0 @@ -/* - -Copyright (c) 2003, 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 - -#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 checker_impl; - } - - struct torrent_plugin; - struct peer_info; - struct peer_list_entry; - -#ifndef BOOST_NO_EXCEPTIONS - // for compatibility with 0.14 - typedef libtorrent_exception duplicate_torrent; - typedef libtorrent_exception invalid_handle; - void throw_invalid_handle(); -#endif - - struct TORRENT_EXPORT torrent_status - { - torrent_status() - : state(checking_resume_data) - , paused(false) - , auto_managed(false) - , sequential_download(false) - , is_seeding(false) - , is_finished(false) - , has_metadata(false) - , progress(0.f) - , progress_ppm(0) - , total_download(0) - , total_upload(0) - , total_payload_download(0) - , total_payload_upload(0) - , total_failed_bytes(0) - , total_redundant_bytes(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) - , num_pieces(0) - , total_done(0) - , total_wanted_done(0) - , total_wanted(0) - , distributed_copies(0.f) - , block_size(0) - , num_uploads(0) - , num_connections(0) - , uploads_limit(0) - , connections_limit(0) - , storage_mode(storage_mode_sparse) - , up_bandwidth_queue(0) - , down_bandwidth_queue(0) - , all_time_upload(0) - , all_time_download(0) - , active_time(0) - , finished_time(0) - , seeding_time(0) - , seed_rank(0) - , last_scrape(0) - , has_incoming(false) - , sparse_regions(0) - , seed_mode(false) - , upload_mode(false) - , share_mode(false) - , priority(0) - , added_time(0) - , completed_time(0) - , last_seen_complete(0) - , time_since_upload(0) - , time_since_download(0) - , queue_position(0) - {} - - enum state_t - { - queued_for_checking, - checking_files, - downloading_metadata, - downloading, - finished, - seeding, - allocating, - checking_resume_data - }; - - state_t state; - bool paused; - bool auto_managed; - bool sequential_download; - bool is_seeding; - bool is_finished; - bool has_metadata; - - float progress; - // progress parts per million (progress * 1000000) - // when disabling floating point operations, this is - // the only option to query progress - int progress_ppm; - std::string error; - - boost::posix_time::time_duration next_announce; - boost::posix_time::time_duration announce_interval; - - std::string current_tracker; - - // transferred this session! - // total, payload plus protocol - size_type total_download; - size_type total_upload; - - // payload only - size_type total_payload_download; - size_type total_payload_upload; - - // the amount of payload bytes that - // has failed their hash test - size_type total_failed_bytes; - - // the number of payload bytes that - // has been received redundantly. - size_type total_redundant_bytes; - - // current transfer rate - // payload plus protocol - int download_rate; - int upload_rate; - - // the rate of payload that is - // sent and received - int download_payload_rate; - int upload_payload_rate; - - // the number of peers this torrent is connected to - // that are seeding. - int num_seeds; - - // the number of peers this torrent - // is connected to (including seeds). - 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 - int num_complete; - int num_incomplete; - - // this is the number of seeds whose IP we know - // but are not necessarily connected to - int list_seeds; - - // this is the number of peers whose IP we know - // (including seeds), but are not necessarily - // connected to - int list_peers; - - // the number of peers in our peerlist that - // we potentially could connect to - int connect_candidates; - - bitfield pieces; - - // this is the number of pieces the client has - // downloaded. it is equal to: - // std::accumulate(pieces->begin(), pieces->end()); - int num_pieces; - - // the number of bytes of the file we have - // including pieces that may have been filtered - // after we downloaded them - size_type total_done; - - // the number of bytes we have of those that we - // want. i.e. not counting bytes from pieces that - // are filtered as 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 filtered as not wanted - size_type total_wanted; - - // the number of distributed copies of the file. - // note that one copy may be spread out among many peers. - // - // 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). - - // the number of full distributed copies (i.e. the number - // of peers that have the rarest piece) - int distributed_full_copies; - - // the fraction of pieces that more peers has than the - // rarest pieces. This indicates how close the swarm is - // to have one more full distributed copy - int distributed_fraction; - - float distributed_copies; - - // the block size that is used in this torrent. i.e. - // the number of bytes each piece request asks for - // and each bit in the download queue bitfield represents - int block_size; - - int num_uploads; - int num_connections; - int uploads_limit; - int connections_limit; - - // true if the torrent is saved in compact mode - // false if it is saved in full allocation mode - storage_mode_t storage_mode; - - int up_bandwidth_queue; - int down_bandwidth_queue; - - // number of bytes downloaded since torrent was started - // saved and restored from resume data - size_type all_time_upload; - size_type all_time_download; - - // the number of seconds of being active - // and as being a seed, saved and restored - // from resume data - int active_time; - int finished_time; - int seeding_time; - - // higher value means more important to seed - int seed_rank; - - // number of seconds since last scrape, or -1 if - // there hasn't been a scrape - int last_scrape; - - // true if there are incoming connections to this - // torrent - bool has_incoming; - - // the number of "holes" in the torrent - int sparse_regions; - - // is true if this torrent is (still) in seed_mode - bool seed_mode; - - // this is set to true when the torrent is blocked - // from downloading, typically caused by a file - // write operation failing - bool upload_mode; - - // this is true if the torrent is in share-mode - bool share_mode; - - // the priority of this torrent - int priority; - - // the time this torrent was added and completed - time_t added_time; - time_t completed_time; - time_t last_seen_complete; - - // number of seconds since last upload or download activity - int time_since_upload; - int time_since_download; - - // the position in the download queue where this torrent is - // this is -1 for seeds and finished torrents - int queue_position; - - // true if this torrent has had changes since the last - // time resume data was saved - bool need_save_resume; - }; - - struct TORRENT_EXPORT block_info - { - enum block_state_t - { none, requested, writing, 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: - - 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); - } - - // number of bytes downloaded in this block - unsigned bytes_progress:15; - // the total number of bytes in this block - unsigned block_size:15; - private: - // the type of the addr union - unsigned is_v6_addr:1; - unsigned unused:1; - public: - // the state this block is in (see block_state_t) - unsigned state:2; - // the number of peers that has requested this block - // typically 0 or 1. If > 1, this block is in - // end game mode - unsigned num_peers:14; - }; - - struct TORRENT_EXPORT partial_piece_info - { - int piece_index; - int blocks_in_piece; - // the number of blocks in the finished state - int finished; - // the number of blocks in the writing state - int writing; - // the number of blocks in the requested state - int requested; - block_info* blocks; - enum state_t { none, slow, medium, fast }; - state_t piece_state; - }; - - struct TORRENT_EXPORT torrent_handle - { - friend class invariant_access; - friend struct aux::session_impl; - friend class torrent; - - torrent_handle() {} - - enum flags_t { overwrite_existing = 1 }; - void add_piece(int piece, char const* data, int flags = 0) const; - void read_piece(int piece) const; - - void get_full_peer_list(std::vector& v) const; - void get_peer_info(std::vector& v) const; - - enum status_flags_t - { - query_distributed_copies = 1, - query_accurate_download_counters = 2, - query_last_seen_complete = 4 - }; - - // the flags specify which fields are calculated. By default everything - // is included, you may save CPU by not querying fields you don't need - torrent_status status(boost::uint32_t flags = 0xffffffff) const; - void get_download_queue(std::vector& queue) const; - - enum deadline_flags { alert_when_available = 1 }; - void set_piece_deadline(int index, int deadline, int flags = 0) const; - - 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 - enum file_progress_flags_t - { - piece_granularity = 1 - }; - - void file_progress(std::vector& progress, int flags = 0) const; - - void clear_error() const; - - std::vector trackers() const; - void replace_trackers(std::vector const&) const; - void add_tracker(announce_entry const&) const; - - void add_url_seed(std::string const& url) const; - void remove_url_seed(std::string const& url) const; - std::set url_seeds() const; - - void add_http_seed(std::string const& url) const; - void remove_http_seed(std::string const& url) const; - std::set http_seeds() const; - -#ifndef TORRENT_DISABLE_EXTENSIONS - void add_extension(boost::function(torrent*, void*)> const& ext - , void* userdata = 0); -#endif - - bool set_metadata(char const* metadata, int size) const; - const torrent_info& get_torrent_info() const; - bool is_valid() const; - - enum pause_flags_t { graceful_pause = 1 }; - void pause(int flags = 0) const; - void resume() const; - void set_upload_mode(bool b) const; - void set_share_mode(bool b) const; - void flush_cache() const; - - void force_recheck() const; - - enum save_resume_flags_t { flush_disk_cache = 1 }; - void save_resume_data(int flags = 0) const; - bool need_save_resume_data() const; - - void auto_managed(bool m) const; - - int queue_position() const; - void queue_position_up() const; - void queue_position_down() const; - void queue_position_top() const; - void queue_position_bottom() const; - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - void resolve_countries(bool r); - bool resolve_countries() const; -#endif - - storage_interface* get_storage_impl() const; - - // all these are deprecated, use piece - // priority functions instead - - // ================ start deprecation ============ - -#ifndef TORRENT_NO_DEPRECATE - // 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; - - // deprecated in 0.13 - // 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; - - // ================ end deprecation ============ -#endif - - void piece_availability(std::vector& avail) const; - - // priority must be within the range [0, 7] - 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; - - // priority must be within the range [0, 7] - 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; - - // set the interface to bind outgoing connections - // to. - void use_interface(const char* net_interface) const; - -#ifndef TORRENT_NO_DEPRECATE - // 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; -#endif - - // forces this torrent to reannounce - // (make a rerequest from the tracker) - void force_reannounce() const; -#ifndef TORRENT_DISABLE_DHT - // announces this torrent to the DHT immediately - void force_dht_announce() const; -#endif - - // 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. - void force_reannounce(boost::posix_time::time_duration) const; - - // performs a scrape request - void scrape_tracker() const; - - // returns the name of this torrent, in case it doesn't - // have metadata it returns the name assigned to it - // when it was added. - std::string name() const; - - // TODO: add a feature where the user can tell the torrent - // to finish all pieces currently in the pipeline, and then - // abort the torrent. - - void set_upload_limit(int limit) const; - int upload_limit() const; - void set_download_limit(int limit) const; - int download_limit() const; - - void set_sequential_download(bool sd) const; - - 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) const; - void set_peer_download_limit(tcp::endpoint ip, int limit) const; - - // manually connect a peer - void connect_peer(tcp::endpoint const& adr, int source = 0) const; - - // valid ratios are 0 (infinite ratio) or [ 1.0 , inf ) - // the ratio is uploaded / downloaded. less than 1 is not allowed - void set_ratio(float up_down_ratio) const; - - std::string save_path() const; - - // -1 means unlimited unchokes - void set_max_uploads(int max_uploads) const; - int max_uploads() const; - - // -1 means unlimited connections - void set_max_connections(int max_connections) const; - int max_connections() const; - - void set_tracker_login(std::string const& name - , std::string const& password) const; - - // post condition: save_path() == save_path if true is returned - void move_storage(std::string const& save_path) const; - void rename_file(int index, std::string const& new_name) const; - -#if TORRENT_USE_WSTRING - void move_storage(std::wstring const& save_path) const; - void rename_file(int index, std::wstring const& new_name) const; -#endif - - bool super_seeding() const; - void super_seeding(bool on) const; - - sha1_hash info_hash() const; - - 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(); } - - private: - - torrent_handle(boost::weak_ptr const& t) - : m_torrent(t) - {} - -#ifdef TORRENT_DEBUG - void check_invariant() const; -#endif - - boost::weak_ptr m_torrent; - - }; - - -} - -#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED - diff --git a/libtorrent_utp/include/libtorrent/torrent_info.hpp b/libtorrent_utp/include/libtorrent/torrent_info.hpp deleted file mode 100644 index 1075aad1e..000000000 --- a/libtorrent_utp/include/libtorrent/torrent_info.hpp +++ /dev/null @@ -1,487 +0,0 @@ -/* - -Copyright (c) 2003-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_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" - -namespace libtorrent -{ - class peer_connection; - - enum - { - // wait 60 seconds before retrying a failed tracker - tracker_retry_delay_min = 10 - // when tracker_failed_max trackers - // has failed, wait 60 minutes instead - , tracker_retry_delay_max = 60 * 60 - }; - - struct TORRENT_EXPORT announce_entry - { - announce_entry(std::string const& u) - : url(u) - , next_announce(min_time()) - , min_announce(min_time()) - , tier(0) - , fail_limit(0) - , fails(0) - , updating(false) - , source(0) - , verified(false) - , start_sent(false) - , complete_sent(false) - , send_stats(true) - {} - - // tracker URL as it appeared in the torrent file - std::string url; - 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; - - 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; - - // the tier this tracker belongs to - boost::uint8_t tier; - - // the number of times this tracker can fail - // in a row before it's removed. 0 means unlimited - boost::uint8_t fail_limit; - - // the number of times in a row this tracker has failed - boost::uint8_t fails:7; - - // true if we're currently trying to announce with - // this tracker - bool updating:1; - - enum tracker_source - { - source_torrent = 1, - source_client = 2, - source_magnet_link = 4, - source_tex = 8 - }; - - // where did we get this tracker from - boost::uint8_t source:4; - - // is set to true if we have ever received a response from - // this tracker - bool verified:1; - - // this is true if event start has been sent to the tracker - bool start_sent:1; - - // this is true if event completed has been sent to the tracker - bool complete_sent:1; - - // this is false the stats sent to this tracker will be 0 - bool send_stats:1; - - void reset() - { - start_sent = false; - next_announce = min_time(); - min_announce = min_time(); - } - - void failed(int retry_interval = 0); - - bool will_announce(ptime now) const - { - return now <= next_announce - && (fails < fail_limit || fail_limit == 0) - && !updating; - } - - bool can_announce(ptime now, bool is_seed) const; - - bool is_working() const - { return fails == 0; } - - void trim(); - }; - - 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(url_), type(type_) - , auth(auth_), extra_headers(extra_headers_) - , retry(time_now()), resolving(false), connection(0) - {} - - bool operator==(web_seed_entry const& e) const - { return url == e.url && type == e.type; } - - bool operator<(web_seed_entry const& e) const - { - if (url < e.url) return true; - if (url > e.url) return false; - return type < e.type; - } - - std::string url; - type_t type; - std::string auth; - headers_t extra_headers; - - // if this is > now, we can't reconnect yet - ptime retry; - - // this indicates whether or not we're resolving the - // hostname of this URL - bool resolving; - - tcp::endpoint endpoint; - - peer_connection* connection; - }; - -#ifndef BOOST_NO_EXCEPTIONS - // for backwards compatibility with 0.14 - typedef libtorrent_exception invalid_torrent_file; -#endif - - int TORRENT_EXPORT load_file(std::string const& filename, std::vector& v); - - class TORRENT_EXPORT torrent_info : public intrusive_ptr_base - { - public: - -#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); -#if TORRENT_USE_WSTRING - torrent_info(std::wstring const& filename, int flags = 0); -#endif // TORRENT_USE_WSTRING -#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); -#if TORRENT_USE_WSTRING - torrent_info(std::wstring const& filename, error_code& ec, int flags = 0); -#endif // TORRENT_USE_WSTRING - - ~torrent_info(); - - file_storage const& files() const { return m_files; } - file_storage const& orig_files() const { return m_orig_files ? *m_orig_files : m_files; } - - void rename_file(int index, std::string const& new_filename) - { - copy_on_write(); - m_files.rename_file(index, new_filename); - } - -#if TORRENT_USE_WSTRING - void rename_file(int index, std::wstring const& new_filename) - { - copy_on_write(); - m_files.rename_file(index, new_filename); - } -#endif // TORRENT_USE_WSTRING - - void remap_files(file_storage const& f); - - 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 - - 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; } - - 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(); } - const sha1_hash& info_hash() const { return m_info_hash; } - const std::string& name() const { return m_files.name(); } - - typedef file_storage::iterator file_iterator; - typedef file_storage::reverse_iterator reverse_file_iterator; - - file_iterator begin_files() const { return m_files.begin(); } - file_iterator end_files() const { return m_files.end(); } - reverse_file_iterator rbegin_files() const { return m_files.rbegin(); } - reverse_file_iterator rend_files() const { return m_files.rend(); } - int num_files() const { return m_files.num_files(); } - file_entry const& file_at(int index) const { return m_files.at(index); } - - file_iterator file_at_offset(size_type offset) const - { return m_files.file_at_offset(offset); } - std::vector map_block(int piece, size_type offset, int size) const - { return m_files.map_block(piece, offset, size); } - 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 - - bool is_valid() const { return m_files.is_valid(); } - - bool priv() const { return m_private; } - - bool is_i2p() const { return m_i2p; } - - 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)); } - - 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); } - - 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]; - } - } - - 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; - - nodes_t const& nodes() const - { return m_nodes; } - void add_node(std::pair const& node) - { m_nodes.push_back(node); } - - bool parse_info_section(lazy_entry const& e, error_code& ec, int flags); - - 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); - } - return m_info_dict.dict_find(key); - } - - void swap(torrent_info& ti); - - boost::shared_array metadata() const - { return m_info_section; } - - int metadata_size() const { return m_info_section_size; } - - bool add_merkle_nodes(std::map const& subtree - , int piece); - std::map build_merkle_list(int piece) const; - bool is_merkle_torrent() const { return !m_merkle_tree.empty(); } - - // if we're logging member offsets, we need access to them -#if !defined NDEBUG \ - && !defined TORRENT_LOGGING \ - && !defined TORRENT_VERBOSE_LOGGING \ - && !defined TORRENT_ERROR_LOGGING - private: -#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); - - 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 index to the first leaf. This is where the hash for the - // first piece is stored - boost::uint32_t m_merkle_first_leaf:24; - - // 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/libtorrent_utp/include/libtorrent/tracker_manager.hpp b/libtorrent_utp/include/libtorrent/tracker_manager.hpp deleted file mode 100644 index 6fec8e179..000000000 --- a/libtorrent_utp/include/libtorrent/tracker_manager.hpp +++ /dev/null @@ -1,281 +0,0 @@ -/* - -Copyright (c) 2003, 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" - -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_EXPORT int gzip_header(const char* buf, int size); - - struct TORRENT_EXPORT tracker_request - { - tracker_request() - : kind(announce_request) - , downloaded(-1) - , uploaded(-1) - , left(-1) - , corrupt(0) - , redundant(0) - , event(none) - , key(0) - , num_want(0) - , send_stats(true) - {} - - 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; - int key; - int num_want; - std::string ipv6; - std::string ipv4; - address bind_ip; - bool send_stats; - }; - - struct TORRENT_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 - , 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 std::string& line) = 0; -#endif - private: - tracker_manager* m_manager; - }; - - struct TORRENT_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() {} - - 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_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(); - virtual ~tracker_connection() {} - - tracker_request const& tracker_req() const { return m_req; } - - void fail_disp(error_code ec) { fail(ec); } - 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; } - - protected: - boost::weak_ptr m_requester; - private: - tracker_manager& m_man; - const tracker_request m_req; - }; - - class TORRENT_EXPORT tracker_manager: 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); - - bool incoming_udp(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 - bool incoming_udp(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/libtorrent_utp/include/libtorrent/udp_socket.hpp b/libtorrent_utp/include/libtorrent/udp_socket.hpp deleted file mode 100644 index 6dbc8cc36..000000000 --- a/libtorrent_utp/include/libtorrent/udp_socket.hpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - -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_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 -#include - -namespace libtorrent -{ - class connection_queue; - - class udp_socket - { - public: - typedef boost::function callback_t; - typedef boost::function callback2_t; - - udp_socket(io_service& ios, callback_t const& c, callback2_t const& c2, connection_queue& cc); - ~udp_socket(); - - 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(); } - - // 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); - - void send(udp::endpoint const& ep, char const* p, int len, error_code& ec); - void bind(udp::endpoint const& ep, error_code& ec); - void bind(int port); - 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; } - - 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 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; - }; - - private: -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - // necessary for logging member offsets - public: -#endif - - // callback for regular incoming packets - callback_t m_callback; - - // callback for proxied incoming packets with a domain - // name as source - callback2_t m_callback2; - - void on_read(udp::socket* sock, 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); - 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 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); - - void maybe_realloc_buffers(); - -#ifdef TORRENT_DEBUG -#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; - udp::endpoint m_v4_ep; - int m_v4_buf_size; - char* m_v4_buf; - -#if TORRENT_USE_IPV6 - udp::socket m_ipv6_sock; - udp::endpoint m_v6_ep; - int m_v6_buf_size; - char* m_v6_buf; -#endif - - int m_bind_port; - char m_outstanding; - - 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_abort; - // this is set to true to indicate that the m_v4_buf - // and m_v6_buf should be reallocated to the size - // of the buffer size members the next time their - // read handler gets triggered - bool m_reallocate_buffers; - udp::endpoint m_proxy_addr; - // while we're connecting to the proxy - // we have to queue the packets, we'll flush - // them once we're connected - std::list m_queue; -#ifdef TORRENT_DEBUG - bool m_started; - int m_magic; - int m_outstanding_when_aborted; -#endif - }; - - struct rate_limited_udp_socket : public udp_socket - { - rate_limited_udp_socket(io_service& ios, callback_t const& c, callback2_t const& c2, connection_queue& cc); - void set_rate_limit(int limit) { m_rate_limit = limit; } - bool can_send() const { return int(m_queue.size()) >= m_queue_size_limit; } - bool send(udp::endpoint const& ep, char const* p, int len, error_code& ec, int flags = 0); - void close(); - - private: - void on_tick(error_code const& e); - - deadline_timer m_timer; - int m_queue_size_limit; - int m_rate_limit; - int m_quota; - ptime m_last_tick; - std::list m_queue; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/udp_tracker_connection.hpp b/libtorrent_utp/include/libtorrent/udp_tracker_connection.hpp deleted file mode 100644 index 272b9cbd5..000000000 --- a/libtorrent_utp/include/libtorrent/udp_tracker_connection.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - -Copyright (c) 2003, 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_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(); - -#if !defined TORRENT_VERBOSE_LOGGING && !defined TORRENT_LOGGING && !defined TORRENT_ERROR_LOGGING - // necessary for logging member offsets - private: -#endif - - 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); - - void send_udp_connect(); - void send_udp_announce(); - void send_udp_scrape(); - - virtual void on_timeout(error_code const& ec); - - tracker_manager& m_man; - - 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/libtorrent_utp/include/libtorrent/union_endpoint.hpp b/libtorrent_utp/include/libtorrent/union_endpoint.hpp deleted file mode 100644 index 1094f3e13..000000000 --- a/libtorrent_utp/include/libtorrent/union_endpoint.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - -Copyright (c) 2010, 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() - { - *this = tcp::endpoint(); - } - - 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; - } - - 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/libtorrent_utp/include/libtorrent/upnp.hpp b/libtorrent_utp/include/libtorrent/upnp.hpp deleted file mode 100644 index 0d818a5e4..000000000 --- a/libtorrent_utp/include/libtorrent/upnp.hpp +++ /dev/null @@ -1,322 +0,0 @@ -/* - -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_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 - { - enum error_code_enum - { - no_error = 0, - invalid_argument = 402, - action_failed = 501, - value_not_in_array = 714, - source_ip_cannot_be_wildcarded = 715, - external_port_cannot_be_wildcarded = 716, - port_mapping_conflict = 718, - internal_port_must_match_external = 724, - only_permanent_leases_supported = 725, - remote_host_must_be_wildcard = 726, - external_port_must_be_wildcard = 727 - }; - } - -#if BOOST_VERSION < 103500 - extern asio::error::error_category upnp_category; -#else - - struct TORRENT_EXPORT upnp_error_category : boost::system::error_category - { - virtual const char* name() const; - virtual std::string message(int ev) const; - virtual boost::system::error_condition default_error_condition(int ev) const - { return boost::system::error_condition(ev, *this); } - }; - - extern TORRENT_EXPORT upnp_error_category upnp_category; -#endif - -// int: port-mapping index -// 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; - -class TORRENT_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 }; - 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 discover_device(); - void close(); - - std::string router_model() - { - mutex::scoped_lock l(m_mutex); - return m_model; - } - -private: - - 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_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 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) - , lease_duration(default_lease_time) - , supports_specific_external(true) - , disabled(false) - { -#ifdef TORRENT_DEBUG - magic = 1337; -#endif - } - -#ifdef TORRENT_DEBUG - ~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; - - int lease_duration; - // true if the device supports specifying a - // specific external port, false if it doesn't - bool supports_specific_external; - - bool disabled; - - mutable boost::shared_ptr upnp_connection; - -#ifdef TORRENT_DEBUG - 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; - - bool m_disabled; - bool m_closing; - bool m_ignore_non_routers; - - connection_queue& m_cc; - - mutex m_mutex; - - std::string m_model; -}; - -} - - -#endif - diff --git a/libtorrent_utp/include/libtorrent/utf8.hpp b/libtorrent_utp/include/libtorrent/utf8.hpp deleted file mode 100644 index 7587ab36e..000000000 --- a/libtorrent_utp/include/libtorrent/utf8.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - -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_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 - -#include "libtorrent/ConvertUTF.h" - -namespace libtorrent -{ - inline int 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 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 ret; - } - else - { - return sourceIllegal; - } - } - - inline int wchar_utf8(const std::wstring &wide, std::string &utf8) - { - // allocate space for worst-case - utf8.resize(wide.size() * 6); - if (wide.empty()) return 0; - 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 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 ret; - } - else - { - return sourceIllegal; - } - } -} -#endif // !BOOST_NO_STD_WSTRING - -#endif - diff --git a/libtorrent_utp/include/libtorrent/utp_socket_manager.hpp b/libtorrent_utp/include/libtorrent/utp_socket_manager.hpp deleted file mode 100644 index ad8adbcea..000000000 --- a/libtorrent_utp/include/libtorrent/utp_socket_manager.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - -Copyright (c) 2009, 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 - { - 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 - bool incoming_packet(char const* p, int size, udp::endpoint const& ep); - - void tick(ptime now); - - tcp::endpoint local_endpoint(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); - - // 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 delayed_ack() const { return m_sett.utp_delayed_ack; } - int min_timeout() const { return m_sett.utp_min_timeout; } - 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); - - 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; - - // 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 - std::vector m_routes; - - // the timestamp for the last time we updated - // the routing table - ptime m_last_route_update; - - // the buffer size of the socket. This is used - // to now lower the buffer size - int m_sock_buf_size; - }; -} - -#endif - diff --git a/libtorrent_utp/include/libtorrent/utp_stream.hpp b/libtorrent_utp/include/libtorrent/utp_stream.hpp deleted file mode 100644 index e2d3157a0..000000000 --- a/libtorrent_utp/include/libtorrent/utp_stream.hpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - -Copyright (c) 2009, 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 - -#define CCONTROL_TARGET 100 - -namespace libtorrent -{ - struct utp_socket_manager; - - // 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 - }; - - // 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 | - +---------------+---------------+---------------+---------------+ - -*/ - - enum type { ST_DATA = 0, 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); - -#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 utp_stream -{ -public: - - typedef stream_socket::endpoint_type endpoint_type; - typedef stream_socket::protocol_type protocol_type; - - explicit utp_stream(asio::io_service& io_service); - ~utp_stream(); - - // 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; } - - 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); - - 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& io_service() - { return m_io_service; } - - template - void async_connect(endpoint_type const& endpoint, Handler const& handler) - { - if (!endpoint.address().is_v4()) - { - error_code ec = asio::error::operation_not_supported; - 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(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; - } - for (typename Mutable_Buffers::const_iterator i = buffers.begin() - , end(buffers.end()); i != end; ++i) - { - TORRENT_ASSERT(buffer_size(*i) > 0); - using asio::buffer_cast; - using asio::buffer_size; - add_read_buffer(buffer_cast(*i), buffer_size(*i)); - } - 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; - } -#ifdef TORRENT_DEBUG - int 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)); -#ifdef TORRENT_DEBUG - buf_size += buffer_size(*i); -#endif - } - std::size_t ret = read_some(true); - TORRENT_ASSERT(int(ret) <= buf_size); - TORRENT_ASSERT(ret > 0); - return ret; - } - - template - std::size_t write_some(Const_Buffers const& buffers, error_code& ec) - { - // TODO: implement - return 0; - } - - 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; - } - - for (typename Const_Buffers::const_iterator i = buffers.begin() - , end(buffers.end()); i != end; ++i) - { - TORRENT_ASSERT(buffer_size(*i) > 0); - using asio::buffer_cast; - using asio::buffer_size; - add_write_buffer((void*)buffer_cast(*i), buffer_size(*i)); - } - 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; - bool m_open; -}; - -} - -#endif diff --git a/libtorrent_utp/include/libtorrent/version.hpp b/libtorrent_utp/include/libtorrent/version.hpp deleted file mode 100644 index 6e88df676..000000000 --- a/libtorrent_utp/include/libtorrent/version.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - -Copyright (c) 2003, 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 0 -#define LIBTORRENT_VERSION_MINOR 16 -#define LIBTORRENT_VERSION_TINY 0 - -// 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 "0.16.0.0" -#define LIBTORRENT_REVISION "$Rev$" - -#endif diff --git a/libtorrent_utp/include/libtorrent/web_connection_base.hpp b/libtorrent_utp/include/libtorrent/web_connection_base.hpp deleted file mode 100644 index 2aa01c28c..000000000 --- a/libtorrent_utp/include/libtorrent/web_connection_base.hpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - -Copyright (c) 2003, 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_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 - , std::string const& url - , policy::peer* peerinfo - , std::string const& ext_auth - , web_seed_entry::headers_t const& ext_headers); - 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) - { incoming_reject_request(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) {} - -#ifdef TORRENT_DEBUG - 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/libtorrent_utp/include/libtorrent/web_peer_connection.hpp b/libtorrent_utp/include/libtorrent/web_peer_connection.hpp deleted file mode 100644 index e32ea24f4..000000000 --- a/libtorrent_utp/include/libtorrent/web_peer_connection.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - -Copyright (c) 2003, 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_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 - , std::string const& url - , policy::peer* peerinfo - , std::string const& ext_auth - , web_seed_entry::headers_t const& ext_headers); - - 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_original_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 has one entry per http-request - // (might be more than the bt requests) - std::deque m_file_requests; - - std::string m_url; - std::string m_original_url; - - // this is used for intermediate storage of pieces - // that are received in more than one HTTP response - 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 - int m_received_body; - - // position in the current range response - int 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 - int 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/libtorrent_utp/include/libtorrent/xml_parse.hpp b/libtorrent_utp/include/libtorrent/xml_parse.hpp deleted file mode 100644 index 087a12e02..000000000 --- a/libtorrent_utp/include/libtorrent/xml_parse.hpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - -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_XML_PARSE_HPP -#define TORRENT_XML_PARSE_HPP - -#include -#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 - }; - - // 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 - - template - void xml_parse(char* p, char* end, CallbackType callback) - { - for(;p != end; ++p) - { - char const* start = p; - char const* val_start = 0; - int token; - // look for tag start - for(; *p != '<' && p != end; ++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; - - // parse the name of the tag. - for (start = p; p != end && *p != '>' && !isspace(*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; - } - 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 && isspace(*i); ++i); - if (i == tag_end) break; - start = i; - // find end of attribute name - for (; i != tag_end && *i != '=' && !isspace(*i); ++i); - char* name_end = i; - - // look for equality sign - for (; i != tag_end && *i != '='; ++i); - - if (i == tag_end) - { - token = xml_parse_error; - val_start = 0; - start = "garbage inside element brackets"; - callback(token, start, val_start); - break; - } - - ++i; - for (; i != tag_end && isspace(*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; - } - } - - } - -} - - -#endif - diff --git a/libtorrent_utp/libtorrent-rasterbar-cmake.pc.in b/libtorrent_utp/libtorrent-rasterbar-cmake.pc.in deleted file mode 100644 index ee5f51943..000000000 --- a/libtorrent_utp/libtorrent-rasterbar-cmake.pc.in +++ /dev/null @@ -1,6 +0,0 @@ -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/libtorrent_utp/libtorrent-rasterbar.pc.in b/libtorrent_utp/libtorrent-rasterbar.pc.in deleted file mode 100644 index 35dc57b8a..000000000 --- a/libtorrent_utp/libtorrent-rasterbar.pc.in +++ /dev/null @@ -1,16 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -bindir=@bindir@ -libdir=@libdir@ -datarootdir=@datarootdir@ -datadir=@datadir@ -sysconfdir=@sysconfdir@ -includedir=@includedir@ -package=@PACKAGE@ - -Name: libtorrent-rasterbar -Description: Bittorrent library. -Version: @VERSION@ -Libs: -L${libdir} -ltorrent-rasterbar -Libs.private: @LIBS@ @BOOST_SYSTEM_LIB@ @PTHREAD_LIBS@ @OPENSSL_LIBS@ -Cflags: -I${includedir} -I${includedir}/libtorrent @COMPILETIME_OPTIONS@ diff --git a/libtorrent_utp/list_files.py b/libtorrent_utp/list_files.py deleted file mode 100755 index 1118b8cb3..000000000 --- a/libtorrent_utp/list_files.py +++ /dev/null @@ -1,22 +0,0 @@ -#! /usr/bin/env python -# Copyright Arvid Norberg 2008. 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) - -import os -import sys - -def list_directory(path): - tree = os.walk(path) - for i in tree: - dirs = i[0].split('/') - if 'CVS' in dirs: continue - if '.svn' in dirs: continue - - for file in i[2]: - if file.startswith('.#'): continue - if file == '.DS_Store': continue - print os.path.join(i[0], file) + ' \\' - -list_directory(sys.argv[1]) - diff --git a/libtorrent_utp/m4/ax_boost_base.m4 b/libtorrent_utp/m4/ax_boost_base.m4 deleted file mode 100644 index 7a9da6d07..000000000 --- a/libtorrent_utp/m4/ax_boost_base.m4 +++ /dev/null @@ -1,224 +0,0 @@ -# =========================================================================== -# http://www.nongnu.org/autoconf-archive/ax_boost_base.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_BASE([MINIMUM-VERSION]) -# -# DESCRIPTION -# -# Test for the Boost C++ libraries of a particular version (or newer) -# -# If no path to the installed boost library is given the macro searchs -# under /usr, /usr/local, /opt and /opt/local and evaluates the -# $BOOST_ROOT environment variable. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) -# -# And sets: -# -# HAVE_BOOST -# -# EDIT: 2009-09-07 Cristian Greco -# - Call AC_SUBST(BOOST_VERSION). -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. - -AC_DEFUN([AX_BOOST_BASE], -[ -AC_ARG_WITH([boost], - AS_HELP_STRING([--with-boost@<:@=DIR@:>@], [use boost (default is yes) - it is possible to specify the root directory for boost (optional)]), - [ - if test "x$withval" = "xno"; then - want_boost="no" - elif test "x$withval" = "xyes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - ], - [want_boost="yes"]) - - -AC_ARG_WITH([boost-libdir], - AS_HELP_STRING([--with-boost-libdir=LIB_DIR], - [Force given directory for boost libraries. Note that this will overwrite library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), - [ - if test -d $withval - then - ac_boost_lib_path="$withval" - else - AC_MSG_ERROR(--with-boost-libdir expected directory name) - fi - ], - [ac_boost_lib_path=""] -) - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=ifelse([$1], ,1.20.0,$1) - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) - succeeded=no - - dnl first we check the system location for boost libraries - dnl this location ist chosen if boost libraries are installed with the --layout=system option - dnl or if you install boost with RPM - if test "x$ac_boost_path" != "x"; then - BOOST_LDFLAGS="-L$ac_boost_path/lib" - BOOST_CPPFLAGS="-I$ac_boost_path/include" - else - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - BOOST_LDFLAGS="-L$ac_boost_path_tmp/lib" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - BOOST_VERSION=`grep "define BOOST_LIB_VERSION" $ac_boost_path_tmp/include/boost/version.hpp | sed 's,^.*\"\([[0-9]][[_0-9]]*\)\"$,\1, ; s,_0$,, ; s,_,.,'` - break; - fi - done - fi - - dnl overwrite ld flags if we have required special directory with - dnl --with-boost-libdir parameter - if test "x$ac_boost_lib_path" != "x"; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - - - - dnl if we found no boost with system layout we search for boost libraries - dnl built and installed without the --layout=system option or for a staged(not installed) version - if test "x$succeeded" != "xyes"; then - _version=0 - if test "x$ac_boost_path" != "x"; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "x$ac_boost_lib_path" = "x" - then - BOOST_LDFLAGS="-L$best_path/lib" - fi - - if test "x$BOOST_ROOT" != "x"; then - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/lib" && test -r "$BOOST_ROOT/stage/lib"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "x$ac_boost_lib_path" = "x" ; then - AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/lib" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - fi - - if test "x$succeeded" != "xyes" ; then - if test "$_version" = "0" ; then - AC_MSG_ERROR([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) - else - AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) - fi - else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) - AC_SUBST(BOOST_VERSION) - AC_DEFINE(HAVE_BOOST,[1],[define if the Boost library is available]) - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - -]) diff --git a/libtorrent_utp/m4/ax_boost_python.m4 b/libtorrent_utp/m4/ax_boost_python.m4 deleted file mode 100644 index 90dfa0ec0..000000000 --- a/libtorrent_utp/m4/ax_boost_python.m4 +++ /dev/null @@ -1,103 +0,0 @@ -# =========================================================================== -# http://www.nongnu.org/autoconf-archive/ax_boost_python.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_PYTHON -# -# DESCRIPTION -# -# This macro checks to see if the Boost.Python library is installed. It -# also attempts to guess the currect library name using several attempts. -# It tries to build the library name using a user supplied name or suffix -# and then just the raw library. -# -# If the library is found, HAVE_BOOST_PYTHON is defined and -# BOOST_PYTHON_LIB is set to the name of the library. -# -# This macro calls AC_SUBST(BOOST_PYTHON_LIB). -# -# In order to ensure that the Python headers are specified on the include -# path, this macro requires AX_PYTHON to be called. -# -# EDIT: -# 2009-09-14 Cristian Greco -# - Require AX_PYTHON_DEVEL to be called before this macro to properly -# detect python include path, instead of AX_PYTHON. -# 2009-09-07 Cristian Greco -# - Prefix BOOST_PYTHON_LIB with a `-l` for consistency with other -# ax_boost_libname.m4 scripts. -# -# LICENSE -# -# Copyright (c) 2008 Michael Tindal -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2 of the License, or (at your -# option) any later version. -# -# This program 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 General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AC_DEFUN([AX_BOOST_PYTHON], -[AC_REQUIRE([AX_PYTHON_DEVEL])dnl -AC_CACHE_CHECK(whether the Boost::Python library is available, -ac_cv_boost_python, -[AC_LANG_SAVE - AC_LANG_CPLUSPLUS - CPPFLAGS_SAVE=$CPPFLAGS - if test "x$PYTHON_CPPFLAGS" != "x"; then - CPPFLAGS="$PYTHON_CPPFLAGS $CPPFLAGS" - fi - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[ - #include - using namespace boost::python; - BOOST_PYTHON_MODULE(test) { throw "Boost::Python test."; }]], - [[return 0;]]), - ac_cv_boost_python=yes, ac_cv_boost_python=no) - AC_LANG_RESTORE - CPPFLAGS=$CPPFLAGS_SAVE -]) -if test "x$ac_cv_boost_python" = "xyes"; then - AC_DEFINE(HAVE_BOOST_PYTHON,[1],[define if the Boost::Python library is available]) - dnl - LDFLAGS_SAVE=$LDFLAGS - if test "x$PYTHON_LDFLAGS" != "x"; then - LDFLAGS="$LDFLAGS $PYTHON_LDFLAGS" - fi - dnl - ax_python_lib=boost_python - AC_ARG_WITH([boost-python],AS_HELP_STRING([--with-boost-python],[specify the boost python library or suffix to use]), - [if test "x$with_boost_python" != "xno"; then - ax_python_lib="$with_boost_python" - ax_boost_python_lib="boost_python-$with_boost_python" - fi]) - for ax_lib in $ax_python_lib $ax_boost_python_lib boost_python; do - AC_CHECK_LIB($ax_lib, main, [BOOST_PYTHON_LIB=-l$ax_lib break]) - done - dnl - LDFLAGS=$LDFLAGS_SAVE - dnl - AC_SUBST(BOOST_PYTHON_LIB) -fi -])dnl diff --git a/libtorrent_utp/m4/ax_boost_system.m4 b/libtorrent_utp/m4/ax_boost_system.m4 deleted file mode 100644 index 48475fc4f..000000000 --- a/libtorrent_utp/m4/ax_boost_system.m4 +++ /dev/null @@ -1,114 +0,0 @@ -# =========================================================================== -# http://www.nongnu.org/autoconf-archive/ax_boost_system.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_SYSTEM -# -# DESCRIPTION -# -# Test for System library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_SYSTEM_LIB) -# -# And sets: -# -# HAVE_BOOST_SYSTEM -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2008 Michael Tindal -# Copyright (c) 2008 Daniel Casimiro -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. - -AC_DEFUN([AX_BOOST_SYSTEM], -[ - AC_ARG_WITH([boost-system], - AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], - [use the System library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-system=boost_system-gcc-mt ]), - [ - if test "x$withval" = "xno"; then - want_boost="no" - elif test "x$withval" = "xyes"; then - want_boost="yes" - ax_boost_user_system_lib="" - else - want_boost="yes" - ax_boost_user_system_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::System library is available, - ax_cv_boost_system, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[@%:@include ]], - [[boost::system::system_category]]), - ax_cv_boost_system=yes, ax_cv_boost_system=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_system" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_SYSTEM,[1],[define if the Boost::System library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_system*.{so,dylib,a}* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.a*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib$;\1;'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - if test "x$link_system" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_system*.{dll,a}* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_system.*\)\.dll.*$;\1;' -e 's;^\(boost_system.*\)\.a*$;\1;'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - - fi - if test "x$link_system" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/libtorrent_utp/m4/ax_check_geoip.m4 b/libtorrent_utp/m4/ax_check_geoip.m4 deleted file mode 100644 index d97697a84..000000000 --- a/libtorrent_utp/m4/ax_check_geoip.m4 +++ /dev/null @@ -1,95 +0,0 @@ -# =========================================================================== -# -# SYNOPSIS -# -# AX_CHECK_GEOIP([action-if-found], [action-if-not-found]) -# -# DESCRIPTION -# -# Tests for the GeoIP (libgeoip) library. -# -# This macro calls: -# -# AC_SUBST(GEOIP_CFLAGS) / AC_SUBST(GEOIP_LIBS) -# -# LAST MODIFICATION -# -# 2009-09-05 -# -# LICENSE -# -# Copyright (c) 2009 Cristian Greco -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. - -AC_DEFUN([AC_CHECK_GEOIP], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl - - ac_geoip_found="no" - - AC_MSG_CHECKING([for libgeoip with pkg-config]) - PKG_CHECK_EXISTS([geoip], [ - AC_MSG_RESULT([yes]) - PKG_CHECK_MODULES([GEOIP], [geoip], [ - ac_geoip_found="yes" - ], [ - AC_MSG_WARN([pkg-config: geoip module not found]) - ]) - ], [ - AC_MSG_RESULT([no]) - - CPPFLAGS_SAVED="$CPPFLAGS" - LDFLAGS_SAVED="$LDFLAGS" - CFLAGS_SAVED="$CFLAGS" - LIBS_SAVED="$LIBS" - - AC_CHECK_HEADER([GeoIP.h], [ - AC_CHECK_LIB([GeoIP], [GeoIP_new], [ - GEOIP_CFLAGS="" - GEOIP_LIBS="-lGeoIP" - ac_geoip_found="yes" - ], [ - AC_MSG_WARN([libgeoip library not found]) - ]) - ], [ - for ac_geoip_path in /usr /usr/local /opt /opt/local; do - AC_MSG_CHECKING([for GeoIP.h in $ac_geoip_path]) - if test -d "$ac_geoip_path/include/" -a -r "$ac_geoip_path/include/GeoIP.h"; then - AC_MSG_RESULT([yes]) - GEOIP_CFLAGS="-I$ac_geoip_path/include" - GEOIP_LIBS="-lGeoIP" - break; - else - AC_MSG_RESULT([no]) - fi - done - - CFLAGS="$GEOIP_CFLAGS $CFLAGS" - export CFLAGS - LIBS="$GEOIP_LIBS $LIBS" - export LIBS - - AC_MSG_CHECKING([for GeoIP_new in -lGeoIP]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ #include ]], [[ GeoIP *g = GeoIP_new(GEOIP_STANDARD); ]]) - ], [ - AC_MSG_RESULT([yes]) - ac_geoip_found="yes" - ], [ - AC_MSG_RESULT([no]) - ]) - ]) - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - CFLAGS="$CFLAGS_SAVED" - LIBS="$LIBS_SAVED" - ]) - - AS_IF([ test "x$ac_geoip_found" != xno ], [$1], [$2]) - -AC_SUBST([GEOIP_CFLAGS]) -AC_SUBST([GEOIP_LIBS]) -]) diff --git a/libtorrent_utp/m4/ax_check_openssl.m4 b/libtorrent_utp/m4/ax_check_openssl.m4 deleted file mode 100644 index aecf20849..000000000 --- a/libtorrent_utp/m4/ax_check_openssl.m4 +++ /dev/null @@ -1,124 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) -# -# DESCRIPTION -# -# Look for OpenSSL in a number of default spots, or in a user-selected -# spot (via --with-openssl). Sets -# -# OPENSSL_INCLUDES to the include directives required -# OPENSSL_LIBS to the -l directives required -# OPENSSL_LDFLAGS to the -L or -R flags required -# -# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately -# -# This macro sets OPENSSL_INCLUDES such that source files should use the -# openssl/ directory in include directives: -# -# #include -# -# LICENSE -# -# Copyright (c) 2009, 2010 Zmanda Inc. -# Copyright (c) 2009, 2010 Dustin J. Mitchell -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 6 - -AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) -AC_DEFUN([AX_CHECK_OPENSSL], [ - found=false - AC_ARG_WITH(openssl, - AS_HELP_STRING([--with-openssl=DIR], - [root of the OpenSSL directory]), - [ - case "$withval" in - "" | y | ye | yes | n | no) - AC_MSG_ERROR([Invalid --with-openssl value]) - ;; - *) ssldirs="$withval" - ;; - esac - ], [ - # if pkg-config is installed and openssl has installed a .pc file, - # then use that information and don't search ssldirs - AC_PATH_PROG(PKG_CONFIG, pkg-config) - if test x"$PKG_CONFIG" != x""; then - OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` - if test $? = 0; then - OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` - OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` - found=true - fi - fi - - # no such luck; use some default ssldirs - if ! $found; then - ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" - fi - ] - ) - - - # note that we #include , so the OpenSSL headers have to be in - # an 'openssl' subdirectory - - if ! $found; then - OPENSSL_INCLUDES= - for ssldir in $ssldirs; do - AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) - if test -f "$ssldir/include/openssl/ssl.h"; then - OPENSSL_INCLUDES="-I$ssldir/include" - OPENSSL_LDFLAGS="-L$ssldir/lib" - OPENSSL_LIBS="-lssl -lcrypto" - found=true - AC_MSG_RESULT([yes]) - break - else - AC_MSG_RESULT([no]) - fi - done - - # if the file wasn't found, well, go ahead and try the link anyway -- maybe - # it will just work! - fi - - # try the preprocessor and linker with our new flags, - # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS - - AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) - echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ - "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD - - save_LIBS="$LIBS" - save_LDFLAGS="$LDFLAGS" - save_CPPFLAGS="$CPPFLAGS" - LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" - LIBS="$OPENSSL_LIBS $LIBS" - CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" - AC_LINK_IFELSE( - AC_LANG_PROGRAM([#include ], [SSL_new(NULL)]), - [ - AC_MSG_RESULT([yes]) - $1 - ], [ - AC_MSG_RESULT([no]) - $2 - ]) - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - - AC_SUBST([OPENSSL_INCLUDES]) - AC_SUBST([OPENSSL_LIBS]) - AC_SUBST([OPENSSL_LDFLAGS]) -]) diff --git a/libtorrent_utp/m4/ax_pthread.m4 b/libtorrent_utp/m4/ax_pthread.m4 deleted file mode 100644 index 8a7a64be1..000000000 --- a/libtorrent_utp/m4/ax_pthread.m4 +++ /dev/null @@ -1,383 +0,0 @@ -# =========================================================================== -# http://www.nongnu.org/autoconf-archive/ax_pthread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro figures out how to build C programs using POSIX threads. It -# sets the PTHREAD_LIBS output variable to the threads library and linker -# flags, and the PTHREAD_CFLAGS output variable to any special C compiler -# flags that are needed. (The user can also force certain compiler -# flags/libs to be tested by setting these environment variables.) -# -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) -# -# NOTE: You are assumed to not only compile your program with these flags, -# but also link it with them as well. e.g. you should link with -# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS -# -# If you are only building threads programs, you may wish to use these -# variables in your default LIBS, CFLAGS, and CC: -# -# LIBS="$PTHREAD_LIBS $LIBS" -# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -# CC="$PTHREAD_CC" -# -# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). -# -# ACTION-IF-FOUND is a list of shell commands to run if a threads library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_PTHREAD. -# -# Please let the authors know if this macro fails on any platform, or if -# you have any other suggestions or comments. This macro was based on work -# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help -# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by -# Alejandro Forero Cuervo to the autoconf macro repository. We are also -# grateful for the helpful feedback of numerous users. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program 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 General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) -AC_DEFUN([AX_PTHREAD], [ -AC_REQUIRE([AC_CANONICAL_HOST]) -AC_LANG_SAVE -AC_LANG_C -ax_pthread_ok=no - -# We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on True64 or Sequent). -# It gets checked for in the link test anyway. - -# First of all, check if the user has set any of the PTHREAD_LIBS, -# etcetera environment variables, and if threads linking works using -# them: -if test "x$PTHREAD_LIBS$PTHREAD_CFLAGS" != "x"; then - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = "xno"; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" -fi - -# We must check for the threads library under a number of different -# names; the ordering is very important because some systems -# (e.g. DEC) have both -lpthread and -lpthreads, where one of the -# libraries is broken (non-POSIX). - -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. - -ax_pthread_flags="pthreads none -Kthread -kthread lthread -lpthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" - -# The ordering *is* (sometimes) important. Some notes on the -# individual items follow: - -# pthreads: AIX (must check this before -lpthread) -# none: in case threads are in libc; should be tried before -Kthread and -# other compiler flags to prevent continual compiler warnings -# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc -# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC -# pthread: Linux, etcetera -# --thread-safe: KAI C++ -# pthread-config: use pthread-config program (for GNU Pth library) - -case "${host_cpu}-${host_os}" in - *solaris*) - - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (We need to link with -pthreads/-mt/ - # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather - # a function called by this macro, so we could check for that, but - # who knows whether they'll stub that too in a future libc.) So, - # we'll just look for -pthreads and -lpthread first: - - ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" - ;; -esac - -if test "x$ax_pthread_ok" = "xno"; then -for flag in $ax_pthread_flags; do - - case $flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $flag]) - PTHREAD_CFLAGS="$flag" - ;; - - pthread-config) - AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) - if test "x$ax_pthread_config" = "xno"; then continue; fi - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$flag]) - PTHREAD_LIBS="-l$flag" - ;; - esac - - save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [ax_pthread_ok=yes]) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = "xyes"; then - break; - fi - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi - -# Various other checks: -if test "x$ax_pthread_ok" = "xyes"; then - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_TRY_LINK([#include ], [int attr=$attr; return attr;], - [attr_name=$attr; break]) - done - AC_MSG_RESULT($attr_name) - if test "x$attr_name" != "xPTHREAD_CREATE_JOINABLE"; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi - - AC_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; - *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; - esac - AC_MSG_RESULT(${flag}) - if test "x$flag" != "xno"; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - # More AIX lossage: must compile with xlc_r or cc_r - if test "x$GCC" != "xyes"; then - AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) - else - PTHREAD_CC=$CC - fi - - # The next part tries to detect GCC inconsistency with -shared on some - # architectures and systems. The problem is that in certain - # configurations, when -shared is specified, GCC "forgets" to - # internally use various flags which are still necessary. - - # - # Prepare the flags - # - save_LDFLAGS="$LDFLAGS" - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - save_CC="$CC" - - # Try with the flags determined by the earlier checks. - # - # -Wl,-z,defs forces link-time symbol resolution, so that the - # linking checks with -shared actually have any value - # - # FIXME: -fPIC is required for -shared on many architectures, - # so we specify it here, but the right way would probably be to - # properly detect whether it is actually required. - CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CC="$PTHREAD_CC" - - # In order not to create several levels of indentation, we test - # the value of "$done" until we find the cure or run out of ideas. - done="no" - - # First, make sure the CFLAGS we added are actually accepted by our - # compiler. If not (and OS X's ld, for instance, does not accept -z), - # then we can't do this test. - if test x"$done" = xno; then - AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) - AC_TRY_LINK(,, , [done=yes]) - - if test "x$done" = xyes ; then - AC_MSG_RESULT([no]) - else - AC_MSG_RESULT([yes]) - fi - fi - - if test x"$done" = xyes; then - done="no" - AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [done=yes]) - - if test "x$done" = xyes; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - fi - - # - # Linux gcc on some architectures such as mips/mipsel forgets - # about -lpthread - # - if test x"$done" = xno; then - AC_MSG_CHECKING([whether -lpthread fixes that]) - LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [done=yes]) - - if test "x$done" = xyes; then - AC_MSG_RESULT([yes]) - PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" - else - AC_MSG_RESULT([no]) - fi - fi - # - # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc - # - if test x"$done" = xno; then - AC_MSG_CHECKING([whether -lc_r fixes that]) - LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [done=yes]) - - if test "x$done" = xyes; then - AC_MSG_RESULT([yes]) - PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" - else - AC_MSG_RESULT([no]) - fi - fi - if test x"$done" = xno; then - # OK, we have run out of ideas - AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) - - # so it's not safe to assume that we may use pthreads - acx_pthread_ok=no - fi - - CFLAGS="$save_CFLAGS" - LIBS="$save_LIBS" - CC="$save_CC" - -else - PTHREAD_CC="$CC" -fi - -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test "x$ax_pthread_ok" = "xyes"; then - ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,[1],[Define if you have POSIX threads libraries and header files.]),[$1]) - : -else - ax_pthread_ok=no - $2 -fi -AC_LANG_RESTORE -])dnl AX_PTHREAD diff --git a/libtorrent_utp/m4/ax_python_devel.m4 b/libtorrent_utp/m4/ax_python_devel.m4 deleted file mode 100644 index 0740c640c..000000000 --- a/libtorrent_utp/m4/ax_python_devel.m4 +++ /dev/null @@ -1,323 +0,0 @@ -# =========================================================================== -# http://www.nongnu.org/autoconf-archive/ax_python_devel.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PYTHON_DEVEL([version]) -# -# DESCRIPTION -# -# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it -# in your configure.ac. -# -# This macro checks for Python and tries to get the include path to -# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) -# output variables. It also exports $(PYTHON_EXTRA_LIBS) and -# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. -# -# You can search for some particular version of Python by passing a -# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please -# note that you *have* to pass also an operator along with the version to -# match, and pay special attention to the single quotes surrounding the -# version number. Don't use "PYTHON_VERSION" for this: that environment -# variable is declared as precious and thus reserved for the end-user. -# -# This macro should work for all versions of Python >= 2.1.0. As an end -# user, you can disable the check for the python version by setting the -# PYTHON_NOVERSIONCHECK environment variable to something else than the -# empty string. -# -# If you need to use this macro for an older Python version, please -# contact the authors. We're always open for feedback. -# -# LICENSE -# -# Copyright (c) 2009 Sebastian Huber -# Copyright (c) 2009 Alan W. Irwin -# Copyright (c) 2009 Rafael Laboissiere -# Copyright (c) 2009 Andrew Collier -# Copyright (c) 2009 Matteo Settenvini -# Copyright (c) 2009 Horst Knorr -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program 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 General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) -AC_DEFUN([AX_PYTHON_DEVEL],[ - # - # Allow the use of a (user set) custom python version - # - AC_ARG_VAR([PYTHON_VERSION],[The installed Python - version to use, for example '2.3'. This string - will be appended to the Python interpreter - canonical name.]) - - AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) - if test -z "$PYTHON"; then - AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) - PYTHON_VERSION="" - fi - - # - # Check for a version of Python >= 2.1.0 - # - AC_MSG_CHECKING([for a version of Python >= '2.1.0']) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ - print (ver >= '2.1.0')"` - if test "$ac_supports_python_ver" != "True"; then - if test -z "$PYTHON_NOVERSIONCHECK"; then - AC_MSG_RESULT([no]) - AC_MSG_FAILURE([ -This version of the AC@&t@_PYTHON_DEVEL macro -doesn't work properly with versions of Python before -2.1.0. You may need to re-run configure, setting the -variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, -PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. -Moreover, to disable this check, set PYTHON_NOVERSIONCHECK -to something else than an empty string. -]) - else - AC_MSG_RESULT([skip at user request]) - fi - else - AC_MSG_RESULT([yes]) - fi - - # - # if the macro parameter ``version'' is set, honour it - # - if test -n "$1"; then - AC_MSG_CHECKING([for a version of Python $1]) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ - print (ver $1)"` - if test "$ac_supports_python_ver" = "True"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_ERROR([this package requires Python $1. -If you have it installed, but it isn't the default Python -interpreter in your system path, please pass the PYTHON_VERSION -variable to configure. See ``configure --help'' for reference. -]) - PYTHON_VERSION="" - fi - fi - - # - # Check if you have distutils, else fail - # - AC_MSG_CHECKING([for the distutils Python package]) - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test -z "$ac_distutils_result"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot import Python module "distutils". -Please check your Python installation. The error was: -$ac_distutils_result]) - PYTHON_VERSION="" - fi - - # - # Check for Python include path - # - AC_MSG_CHECKING([for Python include path]) - if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc ());"` - if test -n "${python_path}"; then - python_path="-I$python_path" - fi - PYTHON_CPPFLAGS=$python_path - fi - AC_MSG_RESULT([$PYTHON_CPPFLAGS]) - AC_SUBST([PYTHON_CPPFLAGS]) - - # - # Check for Python library path - # - AC_MSG_CHECKING([for Python library path]) - if test -z "$PYTHON_LDFLAGS"; then - # (makes two attempts to ensure we've got a version number - # from the interpreter) - ac_python_version=`cat<]], - [[Py_Initialize();]]) - ],[pythonexists=yes],[pythonexists=no]) - AC_LANG_POP([C]) - # turn back to default flags - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - - AC_MSG_RESULT([$pythonexists]) - - if test ! "x$pythonexists" = "xyes"; then - AC_MSG_FAILURE([ - Could not link test program to Python. Maybe the main Python library has been - installed in some non-standard library path. If so, pass it to configure, - via the LDFLAGS environment variable. - Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" - ============================================================================ - ERROR! - You probably have to install the development version of the Python package - for your distribution. The exact name of this package varies among them. - ============================================================================ - ]) - PYTHON_VERSION="" - fi - - # - # all done! - # -]) diff --git a/libtorrent_utp/m4/pkgconfig.m4 b/libtorrent_utp/m4/pkgconfig.m4 deleted file mode 100644 index a0b9cd45d..000000000 --- a/libtorrent_utp/m4/pkgconfig.m4 +++ /dev/null @@ -1,155 +0,0 @@ -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -# -# Copyright © 2004 Scott James Remnant . -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# PKG_PROG_PKG_CONFIG([MIN-VERSION]) -# ---------------------------------- -AC_DEFUN([PKG_PROG_PKG_CONFIG], -[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) -m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) -AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=m4_default([$1], [0.9.0]) - AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - PKG_CONFIG="" - fi - -fi[]dnl -])# PKG_PROG_PKG_CONFIG - -# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# Check to see whether a particular set of modules exists. Similar -# to PKG_CHECK_MODULES(), but does not set variables or print errors. -# -# -# Similar to PKG_CHECK_MODULES, make sure that the first instance of -# this or PKG_CHECK_MODULES is called, or make sure to call -# PKG_CHECK_EXISTS manually -# -------------------------------------------------------------- -AC_DEFUN([PKG_CHECK_EXISTS], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -if test -n "$PKG_CONFIG" && \ - AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then - m4_ifval([$2], [$2], [:]) -m4_ifvaln([$3], [else - $3])dnl -fi]) - - -# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) -# --------------------------------------------- -m4_define([_PKG_CONFIG], -[if test -n "$$1"; then - pkg_cv_[]$1="$$1" - elif test -n "$PKG_CONFIG"; then - PKG_CHECK_EXISTS([$3], - [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], - [pkg_failed=yes]) - else - pkg_failed=untried -fi[]dnl -])# _PKG_CONFIG - -# _PKG_SHORT_ERRORS_SUPPORTED -# ----------------------------- -AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi[]dnl -])# _PKG_SHORT_ERRORS_SUPPORTED - - -# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], -# [ACTION-IF-NOT-FOUND]) -# -# -# Note that if there is a possibility the first call to -# PKG_CHECK_MODULES might not happen, you should be sure to include an -# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac -# -# -# -------------------------------------------------------------- -AC_DEFUN([PKG_CHECK_MODULES], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl -AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl - -pkg_failed=no -AC_MSG_CHECKING([for $1]) - -_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) -_PKG_CONFIG([$1][_LIBS], [libs], [$2]) - -m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS -and $1[]_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details.]) - -if test $pkg_failed = yes; then - _PKG_SHORT_ERRORS_SUPPORTED - if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` - else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - - ifelse([$4], , [AC_MSG_ERROR(dnl -[Package requirements ($2) were not met: - -$$1_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -_PKG_TEXT -])], - [AC_MSG_RESULT([no]) - $4]) -elif test $pkg_failed = untried; then - ifelse([$4], , [AC_MSG_FAILURE(dnl -[The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -_PKG_TEXT - -To get pkg-config, see .])], - [$4]) -else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS - AC_MSG_RESULT([yes]) - ifelse([$3], , :, [$3]) -fi[]dnl -])# PKG_CHECK_MODULES diff --git a/libtorrent_utp/parse_bandwidth_log.py b/libtorrent_utp/parse_bandwidth_log.py deleted file mode 100755 index e2403290e..000000000 --- a/libtorrent_utp/parse_bandwidth_log.py +++ /dev/null @@ -1,24 +0,0 @@ -#! /usr/bin/env python -import os, sys, time - -keys = [['upload rate', 'x1y1', 6], ['history entries', 'x1y2', 10], ['queue', 'x1y2', 4]] - -out = open('bandwidth.gnuplot', 'wb') -print >>out, "set term png size 1200,700" -print >>out, 'set output "bandwidth_manager.png"' -print >>out, 'set xrange [0:*]' -print >>out, 'set xlabel "time (ms)"' -print >>out, 'set ylabel "Rate (B/s)"' -print >>out, 'set ytics 10000' -print >>out, 'set y2label "number"' -print >>out, 'set y2range [0:*]' -#print >>out, "set style data lines" -print >>out, "set key box" -print >>out, 'plot', -for k, a, c in keys: - print >>out, ' "%s" using 1:%d title "%s" axes %s with steps,' % (sys.argv[1], c, k, a), -print >>out, 'x=0' -out.close() - -os.system('gnuplot bandwidth.gnuplot'); - diff --git a/libtorrent_utp/parse_buffer_log.py b/libtorrent_utp/parse_buffer_log.py deleted file mode 100755 index 451d873b3..000000000 --- a/libtorrent_utp/parse_buffer_log.py +++ /dev/null @@ -1,90 +0,0 @@ -#! /usr/bin/env python -# Copyright Arvid Norberg 2008. 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) - -import os, sys, time - -lines = open(sys.argv[1], 'rb').readlines() - -#keys = ['send_buffer_utilization'] -keys = ['send_buffer_size', 'used_send_buffer', 'protocol_buffer'] -#keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer'] -#keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer', 'append_send_buffer'] - -average = ['send_buffer_utilization', 'send_buffer_size', 'used_send_buffer'] -average_interval = 120000 -render = 'lines' - -time_limit = -1 -if len(sys.argv) > 2: - time_limit = long(sys.argv[2]) - - -# logfile format: -# -# example: -# 16434 allocate_buffer: 17 -for k in keys: - - last_sample = 0 - average_accumulator = 0 - average_samples = 0 - peak = 0 - - out = open(k + '.dat', 'wb') - eval_average = False - if k in average: - eval_average = True - peak_out = open(k + '_peak.dat', 'wb') - - for l in lines: - l = l.split(' ') - if len(l) != 3: - print l - continue - try: - if l[1] == k + ':': - if time_limit != -1 and long(l[0]) > time_limit: break - time = l[0] - value = l[2] - if eval_average: - while long(time) > last_sample + average_interval: - last_sample = last_sample + average_interval - if average_samples < 1: average_samples = 1 - print >>out, '%d %f' % (last_sample, average_accumulator / average_samples) - print >>peak_out, '%d %f' % (last_sample, peak) - average_accumulator = 0 - average_samples = 0 - peak = 0 - average_accumulator = average_accumulator + float(value) - average_samples = average_samples + 1 - if float(value) > peak: peak = float(value) - else: - print >>out, time + ' ' + value, - except: - print l - - out.close() - peak_out.close() - -out = open('send_buffer.gnuplot', 'wb') -print >>out, "set term png size 1200,700" -print >>out, 'set output "send_buffer.png"' -print >>out, 'set xrange [0:*]' -print >>out, 'set xlabel "time (ms)"' -print >>out, 'set ylabel "bytes (B)"' -print >>out, "set style data lines" -print >>out, "set key box" -print >>out, 'plot', -for k in keys: - if k in average: - print >>out, ' "%s.dat" using 1:2 title "%s %d seconds average" with %s,' % (k, k, average_interval / 1000., render), - print >>out, ' "%s_peak.dat" using 1:2 title "%s %d seconds peak" with %s,' % (k, k, average_interval / 1000., render), - else: - print >>out, ' "%s.dat" using 1:2 title "%s" with %s,' % (k, k, render), -print >>out, 'x=0' -out.close() - -os.system('gnuplot send_buffer.gnuplot') - diff --git a/libtorrent_utp/parse_dht_log.py b/libtorrent_utp/parse_dht_log.py deleted file mode 100755 index 0e6063d06..000000000 --- a/libtorrent_utp/parse_dht_log.py +++ /dev/null @@ -1,78 +0,0 @@ -#! /usr/bin/env python -import sys -import os - -up_time_quanta = 2000 - -f = open(sys.argv[1]) - -announce_histogram = {} -node_uptime_histogram = {} - -counter = 0; - -for line in f: - counter += 1 -# if counter % 1000 == 0: -# print '\r%d' % counter, - try: - if 'distance:' in line: - l = line.split(' ') - idx = l.index('distance:') - - d = int(l[idx+1].strip()) - if not d in announce_histogram: announce_histogram[d] = 0 - announce_histogram[d] += 1 - if 'NODE FAILED' in line: - l = line.split(' ') - idx = l.index('fails:') - if int(l[idx+1].strip()) != 1: continue; - idx = l.index('up-time:') - d = int(l[idx+1].strip()) - # quantize - d = d - (d % up_time_quanta) - if not d in node_uptime_histogram: node_uptime_histogram[d] = 0 - node_uptime_histogram[d] += 1 - except Exception, e: - print line.split(' ') - -out = open('dht_announce_distribution.dat', 'w+') -print 'announce distribution items: %d' % len(announce_histogram) -for k,v in announce_histogram.items(): - print >>out, '%d %d' % (k, v) - print '%d %d' % (k, v) -out.close() - -out = open('dht_node_uptime_distribution.dat', 'w+') -print 'node uptimes: %d' % len(node_uptime_histogram) -for k,v in node_uptime_histogram.items(): - print >>out, '%d %d' % (k + up_time_quanta/2, v) -out.close() - -out = open('dht.gnuplot', 'w+') -out.write(''' -set term png size 1200,700 small -set output "dht_announce_distribution.png" -set title "bucket # announces are made against relative to target node-id" -set ylabel "# of announces" -set style fill solid border -1 pattern 2 -plot "dht_announce_distribution.dat" using 1:2 title "announces" with boxes - -set terminal postscript -set output "dht_announce_distribution.ps" -replot - -set term png size 1200,700 small -set output "dht_node_uptime_distribution.png" -set title "node up time" -set ylabel "# of nodes" -set xlabel "uptime (seconds)" -set boxwidth %f -set style fill solid border -1 pattern 2 -plot "dht_node_uptime_distribution.dat" using 1:2 title "nodes" with boxes -''' % up_time_quanta) -out.close() - -os.system('gnuplot dht.gnuplot'); - - diff --git a/libtorrent_utp/parse_dht_rtt.py b/libtorrent_utp/parse_dht_rtt.py deleted file mode 100755 index 3f4010470..000000000 --- a/libtorrent_utp/parse_dht_rtt.py +++ /dev/null @@ -1,51 +0,0 @@ -#! /usr/bin/env python - -import sys -import os - -quantize = 100 -max_rtt = 5000 - -f = open(sys.argv[1]) -distribution = {} -num_messages = 0 - -for i in range(0, max_rtt, quantize): - distribution[i] = 0 - -for line in f: - time = int(line.split('\t')[1]) - if (time < 0 or time > max_rtt - quantize): continue - num_messages += 1 - time /= quantize - time *= quantize - distribution[time] += 1 - -f = open('round_trip_distribution.log', 'w+') - -for k, v in distribution.items(): - print >>f, '%f %d' % ((k + (quantize / 2)) / 1000.0, v) -f.close(); - -f = open('round_trip_distribution.gnuplot', 'w+') - -f.write(''' -set term png size 1200,700 -set title "Message round trip times" -set terminal postscript -set ylabel "# of requests" -set xlabel "Round trip time (seconds)" -set grid -set style fill solid border -1 pattern 2 -set output "round_trip_distribution.ps" -set boxwidth %f -plot "round_trip_distribution.log" using 1:2 title "requests" with boxes - -set terminal png small -set output "round_trip_distribution.png" -replot -''' % (float(quantize) / 1000.0)) -f.close() - -os.system('gnuplot round_trip_distribution.gnuplot'); - diff --git a/libtorrent_utp/parse_dht_stats.py b/libtorrent_utp/parse_dht_stats.py deleted file mode 100644 index 548334894..000000000 --- a/libtorrent_utp/parse_dht_stats.py +++ /dev/null @@ -1,53 +0,0 @@ -#! /usr/bin/env python -import sys -import os - -gnuplot_scripts = [] - -def gen_stats_gnuplot(name, y, lines): - - global gnuplot_scripts - - stat = open(sys.argv[1]) - line = stat.readline() - while not 'minute:' in line: - line = stat.readline() - - names = line.strip().split(':') - counter = 1 - for i in names: - print '%d: %s' % (counter, i) - counter += 1 - - out = open('%s.gnuplot' % name, 'w+') - out.write(''' -set term png size 1200,700 small -set output "%s.png" -set title "%s" -set ylabel "%s" -set xlabel "time (minutes)" -plot ''' % (name, name.strip('_'), y)) - first = True - for i in lines: - if not first: - out.write(', \\\n') - first = False - out.write('"%s" using 1:%d title "%s" with lines' % (sys.argv[1], names.index(i)+1, i)) - out.write('\n') - - out.write('''set terminal postscript -set output "%s.ps" -replot -''' % (name)) - out.close() - gnuplot_scripts += [name] - -gen_stats_gnuplot('dht_routing_table_size', 'nodes', ['active nodes','passive nodes']) -gen_stats_gnuplot('dht_tracker_table_size', '', ['num torrents', 'num peers']) -gen_stats_gnuplot('dht_announces', 'messages per minute', ['announces per min', 'failed announces per min']) -gen_stats_gnuplot('dht_clients', 'messages per minute', ['total msgs per min', 'az msgs per min', 'ut msgs per min', 'lt msgs per min', 'mp msgs per min', 'gr msgs per min']) -gen_stats_gnuplot('dht_rate', 'bytes per second', ['bytes in per sec', 'bytes out per sec']) -gen_stats_gnuplot('dht_errors', 'messages per minute', ['error replies sent', 'error queries recvd']) - -for i in gnuplot_scripts: - os.system('gnuplot %s.gnuplot' % i); diff --git a/libtorrent_utp/parse_disk_access.py b/libtorrent_utp/parse_disk_access.py deleted file mode 100755 index f4e3155f4..000000000 --- a/libtorrent_utp/parse_disk_access.py +++ /dev/null @@ -1,102 +0,0 @@ -#! /usr/bin/env python - -import os, sys, time - -lines = open(sys.argv[1], 'rb').readlines() - -# logfile format: -# : -# example: -# 16434 read cache: 17 - -keys = ['read', 'write', 'head movement', 'seek per read byte', 'seek per written byte', - 'read operations per second', 'write operations per second'] -colors = ['305030', '503030', '3030f0', '10a010', 'a01010', 'd0d040', 'd040d0'] -style = ['dots', 'points', 'lines', 'lines', 'lines', 'lines', 'lines'] -axis = ['x1y1', 'x1y1', 'x1y2', 'x1y2', 'x1y2', 'x1y2', 'x1y2'] -plot = [True, False, False, False, False, True, False] - -out = open('disk_access_log.dat', 'w+') - -time = 1000000 - -last_pos = 0 -last_t = 0 -cur_movement = 0 -cur_read = 0 -cur_write = 0 -cur_read_ops = 0 -cur_write_ops = 0 - -for l in lines: - try: - # strip newline - l = l[0:-1].split(' ') - t = int(l[0]) - k = l[1] - n = int(l[2]) - except: - print l - continue - - read = '-' - write = '-' - movement = '-' - amount_read = '-' - amount_write = '-' - read_ops = '-' - write_ops = '-' - if k == 'read': - read = '%d' % n - cur_read_ops += 1 - if k == 'write': - write = '%d' % n - cur_write_ops += 1 - if k == 'read_end': cur_read += n - last_pos - if k == 'write_end': cur_write += n - last_pos - - cur_movement += abs(last_pos - n) - last_pos = n - - if last_t + time <= t: - movement = '%d' % cur_movement - if cur_read > 0: - amount_read = '%d' % (cur_movement / cur_read) - if cur_write > 0: - amount_write = '%d' % (cur_movement / cur_write) - read_ops = '%d' % cur_read_ops - write_ops = '%d' % cur_write_ops - cur_movement = 0 - cur_read = 0 - cur_write = 0 - last_t = t - cur_read_ops = 0 - cur_write_ops = 0 - - print >>out, '%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % (t, read, write, movement, amount_read, amount_write, read_ops, write_ops) - -out.close() - -out = open('disk_access.gnuplot', 'wb') -print >>out, "set term png size 1200,700" -print >>out, 'set output "disk_access.png"' -print >>out, 'set xrange [*:*]' -#print >>out, 'set y2range [0:*]' -print >>out, 'set xlabel "time (us)"' -print >>out, 'set ylabel "drive offset"' -#print >>out, 'set y2label "bytes / %d second(s)"' % (time / 1000) -print >>out, "set key box" -print >>out, "set tics nomirror" -print >>out, "set y2tics auto" -print >>out, 'plot', -count = 1 -for k in keys: - count += 1 - if not plot[count-2]: continue - print >>out, ' "disk_access_log.dat" using 1:%d title "%s" with %s lt rgb "#%s" axis %s,' \ - % (count, k, style[count-2], colors[count-2], axis[count-2]), -print >>out, 'x=0' -out.close() - -os.system('gnuplot disk_access.gnuplot') - diff --git a/libtorrent_utp/parse_disk_buffer_log.py b/libtorrent_utp/parse_disk_buffer_log.py deleted file mode 100755 index e914b752f..000000000 --- a/libtorrent_utp/parse_disk_buffer_log.py +++ /dev/null @@ -1,91 +0,0 @@ -#! /usr/bin/env python - -import os, sys, time - -lines = open(sys.argv[1], 'rb').readlines() - -# logfile format: -# : -# example: -# 16434 read cache: 17 - -key_order = ['receive buffer', 'send buffer', 'released send buffer', 'posted send buffer', - 'received send buffer', 'dispatched send buffer', 'queued send buffer', - 'write cache', 'read cache', 'hash temp'] -colors = ['30f030', '001070', '101080', '2040a0', - '4070d0', '80a0f0', 'f03030', - '80f080', 'f08080', '4040ff'] - -keys = [] -fields = {} -maximum = {} -out = open('disk_buffer_log.dat', 'w+') - -field_sum = {} -field_num_samples = {} -field_timestamp = {} - -for c in key_order: - keys.append(c) - fields[c] = 0 - maximum[c] = 0 - field_sum[c] = 0 - field_num_samples[c] = 0 - field_timestamp[c] = 0 - -last_t = 0 -for l in lines: - try: - t = int(l[0:l.find(' ')]) - c = l[l.find(' ')+1:l.find(':')] - n = int(l[l.find(':')+1:-1]) - except: - print l - continue - - if last_t != t: - print >>out, '%d\t' % last_t, - for i in keys: - print >>out, '%d\t' % maximum[i], - print >>out, '\n', - - if not c in keys: continue - - field_sum[c] += fields[c] * float(t - field_timestamp[c]) - field_timestamp[c] = t - - fields[c] = n - - if n > maximum[c]: maximum[c] = n - - if last_t != t: - last_t = t - maximum = fields - -for i in keys: - print '%s: avg: %f' % (i, field_sum[i] / last_t) -print - -out.close() - -out = open('disk_buffer.gnuplot', 'wb') -print >>out, "set term png size 1200,700" -print >>out, 'set output "disk_buffer.png"' -print >>out, 'set xrange [0:*]' -print >>out, 'set xlabel "time (ms)"' -print >>out, 'set ylabel "buffers"' -print >>out, "set style data lines" -print >>out, "set key box" -print >>out, 'plot', -count = 1 + len(keys) -keys.reverse() -for k in keys: - expr = "$%d" % count - for i in xrange(2, count): expr += "+$%d" % i - count -= 1 - print >>out, ' "disk_buffer_log.dat" using 1:(%s) title "%s" with filledcurves x1 lt rgb "#%s",' % (expr, k, colors[count-1]), -print >>out, 'x=0' -out.close() - -os.system('gnuplot disk_buffer.gnuplot') - diff --git a/libtorrent_utp/parse_disk_log.py b/libtorrent_utp/parse_disk_log.py deleted file mode 100755 index 1dc5d5b53..000000000 --- a/libtorrent_utp/parse_disk_log.py +++ /dev/null @@ -1,121 +0,0 @@ -#! /usr/bin/env python -# Copyright Arvid Norberg 2008. 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) - -import os, sys, time - -lines = open(sys.argv[1], 'rb').readlines() - -if len(sys.argv) < 2: - print "usage: parse_disk_log.py logfile [seconds]" - sys.exit(1) - -keys = ['write', 'read', 'read-cache-hit', 'hash', 'move', 'release', 'idle', \ - 'delete', 'check_fastresume', 'check_files', 'clear-cache', \ - 'abort_thread', 'abort_torrent', 'save_resume_data', 'rename_file', \ - 'flushing', 'update_settings', 'finalize_file', 'sorting_job', \ - 'check_cache_hit'] -throughput_keys = ['write', 'read', 'read-cache-hit'] - -# logfile format: -# -# example: -# 34523 idle -# 34722 write - -if len(sys.argv) > 2: - quantization = long(sys.argv[2]) * 1000000 -else: - quantization = 1000000 - -out = open('disk_io.dat', 'wb') -out2 = open('disk_throughput.dat', 'wb') -state = 'idle' -time = -1 -start_time = -1 -i = 0 -state_timer = {} -throughput = {} -for k in keys: state_timer[k] = 0 -for k in throughput_keys: throughput[k] = 0 - -for l in lines: - l = l.strip().split() - if len(l) < 2: - print l - continue -# try: - new_time = long(l[0]) - if time == -1: - time = new_time - i = new_time - start_time = new_time - while new_time > i + quantization: - i += quantization - state_timer[state] += i - time - time = i - for k in keys: print >>out, (state_timer[k] / float(quantization) * 100.), - print >>out - print >>out2, time - start_time, - for k in throughput_keys: - print >>out2, throughput[k] * 1000 / float(quantization), - print '-- %s %d' % (k, throughput[k]) - print >>out2 - for k in keys: state_timer[k] = 0 - for k in throughput_keys: throughput[k] = 0 - state_timer[state] += new_time - time - time = new_time - state = l[1] - if state in throughput_keys: - throughput[state] += long(l[2]) -# except: -# print l - -i += quantization -state_timer[state] += i - time -time = i -for k in keys: print >>out, (state_timer[k] / float(quantization) * 100.), -print >>out -print >>out2, time - start_time, -for k in throughput_keys: - print >>out2, throughput[k] * 1000 / float(quantization), - print '-- %s %d' % (k, throughput[k]) -print >>out2 -for k in keys: state_timer[k] = 0 -for k in throughput_keys: throughput[k] = 0 -out.close() -out2.close() - -out = open('disk_io.gnuplot', 'wb') -print >>out, "set term png size 1200,700" - -print >>out, 'set output "disk_throughput.png"' -print >>out, 'set title "disk throughput per %f second(s)"' % (quantization / float(1000000)) -print >>out, 'set ylabel "throughput (kB/s)"' -print >>out, 'plot', -i = 0 -for k in throughput_keys: - print >>out, ' "disk_throughput.dat" using 1:%d title "%s" with lines,' % (i + 2, throughput_keys[i]), - i = i + 1 -print >>out, 'x=0' - -print >>out, 'set output "disk_io.png"' -print >>out, 'set ylabel "utilization (%)"' -print >>out, 'set xrange [0:*]' -print >>out, 'set title "disk io utilization per %f second(s)"' % (quantization / float(1000000)) -print >>out, "set key box" -print >>out, "set style data histogram" -print >>out, "set style histogram rowstacked" -print >>out, "set style fill solid" -print >>out, 'plot', -i = 0 -for k in keys: - if k != 'idle': - print >>out, ' "disk_io.dat" using %d title "%s",' % (i + 1, keys[i]), - i = i + 1 -print >>out, 'x=0' -out.close() - -os.system('gnuplot disk_io.gnuplot'); - diff --git a/libtorrent_utp/parse_memory_log.py b/libtorrent_utp/parse_memory_log.py deleted file mode 100755 index f7a619458..000000000 --- a/libtorrent_utp/parse_memory_log.py +++ /dev/null @@ -1,126 +0,0 @@ -#! /usr/bin/env python -import os, sys, time - -# usage: memory.log memory_index.log - -lines = open(sys.argv[1], 'rb').readlines() -index = open(sys.argv[2], 'rb').readlines() - -# logfile format: -# #
-# example: -# #12 38 A 0xd902a0 16 16 0 16 - -allocation_points_to_print = 30 - -def print_allocation_point(ap): - print 'space_time: %d kBms' % (ap['spacetime'] / 1024) - print 'allocations: %d' % ap['allocations'] - print 'peak: %d kB' % (ap['peak'] / 1024) - print 'stack: ' - counter = 0 - for e in ap['stack']: - print '#%d %s' % (counter, e) - counter += 1 - -allocation_points = [] -for l in index: - l = l.split('#') - l.pop(0) - ap = { 'allocations': 0, 'peak': 0, 'spacetime': 0, 'allocation_point': len(allocation_points), 'stack': l} - allocation_points.append(ap); - -for l in lines: - l = l.lstrip('#').rstrip('\n').split(' ') - if len(l) != 8: - print l - continue - try: - ap = int(l[0]) - allocation_points[ap]['allocations'] += 1 - allocation_points[ap]['peak'] = int(l[7]) - allocation_points[ap]['spacetime'] = int(l[6]) - except Exception, e: - print type(e), e, l - -print '=== space time ===' - -hot_ap = [] -allocation_points.sort(key = lambda x:x['spacetime'], reverse=True); -counter = 0 -for ap in allocation_points[0:allocation_points_to_print]: - print '== %d ==' % counter - counter += 1 - print_allocation_point(ap) - hot_ap.append(ap['allocation_point']); - -print '=== allocations ===' - -allocation_points.sort(key = lambda x:x['allocations'], reverse=True); -for ap in allocation_points[0:allocation_points_to_print]: - print_allocation_point(ap) - -print '=== peak ===' - -allocation_points.sort(key = lambda x:x['peak'], reverse=True); -for ap in allocation_points[0:allocation_points_to_print]: - print_allocation_point(ap) - -# generate graph -lines = open(sys.argv[1], 'rb').readlines() - -out = open('memory.dat', 'wb') -cur_line = [0] * allocation_points_to_print -prev_line = [0] * allocation_points_to_print -last_time = 0 - -for l in lines: - l = l.lstrip('#').rstrip('\n').split(' ') - if len(l) != 8: - print l - continue - try: - time = int(l[1]) - if time != last_time: - print >>out, last_time, '\t', - for i in range(allocation_points_to_print): - if cur_line[i] == -1: - print >>out, prev_line[i], '\t', - else: - print >>out, cur_line[i], '\t', - prev_line[i] = cur_line[i] - print >>out - cur_line = [-1] * allocation_points_to_print - last_time = time - - size = int(l[5]) - ap = int(l[0]) - if ap in hot_ap: - index = hot_ap.index(ap) - cur_line[index] = max(cur_line[index], size) - - except Exception, e: - print type(e), e, l - -out.close() - -out = open('memory.gnuplot', 'wb') -print >>out, "set term png size 1200,700" -print >>out, 'set output "memory.png"' -print >>out, 'set xrange [0:*]' -print >>out, 'set xlabel "time (ms)"' -print >>out, 'set ylabel "bytes (B)"' -print >>out, "set style data lines" -print >>out, "set key box" -print >>out, 'plot', -for k in range(allocation_points_to_print): - print >>out, ' "memory.dat" using 1:(', - for i in range(k, allocation_points_to_print): - if i == k: print >>out, '$%d' % (i + 2), - else: print >>out, '+$%d' % (i + 2), - print >>out, ') title "%d" with filledcurves x1, \\' % k -print >>out, 'x=0' -out.close() - -os.system('gnuplot memory.gnuplot'); - diff --git a/libtorrent_utp/parse_sample.py b/libtorrent_utp/parse_sample.py deleted file mode 100644 index 6310eb38c..000000000 --- a/libtorrent_utp/parse_sample.py +++ /dev/null @@ -1,101 +0,0 @@ -import sys - -# to use this script, first run 'sample' to sample your libtorrent based process -# the output can then be passed to this script to auto-fold call stacks at -# relevant depths and to filter out low sample counts -f = open(sys.argv[1]) - -def parse_line(l): - indentation = 0 - while indentation < len(l) and l[indentation] == ' ': - indentation += 1 - if indentation == 0: - return (0, 0, '') - - - l = l.strip().split(' ') - samples = int(l[0]) - fun = ' '.join(l[1:]) - - return (indentation, samples, fun) - -fold = -1 - -try: - sample_limit = int(sys.argv[2]) -except: - sample_limit = 5 - -fun_samples = {} - -for l in f: - if 'Sort by top of stack' in l: break - - indentation, samples, fun = parse_line(l) - if samples < sample_limit: continue - if fold != -1 and indentation > fold: continue - fold = -1 - - if '__gnu_cxx::__normal_iterator<' in fun: - fold = indentation - 1 - continue - - if 'boost::_bi::bind_t' in fun: continue - if 'boost::_bi::list' in fun: continue - if 'boost::_mfi::mf' in fun: continue - if 'boost::_bi::storage' in fun: continue - -# should only add leaves - if fun in fun_samples: fun_samples[fun] += samples - else: fun_samples[fun] = samples - - output = '%s%-4d %s' % (' ' * (indentation/2), samples, fun) - if len(output) > 200: output = output[0:200] - print output - - if 'invariant_checker_impl' in fun: fold = indentation - if '::find_POD' in fun: fold = indentation - if 'SHA1_Update' in fun: fold = indentation - if 'boost::detail::function::basic_vtable' in fun: fold = indentation - if 'operator new' in fun: fold = indentation - if 'malloc' == fun: fold = indentation - if 'free' == fun: fold = indentation - if 'std::_Rb_tree' in fun: fold = indentation - if 'pthread_cond_wait' in fun: fold = indentation - if 'mp_exptmod' == fun: fold = indentation - if '::check_invariant()' in fun: fold = indentation - if 'libtorrent::condition::wait' in fun: fold = indentation - if 'libtorrent::sleep' in fun: fold = indentation - if 'puts' == fun: fold = indentation - if 'boost::asio::basic_stream_socket' in fun: fold = indentation - if 'recvmsg' == fun: fold = indentation - if 'sendmsg' == fun: fold = indentation - if 'semaphore_signal_trap' == fun: fold = indentation - if 'boost::detail::atomic_count::operator' in fun: fold = indentation - if 'pthread_mutex_lock' == fun: fold = indentation - if 'pthread_mutex_unlock' == fun: fold = indentation - if '>::~vector()' == fun: fold = indentation - if 'szone_free_definite_size' == fun: fold = indentation - if 'snprintf' == fun: fold = indentation - if 'usleep' == fun: fold = indentation - if 'pthread_mutex_lock' == fun: fold = indentation - if 'pthread_mutex_unlock' == fun: fold = indentation - if 'std::string::append' in fun: fold = indentation - if 'getipnodebyname' == fun: fold = indentation - if '__gnu_debug::_Safe_iterator>out, "set term png size 1200,700" - print >>out, 'set output "session_stats_%s.png"' % name - print >>out, 'set xrange [0:*]' - print >>out, 'set xlabel "time (s)"' - print >>out, 'set ylabel "number"' - print >>out, 'set y2label "Rate (B/s)"' - print >>out, 'set y2range [0:*]' - print >>out, 'set y2tics auto' - print >>out, "set tics nomirror" - print >>out, "set style data lines" - print >>out, "set key box" - print >>out, 'plot', - column = 2 - for k in keys: - if k not in lines: - column = column + 1 - continue - print >>out, ' "%s" using 1:%d title "%s" axes %s with steps,' % (sys.argv[1], column, k, axes[column-2]), - column = column + 1 - print >>out, 'x=0' - out.close() - os.system('gnuplot session_stats_%s.gnuplot' % name); - -gen_report('rates', ['upload rate', 'download rate', 'downloading torrents', 'seeding torrents', 'peers', 'unchoked peers']) -gen_report('peers', ['peers', 'connecting peers', 'unchoked peers', 'num list peers']) -gen_report('buffers', ['upload rate', 'download rate', 'disk block buffers']) - diff --git a/libtorrent_utp/project-root.jam b/libtorrent_utp/project-root.jam deleted file mode 100755 index e69de29bb..000000000 diff --git a/libtorrent_utp/set_version.py b/libtorrent_utp/set_version.py deleted file mode 100644 index 5f60c8a54..000000000 --- a/libtorrent_utp/set_version.py +++ /dev/null @@ -1,46 +0,0 @@ -#! /usr/bin/env python -import os -import sys - -version = (int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4])) - -def substitute_file(name): - subst = '' - f = open(name) - for l in f: - if '#define LIBTORRENT_VERSION_MAJOR' in l and name.endswith('.hpp'): - l = '#define LIBTORRENT_VERSION_MAJOR %d\n' % version[0] - elif '#define LIBTORRENT_VERSION_MINOR' in l and name.endswith('.hpp'): - l = '#define LIBTORRENT_VERSION_MINOR %d\n' % version[1] - elif '#define LIBTORRENT_VERSION_TINY' in l and name.endswith('.hpp'): - l = '#define LIBTORRENT_VERSION_TINY %d\n' % version[2] - elif '#define LIBTORRENT_VERSION' in l and name.endswith('.hpp'): - l = '#define LIBTORRENT_VERSION "%d.%d.%d.%d"\n' % (version[0], version[1], version[2], version[3]) - elif 'AC_INIT([libtorrent-rasterbar]' in l and name.endswith('.ac'): - l = 'AC_INIT([libtorrent-rasterbar],[%d.%d.%d],[arvid@cs.umu.se],\n' % (version[0], version[1], version[2]) - elif 'set (VERSION ' in l and name.endswith('.txt'): - l = 'set (VERSION "%d.%d.%d")\n' % (version[0], version[1], version[2]) - elif ':Version: ' in l and name.endswith('.rst'): - l = ':Version: %d.%d.%d\n' % (version[0], version[1], version[2]) - elif 'VERSION = ' in l and name.endswith('Jamfile'): - l = 'VERSION = %d.%d.%d ;\n' % (version[0], version[1], version[2]) - - subst += l - - f.close() - open(name, 'w+').write(subst) - - -substitute_file('include/libtorrent/version.hpp') -substitute_file('CMakeLists.txt') -substitute_file('configure.ac') -substitute_file('docs/manual.rst') -substitute_file('docs/building.rst') -substitute_file('docs/features.rst') -substitute_file('docs/contributing.rst') -substitute_file('docs/utp.rst') -substitute_file('docs/make_torrent.rst') -substitute_file('docs/tuning.rst') -substitute_file('Jamfile') - - diff --git a/libtorrent_utp/src/ConvertUTF.cpp b/libtorrent_utp/src/ConvertUTF.cpp deleted file mode 100644 index f93a1d56f..000000000 --- a/libtorrent_utp/src/ConvertUTF.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/* - * 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/libtorrent_utp/src/GeoIP.c b/libtorrent_utp/src/GeoIP.c deleted file mode 100644 index 927f0c623..000000000 --- a/libtorrent_utp/src/GeoIP.c +++ /dev/null @@ -1,1077 +0,0 @@ -/* -*- 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(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(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(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(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)) { -#ifndef WIN32 - 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; - -#ifndef WIN32 - 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; -#ifndef WIN32 - /* 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) { -#ifndef WIN32 - 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/libtorrent_utp/src/Makefile.am b/libtorrent_utp/src/Makefile.am deleted file mode 100644 index f8c27a191..000000000 --- a/libtorrent_utp/src/Makefile.am +++ /dev/null @@ -1,107 +0,0 @@ -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/traversal_algorithm.cpp -endif - -if WITH_SHIPPED_GEOIP -GEOIP_SOURCES = GeoIP.c -endif - -libtorrent_rasterbar_la_SOURCES = \ - web_connection_base.cpp \ - alert.cpp \ - allocator.cpp \ - assert.cpp \ - bandwidth_limit.cpp \ - bandwidth_manager.cpp \ - bandwidth_queue_entry.cpp \ - broadcast_socket.cpp \ - bt_peer_connection.cpp \ - connection_queue.cpp \ - ConvertUTF.cpp \ - create_torrent.cpp \ - disk_buffer_holder.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 \ - 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 \ - lazy_bdecode.cpp \ - logger.cpp \ - lsd.cpp \ - lt_trackers.cpp \ - magnet_uri.cpp \ - metadata_transfer.cpp \ - natpmp.cpp \ - parse_url.cpp \ - pe_crypto.cpp \ - peer_connection.cpp \ - piece_picker.cpp \ - packet_buffer.cpp \ - policy.cpp \ - puff.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 \ - 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 \ - utp_socket_manager.cpp \ - utp_stream.cpp \ - web_peer_connection.cpp \ - \ - $(KADEMLIA_SOURCES) \ - $(GEOIP_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 = -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ -AM_CPPFLAGS = -ftemplate-depth-100 -I$(top_srcdir)/include @DEBUGFLAGS@ @OPENSSL_INCLUDES@ - -#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/libtorrent_utp/src/alert.cpp b/libtorrent_utp/src/alert.cpp deleted file mode 100644 index 938a19529..000000000 --- a/libtorrent_utp/src/alert.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.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 - -namespace libtorrent { - - alert::alert() : m_timestamp(time_now()) {} - alert::~alert() {} - ptime alert::timestamp() const { return m_timestamp; } - - - std::string torrent_alert::message() const - { - if (!handle.is_valid()) return " - "; - if (handle.name().empty()) - { - char msg[41]; - to_hex((char const*)&handle.info_hash()[0], 20, msg); - return msg; - } - return handle.name(); - } - - std::string peer_alert::message() const - { - error_code ec; - return torrent_alert::message() + " peer (" + ip.address().to_string(ec) - + ", " + identify_client(pid) + ")"; - } - - std::string tracker_alert::message() const - { - return torrent_alert::message() + " (" + url + ")"; - } - - std::string read_piece_alert::message() const - { - char msg[200]; - snprintf(msg, sizeof(msg), "%s: piece %s %u", torrent_alert::message().c_str() - , buffer ? "successful" : "failed", piece); - return msg; - } - - 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; - } - - 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; - } - - 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, error.message().c_str()); - return ret; - } - - 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" - }; - - return torrent_alert::message() + ": performance warning: " - + warning_str[warning_code]; - } - - 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]; - } - - std::string tracker_error_alert::message() const - { - char ret[400]; - snprintf(ret, sizeof(ret), "%s (%d) %s (%d)" - , tracker_alert::message().c_str(), status_code - , msg.c_str(), times_in_row); - return ret; - } - - std::string tracker_warning_alert::message() const - { - return tracker_alert::message() + " warning: " + msg; - } - - 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; - } - - std::string scrape_failed_alert::message() const - { - return tracker_alert::message() + " scrape failed: " + msg; - } - - 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; - } - - 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; - } - - std::string tracker_announce_alert::message() const - { - const static char* event_str[] = {"none", "completed", "started", "stopped", "paused"}; - TORRENT_ASSERT_VAL(event < sizeof(event_str)/sizeof(event_str[0]), event); - return tracker_alert::message() + " sending announce (" + event_str[event] + ")"; - } - - 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; - } - - std::string peer_ban_alert::message() const - { - return peer_alert::message() + " banned peer"; - } - - std::string peer_unsnubbed_alert::message() const - { - return peer_alert::message() + " peer unsnubbed"; - } - - std::string peer_snubbed_alert::message() const - { - return peer_alert::message() + " peer snubbed"; - } - - - - 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)" - , torrent_alert::message().c_str(), request.piece, request.start, request.length); - return ret; - } - - - 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; - } - - - 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; - } - - 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; - } - - 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; - } - - 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; - } - - 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; - } - - std::string listen_failed_alert::message() const - { - char ret[200]; - snprintf(ret, sizeof(ret), "listening on %s failed: %s" - , print_endpoint(endpoint).c_str(), error.message().c_str()); - return ret; - } - - std::string listen_succeeded_alert::message() const - { - char ret[200]; - snprintf(ret, sizeof(ret), "successfully listening on %s", print_endpoint(endpoint).c_str()); - return ret; - } - - 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] - + ": " + error.message(); - } - - 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; - } - - 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; - } - - 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; - } - - 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; - } - - - - alert_manager::alert_manager(io_service& ios) - : m_alert_mask(alert::error_notification) - , m_queue_size_limit(queue_size_limit_default) - , m_ios(ios) - {} - - alert_manager::~alert_manager() - { - while (!m_alerts.empty()) - { - 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(); - -// system_time end = get_system_time() -// + boost::posix_time::microseconds(total_microseconds(max_wait)); - - // apparently this call can be interrupted - // prematurely if there are other signals -// while (m_condition.timed_wait(lock, end)) -// if (!m_alerts.empty()) return m_alerts.front(); - - ptime start = time_now_hires(); - - // TODO: change this to use an asio timer instead - while (m_alerts.empty()) - { - lock.unlock(); - sleep(50); - lock.lock(); - if (time_now_hires() - start >= max_wait) return 0; - } - return m_alerts.front(); - } - - 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()) - { - m_dispatch(std::auto_ptr(alerts.front())); - alerts.pop_front(); - } - } - - void dispatch_alert(boost::function dispatcher - , alert* alert_) - { - std::auto_ptr holder(alert_); - dispatcher(*alert_); - } - - void alert_manager::post_alert(const alert& alert_) - { - mutex::scoped_lock lock(m_mutex); - - if (m_dispatch) - { - TORRENT_ASSERT(m_alerts.empty()); - m_dispatch(std::auto_ptr(alert_.clone())); - return; - } - - if (m_alerts.size() >= m_queue_size_limit) return; - m_alerts.push_back(alert_.clone().release()); - m_condition.signal(lock); - m_condition.clear(lock); - } - - 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); - } - - 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_; - } - - 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] - , transferred[4] - , transferred[5] - , transferred[6] - , transferred[7] - , transferred[8] - , transferred[9]); - return msg; - } - - cache_flushed_alert::cache_flushed_alert(torrent_handle const& h): torrent_alert(h) {} - - 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; - } - - 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; - } - - std::string trackerid_alert::message() const - { - return "trackerid received: " + trackerid; - } - -} // namespace libtorrent - diff --git a/libtorrent_utp/src/allocator.cpp b/libtorrent_utp/src/allocator.cpp deleted file mode 100644 index aaeca05ad..000000000 --- a/libtorrent_utp/src/allocator.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - -Copyright (c) 2009, 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" - -#ifdef TORRENT_WINDOWS -#include -#elif defined TORRENT_BEOS -#include -#include // malloc/free -#else -#include // valloc/free -#include // _SC_PAGESIZE -#endif - -#if TORRENT_USE_MEMALIGN || TORRENT_USE_POSIX_MEMALIGN -#include // memalign -#endif - -#ifdef TORRENT_DEBUG_BUFFERS -#include -#include "libtorrent/size_type.hpp" - -struct alloc_header -{ - libtorrent::size_type size; - int magic; -}; - -#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(const size_type bytes) - { -#ifdef TORRENT_DEBUG_BUFFERS - int page = page_size(); - char* ret = (char*)valloc(bytes + 2 * page); - // make the two surrounding pages non-readable and -writable - TORRENT_ASSERT((bytes & (page-1)) == 0); - alloc_header* h = (alloc_header*)ret; - h->size = bytes; - h->magic = 0x1337; - mprotect(ret, page, PROT_READ); - mprotect(ret + page + bytes, page, PROT_READ); -// fprintf(stderr, "malloc: %p head: %p tail: %p size: %d\n", ret + page, ret, ret + page + bytes, int(bytes)); - - return ret + page; -#endif - -#if TORRENT_USE_POSIX_MEMALIGN - void* ret; - if (posix_memalign(&ret, page_size(), bytes) != 0) ret = 0; - return (char*)ret; -#elif TORRENT_USE_MEMALIGN - return (char*)memalign(page_size(), bytes); -#elif defined TORRENT_WINDOWS - return (char*)VirtualAlloc(0, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -#elif defined TORRENT_BEOS - void* ret = 0; - 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 0; - return (char*)ret; -#else - return (char*)valloc(bytes); -#endif - } - - void page_aligned_allocator::free(char* const block) - { - -#ifdef TORRENT_DEBUG_BUFFERS - 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); - TORRENT_ASSERT((h->size & (page-1)) == 0); - TORRENT_ASSERT(h->magic == 0x1337); - mprotect(block + h->size, 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; - - ::free(block - page); - return; -#endif - -#ifdef TORRENT_WINDOWS - VirtualFree(block, 0, MEM_RELEASE); -#elif defined TORRENT_BEOS - area_id id = area_for(block); - if (id < B_OK) return; - delete_area(id); -#else - ::free(block); -#endif - } - - -} - diff --git a/libtorrent_utp/src/assert.cpp b/libtorrent_utp/src/assert.cpp deleted file mode 100644 index c22d219c2..000000000 --- a/libtorrent_utp/src/assert.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - -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. - -*/ - -#ifdef TORRENT_DEBUG - -#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; -} - -#else -std::string demangle(char const* name) { return name; } -#endif - -#include -#include -#include -#include "libtorrent/version.hpp" - -// execinfo.h is available in the MacOS X 10.5 SDK. -#if (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)) -#include - -void print_backtrace(FILE* out, char const* label) -{ - void* stack[50]; - int size = backtrace(stack, 50); - char** symbols = backtrace_symbols(stack, size); - - fprintf(out, "%s\n", label); - for (int i = 1; i < size; ++i) - { - fprintf(out, "%d: %s\n", i, demangle(symbols[i]).c_str()); - } - - free(symbols); -} -#else - -void print_backtrace(FILE* out, char const* label) {} - -#endif - -#if TORRENT_PRODUCTION_ASSERTS -char const* libtorrent_assert_log = "asserts.log"; -#endif - -void assert_fail(char const* expr, int line, char const* file, char const* function, char const* value) -{ - -#if TORRENT_PRODUCTION_ASSERTS - FILE* out = fopen(libtorrent_assert_log, "a+"); - if (out == 0) out = stderr; -#else - FILE* out = stderr; -#endif - - fprintf(out, "assertion failed. Please file a bugreport at " - "http://code.rasterbar.com/libtorrent/newticket\n" - "Please include the following information:\n\n" - "version: " LIBTORRENT_VERSION "\n" - "%s\n" - "file: '%s'\n" - "line: %d\n" - "function: %s\n" - "expression: %s\n" - "%s%s" - , LIBTORRENT_REVISION, file, line, function, expr - , value ? value : "", value ? "\n" : ""); - - print_backtrace(out, "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 - -void assert_fail(char const* expr, int line, char const* file, char const* function) {} - -#endif - diff --git a/libtorrent_utp/src/bandwidth_limit.cpp b/libtorrent_utp/src/bandwidth_limit.cpp deleted file mode 100644 index 7ab04fdec..000000000 --- a/libtorrent_utp/src/bandwidth_limit.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - -Copyright (c) 2009, 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() - : 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 / 31); - 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/libtorrent_utp/src/bandwidth_manager.cpp b/libtorrent_utp/src/bandwidth_manager.cpp deleted file mode 100644 index 5881e1010..000000000 --- a/libtorrent_utp/src/bandwidth_manager.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - -Copyright (c) 2009, 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 = false -#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; - m_queue.clear(); - m_queued_bytes = 0; - } - -#ifdef TORRENT_DEBUG - 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(); - } - - int 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 - void 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; - - 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) bwr.channel[i++] = chan1; - if (chan2 && chan2->throttle() > 0) bwr.channel[i++] = chan2; - if (chan3 && chan3->throttle() > 0) bwr.channel[i++] = chan3; - if (chan4 && chan4->throttle() > 0) bwr.channel[i++] = chan4; - if (chan5 && chan5->throttle() > 0) 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 - bwr.peer->assign_bandwidth(m_channel, blk); - return; - } - m_queued_bytes += blk; - m_queue.push_back(bwr); - } - -#ifdef TORRENT_DEBUG - void bandwidth_manager::check_invariant() const - { - int 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; - - int dt_milliseconds = total_milliseconds(dt); - if (dt_milliseconds > 3000) dt_milliseconds = 3000; - - // for each bandwidth channel, call update_quota(dt) - - std::vector channels; - - 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 < 5 && i->channel[j]; ++j) - { - bandwidth_channel* bwc = i->channel[j]; - bwc->return_quota(i->assigned); - } - - i = m_queue.erase(i); - continue; - } - for (int j = 0; j < 5 && 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 < 5 && 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(dt_milliseconds); - } - - queue_t tm; - - 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/libtorrent_utp/src/bandwidth_queue_entry.cpp b/libtorrent_utp/src/bandwidth_queue_entry.cpp deleted file mode 100644 index d95b219d6..000000000 --- a/libtorrent_utp/src/bandwidth_queue_entry.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - -Copyright (c) 2009, 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); - for (int j = 0; j < 5 && channel[j]; ++j) - { - if (channel[j]->throttle() == 0) continue; - TORRENT_ASSERT(channel[j]->distribute_quota - < (std::numeric_limits::max)() / priority); - quota = (std::min)(int(boost::uint64_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); - --ttl; - return quota; - } -} - diff --git a/libtorrent_utp/src/broadcast_socket.cpp b/libtorrent_utp/src/broadcast_socket.cpp deleted file mode 100644 index 96c8cd502..000000000 --- a/libtorrent_utp/src/broadcast_socket.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - -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. - -*/ - -#include - -#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 - -#ifndef NDEBUG -//#include "libtorrent/socket_io.hpp" -#endif - -#if BOOST_VERSION < 103500 -#include -#include -#else -#include -#include -#endif - -namespace libtorrent -{ - bool is_local(address const& a) - { -#if TORRENT_USE_IPV6 - if (a.is_v6()) return a.to_v6().is_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 - } - - bool is_loopback(address const& addr) - { -#if TORRENT_USE_IPV6 - if (addr.is_v4()) - return addr.to_v4() == address_v4::loopback(); - else - return addr.to_v6() == address_v6::loopback(); -#else - return addr.to_v4() == address_v4::loopback(); -#endif - } - - bool is_multicast(address const& addr) - { -#if TORRENT_USE_IPV6 - if (addr.is_v4()) - return addr.to_v4().is_multicast(); - else - return addr.to_v6().is_multicast(); -#else - return addr.to_v4().is_multicast(); -#endif - } - - bool is_any(address const& addr) - { -#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_EXPORT bool is_teredo(address const& addr) - { -#if TORRENT_USE_IPV6 - 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; -#else - return false; -#endif - } - - bool supports_ipv6() - { -#if TORRENT_USE_IPV6 - error_code ec; - address::from_string("::1", ec); - return !ec; -#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. - 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::static_size * 8 - - common_bits(b1.c_array(), b2.c_array(), 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::static_size * 8 - - common_bits(b1.c_array(), b2.c_array(), b1.size()); -#endif - } - - broadcast_socket::broadcast_socket(io_service& ios - , udp::endpoint const& multicast_endpoint - , receive_handler_t const& handler - , bool loopback) - : m_multicast_endpoint(multicast_endpoint) - , m_on_receive(handler) - , m_ip_broadcast(false) - { - TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address())); - - using namespace asio::ip::multicast; - - error_code ec; - std::vector interfaces = enum_net_interfaces(ios, ec); - -#if TORRENT_USE_IPV6 - if (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() != multicast_endpoint.address().is_v4()) continue; - // ignore any loopback interface - if (!loopback && is_loopback(i->interface_address)) continue; - - ec = error_code(); - open_multicast_socket(ios, i->interface_address, loopback, ec); -#ifndef NDEBUG -// fprintf(stderr, "broadcast socket [ if: %s group: %s ] %s\n" -// , i->interface_address.to_string().c_str() -// , print_address(multicast_endpoint.address()).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::enable_ip_broadcast(bool e) - { - if (e == m_ip_broadcast) return; - m_ip_broadcast = e; - - asio::socket_base::broadcast option(m_ip_broadcast); - error_code ec; - for (std::list::iterator i = m_unicast_sockets.begin() - , end(m_unicast_sockets.end()); i != end; ++i) - { - if (i->socket) continue; - i->socket->set_option(option, ec); - } - } - - 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)); - } - - 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(); -#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)); - } - - void broadcast_socket::send(char const* buffer, int size, error_code& ec) - { - 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); -#ifndef NDEBUG -// 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) - { -#ifndef NDEBUG -// 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); - if (m_ip_broadcast && i->socket->local_endpoint(e).address().is_v4()) - i->socket->send_to(asio::buffer(buffer, size) - , udp::endpoint(i->broadcast_address(), m_multicast_endpoint.port()), 0, e); -#ifndef NDEBUG -// 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) - { -#ifndef NDEBUG -// 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 - if (ec || bytes_transferred == 0 || !m_on_receive) return; - m_on_receive(s->remote, s->buffer, bytes_transferred); - 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)); - } - - 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_on_receive.clear(); - } -} - - diff --git a/libtorrent_utp/src/bt_peer_connection.cpp b/libtorrent_utp/src/bt_peer_connection.cpp deleted file mode 100644 index c9271b4be..000000000 --- a/libtorrent_utp/src/bt_peer_connection.cpp +++ /dev/null @@ -1,3431 +0,0 @@ -/* - -Copyright (c) 2003 - 2006, Arvid Norberg -Copyright (c) 2007, 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 "libtorrent/pch.hpp" - -#include -#include -#include - -#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" - -#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, - 0, 0, - &bt_peer_connection::on_extended - }; - - - bt_peer_connection::bt_peer_connection( - session_impl& ses - , boost::weak_ptr tor - , shared_ptr s - , tcp::endpoint const& remote - , policy::peer* peerinfo) - : peer_connection(ses, tor, s, remote - , peerinfo) - , m_state(read_protocol_identifier) -#ifndef TORRENT_DISABLE_EXTENSIONS - , m_upload_only_id(0) - , m_share_mode_id(0) - , m_supports_extensions(false) -#endif - , m_supports_dht_port(false) - , m_supports_fast(false) -#ifndef TORRENT_DISABLE_ENCRYPTION - , m_encrypted(false) - , m_rc4_encrypted(false) - , m_sync_bytes_read(0) - , m_enc_send_buffer(0, 0) -#endif -#ifdef TORRENT_DEBUG - , m_sent_bitfield(false) - , m_in_constructor(true) - , m_sent_handshake(false) -#endif - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << "*** bt_peer_connection\n"; -#endif - -#ifdef TORRENT_DEBUG - m_in_constructor = false; - m_encrypted_bytes = 0; -#endif - } - - bt_peer_connection::bt_peer_connection( - session_impl& ses - , boost::shared_ptr s - , tcp::endpoint const& remote - , policy::peer* peerinfo) - : peer_connection(ses, s, remote, peerinfo) - , m_state(read_protocol_identifier) -#ifndef TORRENT_DISABLE_EXTENSIONS - , m_supports_extensions(false) -#endif - , m_supports_dht_port(false) - , m_supports_fast(false) -#ifndef TORRENT_DISABLE_ENCRYPTION - , m_encrypted(false) - , m_rc4_encrypted(false) - , m_sync_bytes_read(0) - , m_enc_send_buffer(0, 0) -#endif -#ifdef TORRENT_DEBUG - , m_sent_bitfield(false) - , m_in_constructor(true) - , m_sent_handshake(false) -#endif - { - - // we are not attached to any torrent yet. - // we have to wait for the handshake to see - // which torrent the connector want's to connect to - - - // upload bandwidth will only be given to connections - // that are part of a torrent. Since this is an incoming - // connection, we have to give it some initial bandwidth - // to send the handshake. -#ifndef TORRENT_DISABLE_ENCRYPTION - m_quota[download_channel] = 2048; - m_quota[upload_channel] = 2048; -#else - m_quota[download_channel] = 80; - m_quota[upload_channel] = 80; -#endif - -#ifdef TORRENT_DEBUG - m_in_constructor = false; - m_encrypted_bytes = 0; -#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() - { - } - - void bt_peer_connection::on_connected() - { -#ifndef TORRENT_DISABLE_ENCRYPTION - - pe_settings::enc_policy const& out_enc_policy = m_ses.get_pe_settings().out_enc_policy; - - 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() - { - // 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 - (*m_logger) << time_now_string() - << " ==> DHT_PORT [ " << listen_port << " ]\n"; -#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); -#ifdef TORRENT_DEBUG - m_sent_bitfield = true; -#endif -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> HAVE_ALL\n"; -#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); -#ifdef TORRENT_DEBUG - m_sent_bitfield = true; -#endif -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> HAVE_NONE\n"; -#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; - - 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,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)); - } - - 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_local()) p.flags |= peer_info::local_connection; - -#ifndef TORRENT_DISABLE_ENCRYPTION - if (m_encrypted) - { - m_rc4_encrypted ? - p.flags |= peer_info::rc4_encrypted : - p.flags |= 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 = get_socket()->get() - ? peer_info::bittorrent_utp - : 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_local()) - (*m_logger) << " initiating encrypted handshake\n"; -#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 = std::rand() % 512; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " pad size: " << pad_size << "\n"; -#endif - - buffer::interval send_buf = allocate_send_buffer(dh_key_len + pad_size); - if (send_buf.begin == 0) - { - disconnect(errors::no_memory); - return; - } - - std::copy(m_dh_key_exchange->get_local_key(), - m_dh_key_exchange->get_local_key() + dh_key_len, - send_buf.begin); - - std::generate(send_buf.begin + dh_key_len, send_buf.end, std::rand); -#ifdef TORRENT_DEBUG - m_encrypted_bytes += send_buf.left(); - TORRENT_ASSERT(m_encrypted_bytes <= send_buffer_size()); -#endif - setup_send(); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " sent DH key\n"; -#endif - } - - void bt_peer_connection::write_pe3_sync() - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!m_encrypted); - TORRENT_ASSERT(!m_rc4_encrypted); - TORRENT_ASSERT(is_local()); - 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 = rand() % 512; - - TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes); - - // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) - buffer::interval send_buf = - allocate_send_buffer(20 + 20 + 8 + 4 + 2 + pad_size + 2); - if (send_buf.begin == 0) return; // out of memory - - // sync hash (hash('req1',S)) - h.reset(); - h.update("req1",4); - h.update(secret, dh_key_len); - sha1_hash sync_hash = h.final(); - - std::copy(sync_hash.begin(), sync_hash.end(), send_buf.begin); - send_buf.begin += 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; - - std::copy(obfsc_hash.begin(), obfsc_hash.end(), send_buf.begin); - send_buf.begin += 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 - TORRENT_ASSERT(send_buf.left() == 8 + 4 + 2 + pad_size + 2); - int encrypt_size = send_buf.left(); - - int crypto_provide = 0; - pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; - - if (allowed_enc_level == pe_settings::both) - crypto_provide = 0x03; - else if (allowed_enc_level == pe_settings::rc4) - crypto_provide = 0x02; - else if (allowed_enc_level == pe_settings::plaintext) - crypto_provide = 0x01; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " crypto provide : [ "; - if (allowed_enc_level == pe_settings::both) - (*m_logger) << "plaintext rc4 ]\n"; - else if (allowed_enc_level == pe_settings::rc4) - (*m_logger) << "rc4 ]\n"; - else if (allowed_enc_level == pe_settings::plaintext) - (*m_logger) << "plaintext ]\n"; -#endif - - write_pe_vc_cryptofield(send_buf, crypto_provide, pad_size); - m_RC4_handler->encrypt(send_buf.end - encrypt_size, encrypt_size); -#ifdef TORRENT_DEBUG - m_encrypted_bytes = send_buffer_size(); -#endif - - TORRENT_ASSERT(send_buf.begin == send_buf.end); - setup_send(); - } - - void bt_peer_connection::write_pe4_sync(int crypto_select) - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!is_local()); - 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 = rand() % 512; - - TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes); - - const int buf_size = 8 + 4 + 2 + pad_size; - buffer::interval send_buf = allocate_send_buffer(buf_size); - if (send_buf.begin == 0) return; // out of memory - write_pe_vc_cryptofield(send_buf, crypto_select, pad_size); - - m_RC4_handler->encrypt(send_buf.end - buf_size, buf_size); - TORRENT_ASSERT(send_buffer_size() - buf_size == m_encrypted_bytes); -#ifdef TORRENT_DEBUG - m_encrypted_bytes += buf_size; - TORRENT_ASSERT(m_encrypted_bytes <= send_buffer_size()); -#endif - setup_send(); - - // encryption method has been negotiated - if (crypto_select == 0x02) - m_rc4_encrypted = true; - else // 0x01 - m_rc4_encrypted = false; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " crypto select : [ "; - if (crypto_select == 0x01) - (*m_logger) << "plaintext ]\n"; - else - (*m_logger) << "rc4 ]\n"; -#endif - } - - void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf - , 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( (write_buf.left() == 8+4+2+pad_size+2 && is_local()) || - (write_buf.left() == 8+4+2+pad_size && !is_local()) ); - 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 - std::fill(write_buf.begin, write_buf.begin + 8, 0); - write_buf.begin += 8; - - detail::write_uint32(crypto_field, write_buf.begin); - detail::write_uint16(pad_size, write_buf.begin); // len (pad) - - // fill pad with zeroes - std::generate(write_buf.begin, write_buf.begin + pad_size, &std::rand); - write_buf.begin += pad_size; - - // append len(ia) if we are initiating - if (is_local()) - detail::write_uint16(handshake_len, write_buf.begin); // len(IA) - - TORRENT_ASSERT(write_buf.begin == write_buf.end); - } - - 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) - - is_local() ? h.update(keyA, 4) : 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) - - is_local() ? h.update(keyB, 4) : 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_RC4_handler.get()); - m_RC4_handler.reset(new (std::nothrow) RC4_handler(local_key, remote_key)); - if (!m_RC4_handler) - { - disconnect(errors::no_memory); - return; - } - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " computed RC4 keys\n"; -#endif - } - - void bt_peer_connection::append_const_send_buffer(char const* buffer, int size) - { - TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes); - // if we're encrypting this buffer, we need to make a copy - // since we'll mutate it -#ifndef TORRENT_DISABLE_ENCRYPTION - if (m_encrypted && m_rc4_encrypted) - { - send_buffer(buffer, size); - } - else -#endif - { - peer_connection::append_const_send_buffer(buffer, size); - } - } - - void bt_peer_connection::send_buffer(char const* buf, int size, int flags) - { - TORRENT_ASSERT(buf); - TORRENT_ASSERT(size > 0); - -#ifndef TORRENT_DISABLE_ENCRYPTION - encrypt_pending_buffer(); - - if (m_encrypted && m_rc4_encrypted) - { - TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes); - m_RC4_handler->encrypt(const_cast(buf), size); -#ifdef TORRENT_DEBUG - m_encrypted_bytes += size; -#endif - } -#endif - - peer_connection::send_buffer(buf, size, flags); - } - - buffer::interval bt_peer_connection::allocate_send_buffer(int size) - { -#ifndef TORRENT_DISABLE_ENCRYPTION - encrypt_pending_buffer(); - if (m_encrypted && m_rc4_encrypted) - { - TORRENT_ASSERT(m_enc_send_buffer.left() == 0); - m_enc_send_buffer = peer_connection::allocate_send_buffer(size); - return m_enc_send_buffer; - } - else -#endif - { - buffer::interval i = peer_connection::allocate_send_buffer(size); - return i; - } - } - -#ifndef TORRENT_DISABLE_ENCRYPTION - void bt_peer_connection::encrypt_pending_buffer() - { - if (m_encrypted && m_rc4_encrypted && m_enc_send_buffer.left()) - { - TORRENT_ASSERT(m_enc_send_buffer.begin); - TORRENT_ASSERT(m_enc_send_buffer.end); - TORRENT_ASSERT(m_RC4_handler); - TORRENT_ASSERT(send_buffer_size() - m_enc_send_buffer.left() == m_encrypted_bytes); -#ifdef TORRENT_DEBUG - m_encrypted_bytes += m_enc_send_buffer.left(); - TORRENT_ASSERT(m_encrypted_bytes <= send_buffer_size()); -#endif - - m_RC4_handler->encrypt(m_enc_send_buffer.begin, m_enc_send_buffer.left()); - m_enc_send_buffer.end = m_enc_send_buffer.begin; - } - } -#endif - - void bt_peer_connection::setup_send() - { -#ifndef TORRENT_DISABLE_ENCRYPTION - encrypt_pending_buffer(); - TORRENT_ASSERT(!m_encrypted || !m_rc4_encrypted || m_encrypted_bytes == send_buffer_size()); -#endif - peer_connection::setup_send(); - } - - 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::write_handshake() - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!m_sent_handshake); -#ifdef TORRENT_DEBUG - 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; - - buffer::interval i = allocate_send_buffer(1 + string_len + 8 + 20 + 20); - if (i.begin == 0) return; // out of memory - // length of version string - *i.begin = string_len; - ++i.begin; - - // version string itself - std::copy( - version_string - , version_string + string_len - , i.begin); - i.begin += string_len; - - // 8 zeroes - std::fill(i.begin, i.begin + 8, 0); - -#ifndef TORRENT_DISABLE_DHT - // indicate that we support the DHT messages - *(i.begin + 7) |= 0x01; -#endif - -#ifndef TORRENT_DISABLE_EXTENSIONS - // we support extensions - *(i.begin + 5) |= 0x10; -#endif - - // we support merkle torrents - *(i.begin + 5) |= 0x08; - - // we support FAST extension - *(i.begin + 7) |= 0x04; - -#ifdef TORRENT_VERBOSE_LOGGING - for (int k = 0; k < 8; ++k) - { - for (int j = 0; j < 8; ++j) - { - if (i.begin[k] & (0x80 >> j)) (*m_logger) << "1"; - else (*m_logger) << "0"; - } - } -#endif - i.begin += 8; - - // info hash - sha1_hash const& ih = t->torrent_file().info_hash(); - std::copy(ih.begin(), ih.end(), i.begin); - i.begin += 20; - - // peer id - std::copy( - m_ses.get_peer_id().begin() - , m_ses.get_peer_id().end() - , i.begin); - i.begin += 20; - TORRENT_ASSERT(i.begin == i.end); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " ==> HANDSHAKE\n"; -#endif - setup_send(); - } - - 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 - (*m_logger) << time_now_string() << " <== KEEPALIVE\n"; -#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 = 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; - } - - 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 (!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; - if (recv_pos <= 9) - { - // only received protocol data - m_statistics.received_bytes(0, received); - } - else if (recv_pos - received >= 9) - { - // only received payload data - m_statistics.received_bytes(received, 0); - piece_bytes = received; - } - else - { - // received a bit of both - TORRENT_ASSERT(recv_pos - received < 9); - TORRENT_ASSERT(recv_pos > 9); - TORRENT_ASSERT(9 - (recv_pos - received) <= 9); - m_statistics.received_bytes( - recv_pos - 9 - , 9 - (recv_pos - received)); - piece_bytes = recv_pos - 9; - } - - const int header_size = merkle?13:9; - - if (recv_pos < header_size) return; - - const char* ptr = recv_buffer.begin + 1; - peer_request p; - p.piece = detail::read_int32(ptr); - p.start = detail::read_int32(ptr); - - int list_size = 0; - - if (merkle) - { - list_size = detail::read_int32(ptr); - p.length = packet_size() - list_size - header_size; - } - else - { - p.length = packet_size() - header_size; - } - -#ifdef TORRENT_VERBOSE_LOGGING -// (*m_logger) << time_now_string() << " <== PIECE_FRAGMENT p: " << p.piece -// << " start: " << p.start << " length: " << p.length << "\n"; -#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 - (*m_logger) << time_now_string() << " <== HASHPIECE " << p.piece << " list: " << list_size << " "; -#endif - lazy_entry hash_list; - error_code ec; - if (lazy_bdecode(recv_buffer.begin + 13, recv_buffer.end + 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; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " " << e->list_int_value_at(0) << ": " - << sha1_hash(e->list_at(1)->string_ptr()); -#endif - nodes.insert(std::make_pair(int(e->list_int_value_at(0)) - , sha1_hash(e->list_at(1)->string_ptr()))); - } -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "\n"; -#endif - 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 --------- - // ----------------------------- - - 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 || defined TORRENT_LOGGING - error_code ec; - static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"}; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:" - << (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type") - << " from:" << remote().address().to_string(ec) - << " to: unknown address type ]\n"; -#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 || defined TORRENT_LOGGING - error_code ec; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:rendezvous" - << " to:" << ep.address().to_string(ec) << " ]\n"; -#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 || defined TORRENT_LOGGING - error_code ec; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect" - << " to:" << ep.address().to_string(ec) << " error:failed to add peer ]\n"; -#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 || defined TORRENT_LOGGING - error_code ec; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect" - << " to:" << ep.address().to_string(ec) << " error:peer banned ]\n"; -#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 || defined TORRENT_LOGGING - error_code ec; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect" - << " to:" << ep.address().to_string(ec) << " ]\n"; -#endif - } break; - case hp_failed: - { - boost::uint32_t error = detail::read_uint32(ptr); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - error_code ec; - char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"}; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:failed" - " error:" << error << - " msg:" << ((error >= 0 && error < 4)?err_msg[error]:"unknown message id") << - " ]\n"; -#endif - // #error deal with holepunch errors - } break; -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - default: - { - error_code ec; - (*m_logger) << time_now_string() << " <== HOLEPUNCH [" - " msg:unknown message type (" << msg_type << ")" - << " to:" << ep.address().to_string(ec) << " ]\n"; - } -#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 || defined TORRENT_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"}; - (*m_logger) << time_now_string() << " ==> HOLEPUNCH [ msg:" - << (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type") - << " to:" << ep.address().to_string(ec) - << " error:" << hp_error_string[error] - << " ]\n"; -#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); - } - - // ----------------------------- - // --------- EXTENDED ---------- - // ----------------------------- - - 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(); - return; - } - - if (extended_id == upload_only_msg) - { - if (!packet_finished()) return; - bool ul = detail::read_uint8(recv_buffer.begin); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " <== UPLOAD_ONLY [ " << (ul?"true":"false") << " ]\n"; -#endif - set_upload_only(ul); - return; - } - - if (extended_id == holepunch_msg) - { - if (!packet_finished()) return; - on_holepunch(); - return; - } - -#ifdef TORRENT_VERBOSE_LOGGING - if (packet_finished()) - (*m_logger) << time_now_string() << " <== EXTENSION MESSAGE [" - " msg:" << extended_id << - " size:" << packet_size() << " ]\n"; -#endif - -#ifndef TORRENT_DISABLE_EXTENSIONS - 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; - } -#endif - - if (extended_id == upload_only_msg) - { - if (!packet_finished()) return; - set_upload_only(detail::read_uint8(recv_buffer.begin)); - return; - } - - if (extended_id == share_mode_msg) - { - if (!packet_finished()) return; - set_share_mode(detail::read_uint8(recv_buffer.begin)); - 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; - lazy_bdecode(recv_buffer.begin + 2, recv_buffer.end, root, ec, &pos); - if (root.type() != lazy_entry::dict_t) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " invalid extended handshake: " << ec.message() - << "pos: " << pos << "\n"; -#endif - return; - } - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " <== EXTENDED HANDSHAKE: \n" << root; -#endif - -#ifndef TORRENT_DISABLE_EXTENSIONS - 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; -#endif - - // upload_only - if (lazy_entry const* m = root.dict_find_dict("m")) - { - m_upload_only_id = m->dict_find_int_value("upload_only", 0); - m_holepunch_id = m->dict_find_int_value("ut_holepunch", 0); - } - - // there is supposed to be a remote listen port - int listen_port = 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); - if (is_disconnecting()) return; - } - // there should be a version too - // but where do we put that info? - - int last_seen_complete = 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 = root.dict_find_int_value("reqq"); - if (reqq > 0) m_max_out_request_queue = reqq; - - if (root.dict_find_int_value("upload_only", 0)) - set_upload_only(true); - - if (root.dict_find_int_value("share_mode", 0)) - set_share_mode(true); - - std::string myip = root.dict_find_string_value("yourip"); - if (!myip.empty()) - { - // TODO: don't trust this blindly - if (myip.size() == address_v4::bytes_type::static_size) - { - address_v4::bytes_type bytes; - std::copy(myip.begin(), myip.end(), bytes.begin()); - m_ses.set_external_address(address_v4(bytes)); - } -#if TORRENT_USE_IPV6 - else if (myip.size() == address_v6::bytes_type::static_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()); - else - m_ses.set_external_address(ipv6_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); - } - - 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 (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; - - char msg[7] = {0, 0, 0, 3, msg_extended}; - char* ptr = msg + 5; - detail::write_uint8(m_upload_only_id, ptr); - detail::write_uint8(t->is_upload_only(), 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 -#ifdef TORRENT_DEBUG - m_sent_bitfield = true; -#endif - - // bootstrap superseeding by sending one have message - superseed_piece(t->get_piece_to_super_seed( - get_bitfield())); - return; - } - else if (m_supports_fast && t->is_seed()) - { - 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 - (*m_logger) << time_now_string() << " *** NOT SENDING BITFIELD\n"; -#endif -#ifdef TORRENT_DEBUG - 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 (rand() % (num_pieces - i) >= num_lazy_pieces - lazy_piece) continue; - lazy_pieces[lazy_piece++] = i; - } - TORRENT_ASSERT(lazy_piece == num_lazy_pieces); - lazy_piece = 0; - } - - const int packet_size = (num_pieces + 7) / 8 + 5; - - buffer::interval i = allocate_send_buffer(packet_size); - if (i.begin == 0) return; // out of memory - - detail::write_int32(packet_size - 4, i.begin); - detail::write_uint8(msg_bitfield, i.begin); - - if (t->is_seed()) - { - memset(i.begin, 0xff, packet_size - 6); - - // Clear trailing bits - unsigned char *p = ((unsigned char *)i.begin) + packet_size - 6; - *p = (0xff << ((8 - (num_pieces & 7)) & 7)) & 0xff; - } - else - { - memset(i.begin, 0, packet_size - 5); - piece_picker const& p = t->picker(); - int mask = 0x80; - unsigned char* byte = (unsigned char*)i.begin; - for (int i = 0; i < num_pieces; ++i) - { - if (p.have_piece(i)) *byte |= mask; - mask >>= 1; - if (mask == 0) - { - mask = 0x80; - ++byte; - } - } - } - for (int c = 0; c < num_lazy_pieces; ++c) - i.begin[lazy_pieces[c] / 8] &= ~(0x80 >> (lazy_pieces[c] & 7)); - TORRENT_ASSERT(i.end - i.begin == (num_pieces + 7) / 8); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " ==> BITFIELD "; - - std::string bitfield_string; - bitfield_string.resize(num_pieces + 1); - for (int k = 0; k < num_pieces; ++k) - { - if (i.begin[k / 8] & (0x80 >> (k % 8))) bitfield_string[k] = '1'; - else bitfield_string[k] = '0'; - } - bitfield_string[num_pieces] = '\n'; - (*m_logger) << bitfield_string; -#endif -#ifdef TORRENT_DEBUG - m_sent_bitfield = true; -#endif - - setup_send(); - - if (num_lazy_pieces > 0) - { - for (int i = 0; i < num_lazy_pieces; ++i) - { - write_have(lazy_pieces[i]); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> HAVE [ piece: " << lazy_pieces[i] << "]\n"; -#endif - } - // 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; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " ==> EXTENSIONS\n"; -#endif - TORRENT_ASSERT(m_supports_extensions); - TORRENT_ASSERT(m_sent_handshake); - - entry handshake; - entry::dictionary_type& m = handshake["m"].dict(); - - // 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) - { - if (is_local()) handshake["p"] = m_ses.listen_port(); - handshake["v"] = m_ses.settings().user_agent; - } - - 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; - m["share_mode"] = share_mode_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 (t->is_upload_only() && !t->share_mode() && (!m_ses.settings().lazy_bitfields -#ifndef TORRENT_DISABLE_ENCRYPTION - || m_encrypted -#endif - )) - handshake["upload_only"] = 1; - - if (t->share_mode()) - handshake["share_mode"] = 1; - - if (!m_ses.m_settings.anonymous_mode) - { - tcp::endpoint ep = m_ses.get_ipv6_interface(); - if (!is_any(ep.address())) - { - std::string ipv6_address; - std::back_insert_iterator out(ipv6_address); - detail::write_address(ep.address(), out); - handshake["ipv6"] = ipv6_address; - } - } - - // 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 = i->second.integer(); - TORRENT_ASSERT(ext.find(val) == ext.end()); - ext.insert(val); - } -#endif - - std::vector msg; - bencode(std::back_inserter(msg), handshake); - - // make room for message - buffer::interval i = allocate_send_buffer(6 + msg.size()); - if (i.begin == 0) return; // out of memory - - // write the length of the message - detail::write_int32((int)msg.size() + 2, i.begin); - detail::write_uint8(msg_extended, i.begin); - // signal handshake message - detail::write_uint8(0, i.begin); - - std::copy(msg.begin(), msg.end(), i.begin); - i.begin += msg.size(); - TORRENT_ASSERT(i.begin == i.end); - -#if defined TORRENT_VERBOSE_LOGGING && TORRENT_USE_IOSTREAM - std::stringstream handshake_str; - handshake.print(handshake_str); - (*m_logger) << time_now_string() << " ==> EXTENDED HANDSHAKE: \n" << handshake_str.str(); -#endif - - setup_send(); - } -#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)); - } - - 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 (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); - } - - 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_RC4_handler->decrypt(wr_buf.first.begin, wr_buf.first.left()); - if (wr_buf.second.left()) m_RC4_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_local()) write_pe1_2_dhkey(); - if (is_disconnecting()) return; - - // read dh key, generate shared secret - if (m_dh_key_exchange->compute_secret(recv_buffer.begin) == -1) - { - disconnect(errors::no_memory); - return; - } - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " received DH key\n"; -#endif - - TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes); - - // 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_local()) - { - 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_local()); - 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, 2); - 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, 2); - 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 - (*m_logger) << time_now_string << " sync point (hash) found at offset " - << m_sync_bytes_read + bytes_processed - 20 << "\n"; -#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_local()); - 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()); - 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 - (*m_logger) << time_now_string() << " stream key found, torrent located.\n"; -#endif - break; - } - } - - if (!m_RC4_handler.get()) - { - disconnect(errors::invalid_info_hash, 2); - return; - } - - // verify constant - buffer::interval wr_recv_buf = wr_recv_buffer(); - m_RC4_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 - (*m_logger) << time_now_string() << " verification constant found\n"; -#endif - m_state = read_pe_cryptofield; - reset_recv_buffer(4 + 2); - } - - // cannot fall through into - if (m_state == read_pe_syncvc) - { - TORRENT_ASSERT(is_local()); - 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_RC4_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 - (*m_logger) << time_now_string() << " sync point (verification constant) found at offset " - << m_sync_bytes_read + bytes_processed - 8 << "\n"; -#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_RC4_handler->decrypt(wr_buf.begin, packet_size()); - - recv_buffer = receive_buffer(); - - int crypto_field = detail::read_int32(recv_buffer.begin); - -#ifdef TORRENT_VERBOSE_LOGGING - if (!is_local()) - (*m_logger) << time_now_string() << " crypto provide : [ "; - else - (*m_logger) << time_now_string() << " crypto select : [ "; - - if (crypto_field & 0x01) - (*m_logger) << "plaintext "; - if (crypto_field & 0x02) - (*m_logger) << "rc4 "; - (*m_logger) << "]\n"; -#endif - - if (!is_local()) - { - int crypto_select = 0; - // select a crypto method - switch (m_ses.get_pe_settings().allowed_enc_level) - { - case pe_settings::plaintext: - if (!(crypto_field & 0x01)) - { - disconnect(errors::no_plaintext_mode, 1); - return; - } - crypto_select = 0x01; - break; - case pe_settings::rc4: - if (!(crypto_field & 0x02)) - { - disconnect(errors::no_rc4_mode, 1); - return; - } - crypto_select = 0x02; - break; - case pe_settings::both: - if (m_ses.get_pe_settings().prefer_rc4) - { - if (crypto_field & 0x02) - crypto_select = 0x02; - else if (crypto_field & 0x01) - crypto_select = 0x01; - } - else - { - if (crypto_field & 0x01) - crypto_select = 0x01; - else if (crypto_field & 0x02) - crypto_select = 0x02; - } - if (!crypto_select) - { - disconnect(errors::unsupported_encryption_mode, 1); - return; - } - break; - } // switch - - // write the pe4 step - write_pe4_sync(crypto_select); - } - else // is_local() - { - // check if crypto select is valid - pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; - - if (crypto_field == 0x02) - { - if (allowed_enc_level == pe_settings::plaintext) - { - disconnect(errors::unsupported_encryption_mode_selected, 2); - return; - } - m_rc4_encrypted = true; -#ifdef TORRENT_DEBUG - m_encrypted_bytes = send_buffer_size(); -#endif - } - else if (crypto_field == 0x01) - { - if (allowed_enc_level == pe_settings::rc4) - { - disconnect(errors::unsupported_encryption_mode_selected, 2); - return; - } - m_rc4_encrypted = false; - } - else - { - disconnect(errors::unsupported_encryption_mode_selected, 2); - return; - } - } - - 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_local()) - 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_local() ? packet_size() : packet_size() - 2; - - buffer::interval wr_buf = wr_recv_buffer(); - m_RC4_handler->decrypt(wr_buf.begin, packet_size()); - - recv_buffer = receive_buffer(); - - if (!is_local()) - { - 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 - (*m_logger) << time_now_string() << " len(IA) : " << len_ia << "\n"; -#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_local() - { - // 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_local()); - TORRENT_ASSERT(!m_encrypted); - - if (!packet_finished()) return; - - // ia is always rc4, so decrypt it - buffer::interval wr_buf = wr_recv_buffer(); - m_RC4_handler->decrypt(wr_buf.begin, packet_size()); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " decrypted ia : " << packet_size() << " bytes\n"; -#endif - - if (!m_rc4_encrypted) - { - m_RC4_handler.reset(); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " destroyed rc4 keys\n"; -#endif - } - - // everything that arrives after this is Encrypt2 - 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_RC4_handler->decrypt(wr_buf.begin, wr_buf.left()); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " decrypted remaining " << wr_buf.left() << " bytes\n"; -#endif - } - else // !m_rc4_encrypted - { - m_RC4_handler.reset(); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " destroyed rc4 keys\n"; -#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_local() && - 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[] = "BitTorrent protocol"; - - if (packet_size != 19 || - !std::equal(recv_buffer.begin + 1, recv_buffer.begin + 19, protocol_string)) - { -#ifndef TORRENT_DISABLE_ENCRYPTION - if (!is_local() && 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 - if (!m_encrypted && !is_local()) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " attempting encrypted connection\n"; -#endif - m_state = read_pe_dhkey; - cut_receive_buffer(0, dh_key_len); - TORRENT_ASSERT(!packet_finished()); - return; - } - - TORRENT_ASSERT((!is_local() && m_encrypted) || is_local()); -#endif // #ifndef TORRENT_DISABLE_ENCRYPTION - disconnect(errors::invalid_info_hash, 2); - return; - } - -#ifndef TORRENT_DISABLE_ENCRYPTION - TORRENT_ASSERT(m_state != read_pe_dhkey); - - if (!is_local() && - (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && - !m_encrypted) - { - disconnect(errors::no_incoming_regular); - return; - } -#endif - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " BitTorrent protocol\n"; -#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 - for (int i=0; i < 8; ++i) - { - for (int j=0; j < 8; ++j) - { - if (recv_buffer[i] & (0x80 >> j)) (*m_logger) << "1"; - else (*m_logger) << "0"; - } - } - (*m_logger) << "\n"; - if (recv_buffer[7] & 0x01) - (*m_logger) << " supports DHT port message\n"; - if (recv_buffer[7] & 0x04) - (*m_logger) << " supports FAST extensions\n"; - if (recv_buffer[5] & 0x10) - (*m_logger) << " supports extensions protocol\n"; -#endif - -#ifndef 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()); - - attach_to_torrent(info_hash); - 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 - (*m_logger) << time_now_string() << " received invalid info_hash\n"; -#endif - disconnect(errors::invalid_info_hash, 2); - return; - } - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " info_hash received\n"; -#endif - } - - t = associated_torrent().lock(); - TORRENT_ASSERT(t); - - // if this is a local connection, we have already - // sent the handshake - if (!is_local()) write_handshake(); -// if (t->valid_metadata()) -// write_bitfield(); - - 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) - { - 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); - char ascii_pid[21]; - 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] = '.'; - } - char msg[200]; - snprintf(msg, 200, "received peer_id: %s client: %s\nas ascii: %s\n" - , hex_pid, identify_client(peer_id(recv_buffer.begin)).c_str(), ascii_pid); - (*m_logger) << msg; - } -#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_ses.get_peer_id() && is_local()) - { - (*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_ses.get_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 (m_max_out_request_queue > 50) m_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 - (*m_logger) << time_now_string() << " <== HANDSHAKE\n"; -#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_local() && !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 == 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()); - -#if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_ENCRYPTION - if (m_encrypted_bytes > 0) - { - if (m_rc4_encrypted) - { - m_encrypted_bytes -= bytes_transferred; - TORRENT_ASSERT(m_encrypted_bytes >= 0); - } - else - { - m_encrypted_bytes -= (std::min)(int(bytes_transferred), m_encrypted_bytes); - } - TORRENT_ASSERT(m_encrypted_bytes >= 0); - } -#endif - - 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(); - } - } - -#ifdef TORRENT_DEBUG - void bt_peer_connection::check_invariant() const - { - boost::shared_ptr t = associated_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()); - } - -#ifndef TORRENT_DISABLE_ENCRYPTION - TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_dh_key_exchange.get()) - || !is_local()); - - TORRENT_ASSERT(!m_rc4_encrypted || m_RC4_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/libtorrent_utp/src/connection_queue.cpp b/libtorrent_utp/src/connection_queue.cpp deleted file mode 100644 index f970a8e00..000000000 --- a/libtorrent_utp/src/connection_queue.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - -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. - -*/ - -#include -#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_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 < 2); - - entry* e = 0; - - switch (priority) - { - case 0: - m_queue.push_back(entry()); - e = &m_queue.back(); - break; - case 1: - m_queue.push_front(entry()); - e = &m_queue.front(); - break; - } - - 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_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)); - } - - void 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; - } - 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)); - } - - void connection_queue::close() - { - error_code ec; - mutex_t::scoped_lock l(m_mutex); - m_timer.cancel(ec); - m_abort = true; - - while (!m_queue.empty()) - { - // we don't want to call the timeout callback while we're locked - // since that is a recipie for dead-locks - entry e = m_queue.front(); - m_queue.pop_front(); - if (e.connecting) --m_num_connecting; - l.unlock(); -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - e.on_timeout(); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - l.lock(); - } - } - - 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; } - -#ifdef TORRENT_DEBUG - - 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; - } - - 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)); - } - 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; - 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(); -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - ent.on_connect(ent.ticket); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - 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); - - INVARIANT_CHECK; -#ifdef TORRENT_DEBUG - function_guard guard_(m_in_timeout_function); -#endif - - TORRENT_ASSERT(!e || e == error::operation_aborted); - if (e) 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->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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - i->on_timeout(); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } - - 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)); - } - try_connect(l); - } - - void connection_queue::on_try_connect() - { - mutex_t::scoped_lock l(m_mutex); - try_connect(l); - } -} - diff --git a/libtorrent_utp/src/create_torrent.cpp b/libtorrent_utp/src/create_torrent.cpp deleted file mode 100644 index 93b816f92..000000000 --- a/libtorrent_utp/src/create_torrent.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/create_torrent.hpp" -#include "libtorrent/file_pool.hpp" -#include "libtorrent/storage.hpp" -#include "libtorrent/escape_string.hpp" - -#include -#include - -#include -#include - -#define MAX_SYMLINK_PATH 200 - -namespace libtorrent -{ - // defined in torrent_info.cpp - int merkle_num_leafs(int); - int merkle_num_nodes(int); - int merkle_get_parent(int); - int merkle_get_sibling(int); - - namespace detail - { - int TORRENT_EXPORT 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 & 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 TORRENT_EXPORT 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 - } - - } - - create_torrent::create_torrent(file_storage& fs, int piece_size, int pad_file_limit, int flags) - : 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(m_files.at(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 = 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); - 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_private) info["private"] = 1; - - if (!m_multifile) - { - if (m_include_mtime) info["mtime"] = m_files.mtime(m_files.at(0)); - info["length"] = m_files.at(0).size; - if (m_files.at(0).pad_file - || m_files.at(0).hidden_attribute - || m_files.at(0).executable_attribute - || m_files.at(0).symlink_attribute) - { - std::string& attr = info["attr"].string(); - if (m_files.at(0).pad_file) attr += 'p'; - if (m_files.at(0).hidden_attribute) attr += 'h'; - if (m_files.at(0).executable_attribute) attr += 'x'; - if (m_include_symlinks && m_files.at(0).symlink_attribute) attr += 'l'; - } - if (m_include_symlinks - && m_files.at(0).symlink_attribute - && m_files.at(0).symlink_index != -1) - { - entry& sympath_e = info["symlink path"]; - - std::string split = split_path(m_files.symlink(m_files.at(0))); - 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 (file_storage::iterator i = m_files.begin(); - i != m_files.end(); ++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"] = i->size; - 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)); - - if (i->pad_file - || i->hidden_attribute - || i->executable_attribute - || i->symlink_attribute) - { - std::string& attr = file_e["attr"].string(); - if (i->pad_file) attr += 'p'; - if (i->hidden_attribute) attr += 'h'; - if (i->executable_attribute) attr += 'x'; - if (m_include_symlinks && i->symlink_attribute) attr += 'l'; - } - if (m_include_symlinks - && i->symlink_attribute - && i->symlink_index != -1) - { - 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)); - } - int file_index = i - m_files.begin(); - if (!m_filehashes.empty() && m_filehashes[file_index] != sha1_hash()) - { - file_e["sha1"] = m_filehashes[file_index].to_string(); - } - } - } - } - - info["piece length"] = m_files.piece_length(); - if (m_merkle_torrent) - { - std::vector merkle_tree; - - 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; - merkle_tree.resize(num_nodes); - int num_pieces = m_piece_hash.size(); - for (int i = 0; i < num_pieces; ++i) - merkle_tree[first_leaf + i] = m_piece_hash[i]; - sha1_hash filler(0); - for (int i = num_pieces; i < num_leafs; ++i) - 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*)&merkle_tree[i][0], 20); - h.update((char const*)&merkle_tree[i+1][0], 20); - 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*)&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_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) - { - m_comment = str; - } - - void create_torrent::set_creator(char const* str) - { - m_created_by = str; - } - -} - diff --git a/libtorrent_utp/src/disk_buffer_holder.cpp b/libtorrent_utp/src/disk_buffer_holder.cpp deleted file mode 100644 index 4effe02d5..000000000 --- a/libtorrent_utp/src/disk_buffer_holder.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - -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. - -*/ - -#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), m_num_blocks(1) - { - 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), m_num_blocks(1) - { - TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); - } - - disk_buffer_holder::disk_buffer_holder(disk_buffer_pool& iothread, char* buf, int num_blocks) - : m_disk_pool(iothread), m_buf(buf), m_num_blocks(num_blocks) - { - TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf)); - } - - void disk_buffer_holder::reset(char* buf, int num_blocks) - { - if (m_buf) - { - if (m_num_blocks == 1) m_disk_pool.free_buffer(m_buf); - else m_disk_pool.free_buffers(m_buf, m_num_blocks); - } - m_buf = buf; - m_num_blocks = num_blocks; - } - - char* disk_buffer_holder::release() - { - char* ret = m_buf; - m_buf = 0; - return ret; - } - - disk_buffer_holder::~disk_buffer_holder() - { - if (m_buf) - { - if (m_num_blocks == 1) m_disk_pool.free_buffer(m_buf); - else m_disk_pool.free_buffers(m_buf, m_num_blocks); - } - } -} - diff --git a/libtorrent_utp/src/disk_io_thread.cpp b/libtorrent_utp/src/disk_io_thread.cpp deleted file mode 100644 index 2e80ad046..000000000 --- a/libtorrent_utp/src/disk_io_thread.cpp +++ /dev/null @@ -1,2421 +0,0 @@ -/* - -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. - -*/ - -/* - 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_buffer_pool::disk_buffer_pool(int block_size) - : m_block_size(block_size) - , m_in_use(0) -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - , 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 -#ifdef TORRENT_DEBUG - m_magic = 0x1337; -#endif - } - -#ifdef TORRENT_DEBUG - disk_buffer_pool::~disk_buffer_pool() - { - TORRENT_ASSERT(m_magic == 0x1337); - m_magic = 0; - } -#endif - -#if defined TORRENT_DEBUG || 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 - return m_pool.is_from(buffer); -#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); -#ifdef TORRENT_DISABLE_POOL_ALLOCATOR - char* ret = page_aligned_allocator::malloc(m_block_size); -#else - char* ret = (char*)m_pool.ordered_malloc(); - m_pool.set_next_size(m_settings.cache_buffer_chunk_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 - m_pool.ordered_free(buf); -#endif - --m_in_use; - } - - char* disk_buffer_pool::allocate_buffers(int num_blocks, char const* category) - { - mutex::scoped_lock l(m_pool_mutex); - TORRENT_ASSERT(m_magic == 0x1337); -#ifdef TORRENT_DISABLE_POOL_ALLOCATOR - char* ret = page_aligned_allocator::malloc(m_block_size * num_blocks); -#else - char* ret = (char*)m_pool.ordered_malloc(num_blocks); - m_pool.set_next_size(m_settings.cache_buffer_chunk_size); -#endif - m_in_use += num_blocks; -#if TORRENT_USE_MLOCK - if (m_settings.lock_disk_cache) - { -#ifdef TORRENT_WINDOWS - VirtualLock(ret, m_block_size * num_blocks); -#else - mlock(ret, m_block_size * num_blocks); -#endif - } -#endif -#if defined TORRENT_DISK_STATS || defined TORRENT_STATS - m_allocations += num_blocks; -#endif -#ifdef TORRENT_DISK_STATS - m_categories[category] += num_blocks; - 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; - } - - void disk_buffer_pool::free_buffers(char* buf, int num_blocks) - { - TORRENT_ASSERT(buf); - TORRENT_ASSERT(num_blocks >= 1); - mutex::scoped_lock l(m_pool_mutex); - TORRENT_ASSERT(m_magic == 0x1337); - TORRENT_ASSERT(is_disk_buffer(buf, l)); -#if defined TORRENT_DISK_STATS || defined TORRENT_STATS - m_allocations -= num_blocks; -#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] -= num_blocks; - 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 * num_blocks); -#else - munlock(buf, m_block_size * num_blocks); -#endif - } -#endif -#ifdef TORRENT_DISABLE_POOL_ALLOCATOR - page_aligned_allocator::free(buf); -#else - m_pool.ordered_free(buf, num_blocks); -#endif - m_in_use -= num_blocks; - } - - void disk_buffer_pool::release_memory() - { - TORRENT_ASSERT(m_magic == 0x1337); -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - mutex::scoped_lock l(m_pool_mutex); - m_pool.release_memory(); -#endif - } - -// ------- 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_physical_ram(0) - , m_ios(ios) - , m_queue_callback(queue_callback) - , m_work(io_service::work(m_ios)) - , m_file_pool(fp) - , m_disk_io_thread(boost::bind(&disk_io_thread::thread_fun, this)) - { -#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 - int mib[2] = { CTL_HW, HW_MEMSIZE }; - 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 - } - - disk_io_thread::~disk_io_thread() - { - TORRENT_ASSERT(m_abort == true); - } - - void disk_io_thread::abort() - { - mutex::scoped_lock l(m_queue_mutex); - disk_io_job j; - m_waiting_to_shutdown = true; - j.action = disk_io_job::abort_thread; - m_jobs.insert(m_jobs.begin(), j); - m_signal.signal(l); - } - - void disk_io_thread::join() - { - m_disk_io_thread.join(); - mutex::scoped_lock l(m_queue_mutex); - TORRENT_ASSERT(m_abort == true); - m_jobs.clear(); - } - - void disk_io_thread::get_cache_info(sha1_hash const& ih, std::vector& ret) const - { - 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.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.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.average_queue_time = m_queue_time.mean(); - ret.average_read_time = m_read_time.mean(); - ret.job_queue_length = m_jobs.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::list::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->callback, *i, -3); - 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); - widx.erase(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, int 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) - { - ++i; - if (i == idx.end()) return 0; - } - - // don't replace an entry that is is too young - 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) - { - // 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; - 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; - } - - // flushes 'blocks' blocks from the cache - int disk_io_thread::flush_cache_blocks(mutex::scoped_lock& l - , int blocks, int 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 (options & dont_flush_write_blocks) 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() - , boost::bind(&contiguous_blocks, _1) - < boost::bind(&contiguous_blocks, _2)); - 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; - } - } - 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); - 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) - { - p.storage->write_impl(iov, p.piece, (std::min)( - i * m_block_size, piece_size) - buffer_size, iov_counter); - iov_counter = 0; - } - else - { - TORRENT_ASSERT(buf); - file::iovec_t b = { buf.get(), buffer_size }; - p.storage->write_impl(&b, p.piece, (std::min)( - i * m_block_size, piece_size) - buffer_size, 1); - } - 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 (!buf) - { - iov[iov_counter].iov_base = p.blocks[i].buf; - iov[iov_counter].iov_len = block_size; - ++iov_counter; - } - else - { - 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; - } - - 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; - buffers.push_back(p.blocks[i].buf); - post_callback(p.blocks[i].callback, j, result); - p.blocks[i].callback.clear(); - p.blocks[i].buf = 0; - ++ret; - } - if (!buffers.empty()) free_multiple_buffers(&buffers[0], buffers.size()); - - 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.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(), 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 - p.storage->get_storage_impl()->set_error("" - , 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(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 - p.storage->get_storage_impl()->set_error("" - , 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, j.piece, 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.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; - } - -#ifdef TORRENT_DEBUG - 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.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) - { -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - 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) - { -#ifndef TORRENT_DISABLE_POOL_ALLOCATOR - 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.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); - - cache_piece_index_t::iterator p; - bool hit; - int ret = cache_piece(j, p, hit, ignore_cache_size, l); - if (ret < 0) return ret; - - int piece_size = j.storage->info()->piece_size(j.piece); - int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; - - 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 ? 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; - - 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 ? 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; - // 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 piece_size = j.storage->info()->piece_size(j.piece); - int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; - 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, p.piece, 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) - { - TORRENT_ASSERT(j.buffer); - TORRENT_ASSERT(j.cache_min_time >= 0); - - mutex::scoped_lock l(m_piece_mutex); - if (!m_settings.use_read_cache) 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 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 - { - mutex::scoped_lock l(m_queue_mutex); - return m_queue_buffer_size; - } - - void disk_io_thread::add_job(disk_io_job const& j - , mutex::scoped_lock& l - , boost::function const& f) - { - m_jobs.push_back(j); - m_jobs.back().callback.swap(const_cast&>(f)); - m_jobs.back().start_time = time_now_hires(); - - if (j.action == disk_io_job::write) - m_queue_buffer_size += j.buffer_size; - m_signal.signal(l); - } - - void disk_io_thread::add_job(disk_io_job const& j - , boost::function const& f) - { - 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); - add_job(j, l, f); - } - - bool disk_io_thread::test_error(disk_io_job& j) - { - 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(); -#ifdef TORRENT_DEBUG - printf("ERROR: '%s' in %s\n", ec.message().c_str(), j.error_file.c_str()); -#endif - j.storage->clear_error(); - return true; - } - return false; - } - - void disk_io_thread::post_callback( - boost::function const& handler - , disk_io_job const& j, int ret) - { - if (!handler) return; - - m_ios.post(boost::bind(handler, ret, j)); - } - - 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 - , read_operation + 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 // finalize_file - }; - - bool should_cancel_on_abort(disk_io_job const& j) - { - TORRENT_ASSERT(j.action >= 0 && j.action < 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 < 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 < sizeof(action_flags)); - return action_flags[j.action] & buffer_operation; - } - - void disk_io_thread::thread_fun() - { - // 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; - - for (;;) - { -#ifdef TORRENT_DISK_STATS - m_log << log_time() << " idle" << std::endl; -#endif - mutex::scoped_lock jl(m_queue_mutex); - - 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); - } - - 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(); - return; - } - - disk_io_job j; - - if (!m_jobs.empty()) - { - // 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; - } - - 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; - } - } - - 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 - 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)); - continue; - } - } - 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(); - - 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); - } - - // 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); - - bool post = false; - if (m_queue_buffer_size + j.buffer_size >= m_settings.max_queued_disk_bytes - && m_queue_buffer_size < m_settings.max_queued_disk_bytes - && m_queue_callback - && m_settings.max_queued_disk_bytes > 0) - { - // 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 - post = true; - } - - if (post) m_ios.post(m_queue_callback); - - 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); - -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - - if (j.storage && j.storage->get_storage_impl()->m_settings == 0) - j.storage->get_storage_impl()->m_settings = &m_settings; - - ptime now = time_now_hires(); - m_queue_time.add_sample(total_microseconds(now - j.start_time)); - - 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 >= 0); - 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; - 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()); -#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::list::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->callback, *i, -3); - 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.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::list::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->callback, *i, -3); - 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.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; - } - case disk_io_job::finalize_file: - { -#ifdef TORRENT_DISK_STATS - m_log << log_time() << " finalize_file " << j.piece << std::endl; -#endif - j.storage->finalize_file(j.piece); - break; - } - case disk_io_job::read: - { - if (test_error(j)) - { - ret = -1; - break; - } -#ifdef TORRENT_DISK_STATS - m_log << log_time(); -#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) - { -#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, 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) - { - // this means the file wasn't big enough for this read - j.buffer = 0; - j.error = errors::file_too_short; - j.error_file.clear(); - 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 - j.start_time)); - } - 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); - if (p != idx.end()) - { - 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; - } - 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; - 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 - flush_contiguous_blocks(const_cast(*p) - , l, m_settings.write_cache_line_size); - if (p->num_blocks == 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(); - file::iovec_t iov = {j.buffer, j.buffer_size}; - ret = j.storage->write_impl(&iov, j.piece, j.offset, 1); - l.lock(); - if (ret < 0) - { - test_error(j); - break; - } - // 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); - int 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; - } - sha1_hash h = j.storage->hash_for_piece_impl(j.piece); - if (test_error(j)) - { - ret = -1; - j.storage->mark_failed(j.piece); - break; - } - - ret = (j.storage->info()->hash_for_piece(j.piece) == h)?0:-2; - if (ret == -2) j.storage->mark_failed(j.piece); - 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); - if (ret != 0) - { - 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; - - 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; - for (cache_piece_index_t::iterator i = start; i != end; ++i) - { - torrent_info const& ti = *i->storage->info(); - int blocks_in_piece = (ti.piece_size(i->piece) + m_block_size - 1) / m_block_size; - 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; - } - } - 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); - 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 - - if (m_waiting_to_shutdown) break; - - ret = j.storage->check_files(j.piece, j.offset, j.error); - -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - TORRENT_ASSERT(j.callback); - if (j.callback && ret == piece_manager::need_full_check) - post_callback(j.callback, j, ret); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - 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; - } - } - } -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - ret = -1; - try - { - j.str = e.what(); - } - catch (std::exception&) {} - } -#endif - - TORRENT_ASSERT(!j.storage || !j.storage->error()); - -// if (!j.callback) std::cerr << "DISK THREAD: no callback specified" << std::endl; -// else std::cerr << "DISK THREAD: invoking callback" << std::endl; -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - 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.callback, j, ret); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) - { - TORRENT_ASSERT(false); - } -#endif - } - TORRENT_ASSERT(false); - } -} - diff --git a/libtorrent_utp/src/entry.cpp b/libtorrent_utp/src/entry.cpp deleted file mode 100644 index 20f7f3888..000000000 --- a/libtorrent_utp/src/entry.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#include -#if (defined TORRENT_VERBOSE_LOGGING || defined TORRENT_DEBUG) && TORRENT_USE_IOSTREAM -#include -#include -#endif -#include -#include "libtorrent/entry.hpp" -#include "libtorrent/config.hpp" -#include "libtorrent/escape_string.hpp" -#include "libtorrent/lazy_entry.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 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; - } - } - } - - 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) - { - // not implemented - TORRENT_ASSERT(false); - } - -#if (defined TORRENT_VERBOSE_LOGGING || defined TORRENT_DEBUG) && TORRENT_USE_IOSTREAM - void entry::print(std::ostream& os, int indent) const - { - TORRENT_ASSERT(indent >= 0); - for (int i = 0; i < indent; ++i) os << " "; - switch (m_type) - { - case int_t: - os << integer() << "\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) os << to_hex(string()) << "\n"; - else os << string() << "\n"; - } break; - case list_t: - { - os << "list\n"; - for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) - { - i->print(os, indent+1); - } - } break; - case dictionary_t: - { - os << "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) os << " "; - os << "["; - if (binary_string) os << to_hex(i->first); - else os << i->first; - os << "]"; - - if (i->second.type() != entry::string_t - && i->second.type() != entry::int_t) - os << "\n"; - else os << " "; - i->second.print(os, indent+2); - } - } break; - default: - os << "\n"; - } - } -#endif -} - diff --git a/libtorrent_utp/src/enum_net.cpp b/libtorrent_utp/src/enum_net.cpp deleted file mode 100644 index d786af0b0..000000000 --- a/libtorrent_utp/src/enum_net.cpp +++ /dev/null @@ -1,953 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/config.hpp" -#include -#include -#include // for wcstombscstombs -#include "libtorrent/enum_net.hpp" -#include "libtorrent/broadcast_socket.hpp" -#include "libtorrent/error_code.hpp" -#if BOOST_VERSION < 103500 -#include -#else -#include -#endif - -#if defined TORRENT_BSD || defined TORRENT_SOLARIS -#include -#include -#include -#include -#include -#include -#include -#endif - -#if defined TORRENT_BSD -#include -#endif - -#if defined TORRENT_WINDOWS || defined TORRENT_MINGW -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#endif - -#if defined TORRENT_LINUX -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 defined TORRENT_WINDOWS || TORRENT_MINGW || defined TORRENT_LINUX - return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); -#else - return sin->sa_len; -#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 defined TORRENT_LINUX - - 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 defined TORRENT_BSD - - 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 - - -#ifdef TORRENT_BSD - bool verify_sockaddr(sockaddr_in* sin) - { - return (sin->sin_len == sizeof(sockaddr_in) - && sin->sin_family == AF_INET) -#if TORRENT_USE_IPV6 - || (sin->sin_len == sizeof(sockaddr_in6) - && sin->sin_family == AF_INET6) -#endif - ; - } -#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 < b1.size(); ++i) - b1[i] &= m[i]; - return memcmp(&b1[0], &b2[0], b1.size()); - } -#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 defined TORRENT_WINDOWS || defined TORRENT_MINGW - 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; -// covers linux, MacOS X and BSD distributions -#if defined TORRENT_LINUX || defined TORRENT_BSD || defined TORRENT_SOLARIS - int s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) - { - ec = error_code(errno, asio::error::system_category); - return ret; - } - ifconf ifc; - char buf[1024]; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = 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) - { - ifreq const& item = *reinterpret_cast(ifr); - - 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)); - strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE); - if (ioctl(s, SIOCGIFMTU, &req) < 0) - { - ec = error_code(errno, asio::error::system_category); - close(s); - return ret; - } - iface.mtu = req.ifr_mtu; - - memset(&req, 0, sizeof(req)); - strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE); - 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); - } - -#if defined TORRENT_BSD - int current_size = item.ifr_addr.sa_len + IFNAMSIZ; -#elif defined TORRENT_LINUX || defined TORRENT_SOLARIS - int current_size = sizeof(ifreq); -#endif - ifr += current_size; - remaining -= current_size; - } - close(s); - -#elif defined TORRENT_WINDOWS || defined TORRENT_MINGW - - // 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); - } - - 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 -#warning THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK - // 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 defined TORRENT_BSD -/* - 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; - if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) - { - 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(); - } - - if (sysctl(mib, 6, buf.get(), &needed, 0, 0) < 0) - { - 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 defined TORRENT_WINDOWS || defined TORRENT_MINGW -/* - 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 *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; - } - - // 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 defined TORRENT_LINUX - - 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/libtorrent_utp/src/error_code.cpp b/libtorrent_utp/src/error_code.cpp deleted file mode 100644 index 7d2404a1a..000000000 --- a/libtorrent_utp/src/error_code.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - -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. - -*/ - -#include - -#include "libtorrent/config.hpp" -#include "libtorrent/error_code.hpp" - -namespace libtorrent -{ -#if BOOST_VERSION >= 103500 - - const char* libtorrent_error_category::name() const - { - return "libtorrent error"; - } - - std::string libtorrent_error_category::message(int ev) const - { - 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)", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", -// 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", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - - -// 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", - }; - if (ev < 0 || ev >= sizeof(msgs)/sizeof(msgs[0])) - return "Unknown error"; - return msgs[ev]; - } - -#endif - -} - diff --git a/libtorrent_utp/src/escape_string.cpp b/libtorrent_utp/src/escape_string.cpp deleted file mode 100644 index 8559d80b3..000000000 --- a/libtorrent_utp/src/escape_string.cpp +++ /dev/null @@ -1,685 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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" - -#ifdef TORRENT_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#endif - -#include "libtorrent/utf8.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; - do { - *--p = '0' + un % 10; - un /= 10; - } while (un); - if (n < 0) *--p = '-'; - std::memmove(&ret.front(), p, sizeof(ret.elems)); - return ret; - } - - 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 bool(std::strchr(ws, c)); - } - - // 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[rand() % (sizeof(printable)-1)]; - } - - 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; - } - - 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 < 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:%d%s", protocol.c_str(), auth.c_str() - , auth.empty()?"":"@", host.c_str(), port - , 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_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_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'; - } - - 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_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_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 = '.'; - int 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); - int result = std::wctomb(c, *i); - if (result > 0) - { - i += result; - ret.append(c, result); - } - else - { - ++i; - ret += "."; - } - } - return ret; - } -#endif - -#ifdef TORRENT_WINDOWS - 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; - } - -#elif 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]; -#ifdef TORRENT_LINUX -// linux seems to have a weird iconv signature -#define ICONV_IN_CAST (char**) -#else -#define ICONV_IN_CAST -#endif - size_t retval = iconv(h, ICONV_IN_CAST &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) - { - // 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) - { - // 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); - } -#endif - -} - diff --git a/libtorrent_utp/src/file.cpp b/libtorrent_utp/src/file.cpp deleted file mode 100644 index 49890e61f..000000000 --- a/libtorrent_utp/src/file.cpp +++ /dev/null @@ -1,1688 +0,0 @@ -/* - -Copyright (c) 2003, 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 -*/ - -#include "libtorrent/pch.hpp" -#include "libtorrent/config.hpp" -#include "libtorrent/alloca.hpp" -#include "libtorrent/allocator.hpp" // page_size -#include "libtorrent/escape_string.hpp" // for string conversion - -#include -#include - -#ifdef TORRENT_WINDOWS -// windows part - -#include "libtorrent/utf8.hpp" - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include // for _getcwd, _mkdir -#include -#include -#else -// posix part - -#define _FILE_OFFSET_BITS 64 -#include -#include // for F_LOG2PHYS -#include -#include -#include -#include -#include - -#ifdef TORRENT_LINUX -// linux specifics - -#include -#ifdef HAVE_LINUX_FIEMAP_H -#include // FIEMAP_* -#include // FS_IOC_FIEMAP -#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 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 - -#ifdef TORRENT_WINDOWS -#if defined UNICODE && !TORRENT_USE_WSTRING -#warning wide character support not available. Files will be saved using narrow string names -#endif -#endif // TORRENT_WINDOWS - -namespace libtorrent -{ - void stat_file(std::string const& inf, file_status* s - , error_code& ec, int flags) - { - ec.clear(); - -#if TORRENT_USE_WSTRING && defined TORRENT_WINDOWS - std::wstring f = convert_to_wstring(inf); -#else - std::string f = convert_to_native(inf); -#endif - -#ifdef TORRENT_WINDOWS - struct _stati64 ret; -#if TORRENT_USE_WSTRING - if (_wstati64(f.c_str(), &ret) < 0) -#else - if (_stati64(f.c_str(), &ret) < 0) -#endif - { - ec.assign(errno, boost::system::get_generic_category()); - return; - } -#else - 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, boost::system::get_generic_category()); - return; - } -#endif // TORRENT_WINDOWS - - s->file_size = ret.st_size; - s->atime = ret.st_atime; - s->mtime = ret.st_mtime; - s->ctime = ret.st_ctime; - s->mode = ret.st_mode; - } - - 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, boost::system::get_generic_category()); - return; - } - } - - void create_directories(std::string const& f, error_code& ec) - { - 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(), boost::system::get_system_category()); -#else - int ret = mkdir(n.c_str(), 0777); - if (ret < 0 && errno != EEXIST) - ec.assign(errno, boost::system::get_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 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(), boost::system::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, boost::system::get_generic_category()); - copyfile_state_free(state); -#else - int infd = ::open(inf.c_str(), O_RDONLY); - if (infd < 0) - { - ec.assign(errno, boost::system::get_generic_category()); - return; - } - - // rely on default umask to filter x and w permissions - // for group and others - // TODO: copy the mode from the source file - 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, boost::system::get_generic_category()); - return; - } - char buffer[4096]; - for (;;) - { - int num_read = read(infd, buffer, sizeof(buffer)); - if (num_read == 0) break; - int num_written = write(outfd, buffer, num_read); - if (num_written < num_read) - { - ec.assign(errno, boost::system::get_generic_category()); - break; - } - if (num_read < 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' -#ifdef TORRENT_WINDOWS - && *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) - { - char const* ext = strrchr(f.c_str(), '.'); - if (ext == 0) return ""; - return ext; - } - - void replace_extension(std::string& f, std::string const& ext) - { - char const* e = strrchr(f.c_str(), '.'); - if (e == 0) f += '.'; - else f.resize(e - f.c_str() + 1); - f += ext; - } - - bool is_root_path(std::string const& f) - { - if (f.empty()) return false; - -#ifdef TORRENT_WINDOWS - 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; -#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; - -#ifdef TORRENT_WINDOWS - if (f == "\\\\") return false; -#else - // as well as parent_path("/") should be "/". - if (f == "/") return false; -#endif - - 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, '/'); -#ifdef TORRENT_WINDOWS - char const* altsep = strrchr(first, '\\'); - if (sep == 0 || altsep > sep) sep = altsep; -#endif - if (sep == 0) return f; - - if (sep - first == f.size() - 1) - { - // if the last character is a / (or \) - // ignore it - int len = 0; - while (sep > first) - { - --sep; - if (*sep == '/' -#ifdef TORRENT_WINDOWS - || *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()) return rhs; - if (rhs.empty()) return lhs; - -#ifdef TORRENT_WINDOWS -#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() - { -#ifdef TORRENT_WINDOWS -#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 && TORRENT_USE_WSTRING - return convert_from_wstring(cwd); -#else - return convert_from_native(cwd); -#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(), boost::system::get_system_category()); - return; - } -#else // TORRENT_WINDOWS - std::string f = convert_to_native(inf); - if (::remove(f.c_str()) < 0) - { - ec.assign(errno, boost::system::get_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; - return combine_path(current_working_directory(), f); - } - - bool is_complete(std::string const& f) - { - if (f.empty()) return false; -#ifdef TORRENT_WINDOWS - 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 - // the path passed to FindFirstFile() must be - // a pattern - std::string f = path; - if (!f.empty() && (f[f.size()-1] != '/' || 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 - // 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, boost::system::get_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 - } - - 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()); - } -#else - dirent* dummy; - if (readdir_r(m_handle, &m_dirent, &dummy) != 0) - { - ec.assign(errno, boost::system::get_generic_category()); - m_done = true; - } - if (dummy == 0) m_done = true; -#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) - { - 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 share_mode; - DWORD create_mode; - DWORD flags; - }; - - const static open_mode_t mode_array[] = - { - // read_only - {GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS}, - // write_only - {GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS}, - // read_write - {GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS}, - // invalid option - {0,0,0,0}, - // read_only no_buffer - {GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING }, - // write_only no_buffer - {GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING }, - // read_write no_buffer - {GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING }, - // invalid option - {0,0,0,0} - }; - - const static DWORD attrib_array[] = - { - FILE_ATTRIBUTE_NORMAL, // no attrib - FILE_ATTRIBUTE_HIDDEN, // hidden - FILE_ATTRIBUTE_NORMAL, // executable - FILE_ATTRIBUTE_HIDDEN, // hidden + executable - }; - -#if TORRENT_USE_WSTRING -#define CreateFile_ CreateFileW - m_path = convert_to_wstring(path); -#else -#define CreateFile_ CreateFileA - m_path = convert_to_native(path); -#endif - - TORRENT_ASSERT((mode & mode_mask) < sizeof(mode_array)/sizeof(mode_array[0])); - open_mode_t const& m = mode_array[mode & mode_mask]; - DWORD a = attrib_array[(mode & attribute_mask) >> 12]; - - m_file_handle = CreateFile_(m_path.c_str(), m.rw_mode, m.share_mode, 0 - , m.create_mode, m.flags | (a ? a : FILE_ATTRIBUTE_NORMAL), 0); - - if (m_file_handle == INVALID_HANDLE_VALUE) - { - ec.assign(GetLastError(), get_system_category()); - 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; - ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 - , 0, 0, &temp, 0); - } -#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; - - static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT}; -#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 & (rw_mask | no_buffer), permissions); - } - -#endif - if (m_fd == -1) - { - ec.assign(errno, get_posix_category()); - TORRENT_ASSERT(ec); - return false; - } - -#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 - // 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 - } - - 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; - 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 - - size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) - { - 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) 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 (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); - 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; - 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); - - 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) - { - ec.assign(GetLastError(), get_system_category()); - CloseHandle(ol.hEvent); - return -1; - } - if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0) - { - ec.assign(GetLastError(), get_system_category()); - CloseHandle(ol.hEvent); - return -1; - } - } - 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 - -#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, num_bufs); - if ((size & (size_alignment()-1)) == 0) aligned = true; - } - if (aligned) -#endif // TORRENT_LINUX - { - ret = ::readv(m_fd, bufs, num_bufs); - if (ret < 0) - { - ec.assign(errno, get_posix_category()); - return -1; - } - return ret; - } -#ifdef TORRENT_LINUX - file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); - memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs); - iovec_t& last = temp_bufs[num_bufs-1]; - last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size; - ret = ::readv(m_fd, temp_bufs, num_bufs); - if (ret < 0) - { - ec.assign(errno, get_posix_category()); - return -1; - } - return (std::min)(ret, size_type(size)); -#endif // TORRENT_LINUX - -#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) - { - 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); - 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; - 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); - - ret += size; - // if file_size is > 0, the file will be opened in unbuffered - // mode after the write completes, and truncate the file to - // file_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. Write the whole page and then open a new - // file without FILE_FLAG_NO_BUFFERING and set the - // file size to file_offset + size - - file_size = file_offset + size; - size = num_pages * m_page_size; - } - - if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0) - { - if (GetLastError() != ERROR_IO_PENDING) - { - TORRENT_ASSERT(GetLastError() != ERROR_BAD_ARGUMENTS); - TORRENT_ASSERT(GetLastError() != ERROR_BAD_ARGUMENTS); - ec.assign(GetLastError(), get_system_category()); - CloseHandle(ol.hEvent); - return -1; - } - DWORD tmp; - if (GetOverlappedResult(m_file_handle, &ol, &tmp, true) == 0) - { - ec.assign(GetLastError(), get_system_category()); - CloseHandle(ol.hEvent); - return -1; - } - if (tmp < ret) ret = tmp; - } - CloseHandle(ol.hEvent); - - if (file_size > 0) - { - HANDLE f = CreateFile_(m_path.c_str(), GENERIC_WRITE - , FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0); - - if (f == INVALID_HANDLE_VALUE) - { - ec.assign(GetLastError(), get_system_category()); - return -1; - } - - LARGE_INTEGER offs; - offs.QuadPart = file_size; - if (SetFilePointerEx(f, offs, &offs, FILE_BEGIN) == FALSE) - { - CloseHandle(f); - ec.assign(GetLastError(), get_system_category()); - return -1; - } - if (::SetEndOfFile(f) == FALSE) - { - ec.assign(GetLastError(), get_system_category()); - CloseHandle(f); - return -1; - } - CloseHandle(f); - } - - 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 - -#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, num_bufs); - if ((size & (size_alignment()-1)) == 0) aligned = true; - } - if (aligned) -#endif - { - ret = ::writev(m_fd, bufs, num_bufs); - if (ret < 0) - { - ec.assign(errno, get_posix_category()); - return -1; - } - return ret; - } -#ifdef TORRENT_LINUX - file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); - memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs); - iovec_t& last = temp_bufs[num_bufs-1]; - last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment(); - ret = ::writev(m_fd, temp_bufs, num_bufs); - if (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; - } - return (std::min)(ret, size_type(size)); -#endif // TORRENT_LINUX - -#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_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; - } - - bool file::set_size(size_type s, error_code& ec) - { - TORRENT_ASSERT(is_open()); - TORRENT_ASSERT(s >= 0); - -#ifdef TORRENT_WINDOWS - 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 _WIN32_WINNT >= 0x501 - if ((m_open_mode & sparse) == 0) - { - // 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; - 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 - SetFileValidData(m_file_handle, offs.QuadPart); - } - } -#endif // _WIN32_WINNT >= 0x501 -#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) - { - ec.assign(errno, get_posix_category()); - return false; - } -#endif // F_PREALLOCATE - - int ret; -#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) - { - 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; - } - - void file::finalize() - { -#ifdef TORRENT_WINDOWS - // 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; - ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, &b, sizeof(b) - , 0, 0, &temp, 0); -#endif - } - - 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; - 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)) - { - int err = GetLastError(); - 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; -#else - return start; -#endif - } - -} - diff --git a/libtorrent_utp/src/file_pool.cpp b/libtorrent_utp/src/file_pool.cpp deleted file mode 100644 index 1cf2ad589..000000000 --- a/libtorrent_utp/src/file_pool.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - -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. - -*/ - -#include -#include -#include "libtorrent/pch.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/file_pool.hpp" -#include "libtorrent/error_code.hpp" -#include "libtorrent/file_storage.hpp" // for file_entry - -namespace libtorrent -{ - boost::intrusive_ptr file_pool::open_file(void* st, std::string const& p - , file_entry const& fe, 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, fs.file_index(fe))); - 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)) - { - // close the file before we open it with - // the new read/write privilages - TORRENT_ASSERT(e.file_ptr->refcount() == 1); - e.file_ptr->close(); - std::string full_path = combine_path(p, fs.file_path(fe)); - if (!e.file_ptr->open(full_path, m, ec)) - { - m_files.erase(i); - return boost::intrusive_ptr(); - } -#ifdef TORRENT_WINDOWS -// file prio is supported on vista and up -#if _WIN32_WINNT >= 0x0600 - if (m_low_prio_io) - { - FILE_IO_PRIORITY_HINT_INFO priorityHint; - priorityHint.PriorityHint = IoPriorityHintLow; - SetFileInformationByHandle(e.file_ptr->native_handle(), - FileIoPriorityHintInfo, &priorityHint, sizeof(PriorityHint)); - } -#endif -#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 = combine_path(p, fs.file_path(fe)); - if (!e.file_ptr->open(full_path, m, ec)) - return boost::intrusive_ptr(); - e.mode = m; - e.key = st; - m_files.insert(std::make_pair(std::make_pair(st, fs.file_index(fe)), 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; - 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()) 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/libtorrent_utp/src/file_storage.cpp b/libtorrent_utp/src/file_storage.cpp deleted file mode 100644 index 2520f1d30..000000000 --- a/libtorrent_utp/src/file_storage.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/* - -Copyright (c) 2003-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. - -*/ - -#include "libtorrent/pch.hpp" - -#include "libtorrent/file_storage.hpp" -#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) - {} - - void file_storage::reserve(int num_files) - { - m_files.reserve(num_files); - } - - int file_storage::piece_size(int index) const - { - TORRENT_ASSERT(index >= 0 && index < num_pieces()); - if (index == num_pieces()-1) - { - int size = int(total_size() - - size_type(num_pieces() - 1) * piece_length()); - TORRENT_ASSERT(size > 0); - TORRENT_ASSERT(size <= piece_length()); - return int(size); - } - else - return piece_length(); - } - - void file_storage::update_path_index(file_entry& e) - { - std::string parent = parent_path(e.filename()); - if (parent.empty()) - { - e.path_index = -1; - } - else - { - // 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() { if (name_len == 0) free((void*)name); } - - file_entry::file_entry(file_entry const& fe) - : name(0) - , offset(fe.offset) - , symlink_index(fe.symlink_index) - , 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) - , path_index(fe.path_index) - { - set_name(fe.name, fe.name_len); - } - - file_entry& file_entry::operator=(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; - set_name(fe.name, fe.name_len); - return *this; - } - - void file_entry::set_name(char const* n, int borrow_chars) - { - TORRENT_ASSERT(borrow_chars >= 0); - if (borrow_chars > 1023) borrow_chars = 1023; - if (name_len == 0) free((void*)name); - if (n == 0) - { - TORRENT_ASSERT(borrow_chars == 0); - name = 0; - } - else - { - name = borrow_chars ? n : strdup(n); - } - name_len = borrow_chars; - } - - std::string file_entry::filename() const - { - if (name_len) return std::string(name, name_len); - return name ? name : ""; - } - -#if TORRENT_USE_WSTRING - void file_storage::set_name(std::wstring const& n) - { - std::string utf8; - wchar_utf8(n, utf8); - m_name = utf8; - } - - void file_storage::rename_file(int index, std::wstring const& new_filename) - { - TORRENT_ASSERT(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); - } -#endif // TORRENT_USE_WSTRING - - void file_storage::rename_file(int index, std::string const& new_filename) - { - TORRENT_ASSERT(index >= 0 && index < int(m_files.size())); - m_files[index].set_name(new_filename.c_str()); - update_path_index(m_files[index]); - } - - namespace - { - bool compare_file_offset(file_entry const& lhs, file_entry const& rhs) - { - return lhs.offset < rhs.offset; - } - } - - file_storage::iterator file_storage::file_at_offset(size_type offset) const - { - // find the file iterator and file offset - 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(), end(), target, compare_file_offset); - - TORRENT_ASSERT(file_iter != begin()); - --file_iter; - return file_iter; - } - - std::vector file_storage::map_block(int piece, size_type offset - , int size) const - { - TORRENT_ASSERT(num_files() > 0); - std::vector ret; - - if (m_files.empty()) return ret; - - // find the file iterator and file offset - file_entry target; - target.offset = piece * (size_type)m_piece_length + offset; - TORRENT_ASSERT(target.offset + size <= m_total_size); - TORRENT_ASSERT(!compare_file_offset(target, m_files.front())); - - std::vector::const_iterator file_iter = std::upper_bound( - begin(), end(), target, compare_file_offset); - - TORRENT_ASSERT(file_iter != 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 != end()); - if (file_offset < file_iter->size) - { - file_slice f; - f.file_index = file_iter - begin(); - f.offset = file_offset + file_base(*file_iter); - f.size = (std::min)(file_iter->size - file_offset, (size_type)size); - size -= f.size; - file_offset += f.size; - ret.push_back(f); - } - - TORRENT_ASSERT(size >= 0); - } - return ret; - } - - std::string file_storage::file_path(file_entry const& fe) const - { - TORRENT_ASSERT(fe.path_index >= -1 && fe.path_index < int(m_paths.size())); - if (fe.path_index == -1) return fe.filename(); - return combine_path(m_paths[fe.path_index], fe.filename()); - } - - peer_request file_storage::map_file(int file_index, size_type file_offset - , int size) const - { - TORRENT_ASSERT(file_index < num_files()); - TORRENT_ASSERT(file_index >= 0); - size_type offset = file_offset + at(file_index).offset; - - peer_request ret; - ret.piece = int(offset / piece_length()); - ret.start = int(offset - ret.piece * piece_length()); - ret.length = size; - 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(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(m_files.empty()); - m_name = file; - } - else - { - if (m_files.empty()) - m_name = split_path(file).c_str(); - } - TORRENT_ASSERT(m_name == split_path(file).c_str()); - m_files.push_back(file_entry()); - 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; - e.symlink_attribute = (flags & attribute_symlink) != 0; - if (e.symlink_attribute) - { - e.symlink_index = m_symlinks.size(); - m_symlinks.push_back(symlink_path); - } - 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; - } - - void file_storage::add_file(file_entry const& ent, char const* filehash - , std::string const* symlink, time_t mtime) - { - if (!has_parent_path(ent.filename())) - { - // 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(m_files.empty()); - m_name = ent.filename(); - } - else - { - if (m_files.empty()) - m_name = split_path(ent.filename()).c_str(); - } - m_files.push_back(ent); - file_entry& e = m_files.back(); - e.offset = m_total_size; - m_total_size += ent.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 (symlink) - { - e.symlink_index = m_symlinks.size(); - m_symlinks.push_back(*symlink); - } - 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); - } - - sha1_hash file_storage::hash(file_entry const& fe) const - { - int index = &fe - &m_files[0]; - if (index >= m_file_hashes.size()) return sha1_hash(0); - return sha1_hash(m_file_hashes[index]); - } - - std::string const& file_storage::symlink(file_entry const& fe) const - { - TORRENT_ASSERT(fe.symlink_index < int(m_symlinks.size())); - return m_symlinks[fe.symlink_index]; - } - - time_t file_storage::mtime(file_entry const& fe) const - { - int index = &fe - &m_files[0]; - if (index >= m_mtime.size()) return 0; - return m_mtime[index]; - } - - int file_storage::file_index(file_entry const& fe) const - { - int index = &fe - &m_files[0]; - TORRENT_ASSERT(index >= 0 && index < m_files.size()); - return index; - } - - void file_storage::set_file_base(file_entry const& fe, size_type off) - { - int index = &fe - &m_files[0]; - TORRENT_ASSERT(index >= 0 && index < m_files.size()); - if (m_file_base.size() <= index) m_file_base.resize(index); - m_file_base[index] = off; - } - - size_type file_storage::file_base(file_entry const& fe) const - { - int index = &fe - &m_files[0]; - if (index >= m_file_base.size()) return 0; - return m_file_base[index]; - } - - bool compare_file_entry_size(file_entry const& fe1, file_entry const& fe2) - { return fe1.size < fe2.size; } - - void file_storage::reorder_file(int index, int dst) - { - file_entry e = m_files[index]; - m_files.erase(m_files.begin() + index); - m_files.insert(m_files.begin() + dst, e); - if (!m_mtime.empty()) - { - time_t mtime = 0; - if (m_mtime.size() > index) - { - mtime = m_mtime[index]; - m_mtime.erase(m_mtime.begin() + index); - } - m_mtime.insert(m_mtime.begin() + dst, mtime); - } - if (!m_file_hashes.empty()) - { - char const* fh = 0; - if (m_file_hashes.size() > index) - { - fh = m_file_hashes[index]; - m_file_hashes.erase(m_file_hashes.begin() + index); - } - m_file_hashes.insert(m_file_hashes.begin() + dst, fh); - } - if (!m_file_base.empty()) - { - size_type base = 0; - if (m_file_base.size() > index) - { - base = m_file_base[index]; - m_file_base.erase(m_file_base.begin() + index); - } - m_file_base.insert(m_file_base.begin() + dst, base); - } - } - - void file_storage::optimize(int pad_file_limit) - { - // the main purpuse of padding is to optimize disk - // I/O. This is a conservative memory page size assumption - int alignment = 8*1024; - - // it doesn't make any sense to pad files that - // are smaller than one piece - if (pad_file_limit >= 0 && pad_file_limit < alignment) - pad_file_limit = alignment; - - // put the largest file at the front, to make sure - // it's aligned - std::vector::iterator i = std::max_element(m_files.begin(), m_files.end() - , &compare_file_entry_size); - - int index = file_index(*i); - reorder_file(index, 0); - - size_type off = 0; - int padding_file = 0; - for (std::vector::iterator i = m_files.begin(); - i != m_files.end(); ++i) - { - if (pad_file_limit >= 0 - && (off & (alignment-1)) != 0 - && i->size > 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-1)); - - // find the largest file that fits in pad_size - std::vector::iterator best_match = m_files.end(); - for (std::vector::iterator j = i+1; j < m_files.end(); ++j) - { - if (j->size > 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 - int index = file_index(*best_match); - reorder_file(index, file_index(*i)); - - i->offset = off; - off += i->size; - continue; - } - - // we could not find a file that fits in pad_size - // add a padding file - - file_entry e; - i = m_files.insert(i, e); - i->size = pad_size; - i->offset = off; - char name[30]; - std::sprintf(name, ".____padding_file/%d", padding_file); - i->set_name(name); - i->pad_file = true; - off += pad_size; - ++padding_file; - ++i; - } - i->offset = off; - off += i->size; - } - m_total_size = off; - } -} - diff --git a/libtorrent_utp/src/gzip.cpp b/libtorrent_utp/src/gzip.cpp deleted file mode 100644 index b4ea1bc8c..000000000 --- a/libtorrent_utp/src/gzip.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/assert.hpp" -#include "libtorrent/puff.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 -{ - // 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; - } - - bool inflate_gzip( - char const* in - , int size - , std::vector& buffer - , int maximum_size - , std::string& error) - { - TORRENT_ASSERT(maximum_size > 0); - - int header_len = gzip_header(in, size); - if (header_len < 0) - { - error = "invalid gzip header"; - return true; - } - - // start off with one kilobyte and grow - // if needed - buffer.resize(maximum_size); - - boost::uint32_t destlen = buffer.size(); - boost::uint32_t srclen = size - header_len; - in += header_len; - int ret = puff((unsigned char*)&buffer[0], &destlen, (unsigned char*)in, &srclen); - - if (ret == -1) - { - error = "inflated data too big"; - return true; - } - - buffer.resize(destlen); - - if (ret != 0) - { - error = "error while inflating data"; - return true; - } - return false; - } - -} - diff --git a/libtorrent_utp/src/http_connection.cpp b/libtorrent_utp/src/http_connection.cpp deleted file mode 100644 index 48a70a7da..000000000 --- a/libtorrent_utp/src/http_connection.cpp +++ /dev/null @@ -1,820 +0,0 @@ -/* - -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. - -*/ - -#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" - -#if defined TORRENT_ASIO_DEBUGGING -#include "libtorrent/debug.hpp" -#endif - -#include -#include -#include - -namespace libtorrent { - -enum { max_bottled_buffer = 1024 * 1024 }; - -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 - ) -{ - 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; - - // 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 - , this, ec, (char*)0, 0)); - return; - } - - if (ec) - { - m_resolver.get_io_service().post(boost::bind(&http_connection::callback - , this, ec, (char*)0, 0)); - return; - } - - TORRENT_ASSERT(prio >= 0 && prio < 2); - - bool ssl = false; - if (protocol == "https") ssl = true; - - char request[2048]; - 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.0\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; - } - else - { - APPEND_FMT2("GET %s HTTP/1.0\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"); - } - - if (!auth.empty()) - APPEND_FMT1("Authorization: Basic %s\r\n", base64encode(auth).c_str()); - - if (!user_agent.empty()) - APPEND_FMT1("User-Agent: %s\r\n", user_agent.c_str()); - - if (m_bottled) - APPEND_FMT("Accept-Encoding: gzip\r\n"); - - 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 < 2); - - m_redirects = handle_redirects; - if (ps) m_proxy = *ps; - - m_timeout = timeout; - error_code ec; - m_timer.expires_from_now(m_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(shared_from_this()), _1)); - m_called = false; - m_parser.reset(); - m_recvbuffer.clear(); - m_read_pos = 0; - m_priority = prio; - - // keep ourselves alive even if the callback function - // deletes this object - boost::shared_ptr me(shared_from_this()); - - if (ec) - { - m_resolver.get_io_service().post(boost::bind(&http_connection::callback - , this, 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, shared_from_this(), _1)); - } - else - { - m_ssl = ssl; - m_bind_addr = bind_addr; - error_code ec; - 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_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 - , this, 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) userdata = &m_ssl_ctx; -#endif - bool ret = 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 - , this, 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 - , shared_from_this(), _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 - tcp::resolver::query query(hostname, port); - m_resolver.async_resolve(query, boost::bind(&http_connection::on_resolve - , shared_from_this(), _1, _2)); - } - m_hostname = hostname; - m_port = port; - } -} - -void http_connection::on_connect_timeout() -{ - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - - // keep ourselves alive even if the callback function - // deletes this object - boost::shared_ptr me(shared_from_this()); - - if (!m_endpoints.empty()) - { - error_code ec; - m_sock.close(ec); - } - else - { - callback(asio::error::timed_out); - close(); - } -} - -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_last_receive + c->m_timeout < time_now_hires()) - { - 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; - c->m_sock.close(ec); - c->m_timer.expires_at(c->m_last_receive + c->m_timeout, ec); - c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); - } - else - { - c->callback(asio::error::timed_out); - c->close(); - } - 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(c->m_last_receive + c->m_timeout, ec); - c->m_timer.async_wait(boost::bind(&http_connection::on_timeout, p, _1)); -} - -void http_connection::close() -{ - error_code ec; - m_timer.cancel(ec); - m_resolver.cancel(); - m_limiter_timer.cancel(ec); - m_sock.close(ec); - 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_timeout, m_priority); -} - -void http_connection::connect(int ticket, tcp::endpoint target_address) -{ - 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().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(); - 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 const& e, char const* data, int size) -{ - if (m_bottled && m_called) return; - - std::vector buf; - if (m_bottled && m_parser.header_finished()) - { - std::string const& encoding = m_parser.header("content-encoding"); - if ((encoding == "gzip" || encoding == "x-gzip") && size > 0 && data) - { - std::string error; - if (inflate_gzip(data, size, buf, max_bottled_buffer, error)) - { - if (m_handler) m_handler(errors::http_failed_decompress, m_parser, data, size, *this); - close(); - return; - } - size = int(buf.size()); - data = size == 0 ? 0 : &buf[0]; - } - } - 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) - { - boost::shared_ptr me(shared_from_this()); - callback(e); - close(); - 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); - } - - // 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 const* data = 0; - std::size_t size = 0; - if (m_bottled && m_parser.header_finished()) - { - data = m_parser.get_body().begin; - 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 (code >= 300 && code < 400) - { - // 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; - m_sock.close(ec); - using boost::tuples::ignore; - boost::tie(ignore, ignore, ignore, ignore, ignore) - = parse_url_components(location, ec); - if (!ec) - { - get(location, m_timeout, m_priority, &m_proxy, m_redirects - 1); - } - else - { - // some broken web servers send out relative paths - // in the location header. - std::string url = m_url; - // remove the leaf filename - std::size_t i = url.find_last_of('/'); - if (i != std::string::npos) - url.resize(i); - if ((url.empty() || url[url.size()-1] != '/') - && (location.empty() || location[0] != '/')) - url += '/'; - url += location; - - get(url, m_timeout, m_priority, &m_proxy, m_redirects - 1); - } - 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_parser.get_body().begin, 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 (int(m_recvbuffer.size()) == m_read_pos) - m_recvbuffer.resize((std::min)(m_read_pos + 2048, int(max_bottled_buffer))); - if (m_read_pos == max_bottled_buffer) - { - callback(asio::error::eof); - 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/libtorrent_utp/src/http_parser.cpp b/libtorrent_utp/src/http_parser.cpp deleted file mode 100644 index 87f441635..000000000 --- a/libtorrent_utp/src/http_parser.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include -#include -#include - -#include "libtorrent/config.hpp" -#include "libtorrent/http_parser.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/escape_string.hpp" - -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; - } - - http_parser::http_parser() - : 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_chunked_encoding(false) - , m_finished(false) - {} - - 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); - } - 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; - m_body_start_pos = 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 == "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 <= (int)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_recv_pos - m_body_start_pos + incoming > m_content_length - && m_content_length >= 0) - incoming = 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_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[0] == '\r' && pos[1] == '\n') pos += 2; - else if (pos[0] == '\n') pos += 1; - - 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[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)); - - newline = std::find(pos, buf.end, '\n'); - } - return false; - } - - buffer::const_interval http_parser::get_body() const - { - TORRENT_ASSERT(m_state == read_body); - if (m_content_length >= 0) - return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos - , m_recv_buffer.begin + (std::min)(size_type(m_recv_pos) - , m_body_start_pos + m_content_length)); - else - return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos - , m_recv_buffer.begin + m_recv_pos); - } - - void http_parser::reset() - { - 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(); - } - -} - diff --git a/libtorrent_utp/src/http_seed_connection.cpp b/libtorrent_utp/src/http_seed_connection.cpp deleted file mode 100644 index f5e3eb532..000000000 --- a/libtorrent_utp/src/http_seed_connection.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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 - , std::string const& url - , policy::peer* peerinfo - , std::string const& auth - , web_seed_entry::headers_t const& extra_headers) - : web_connection_base(ses, t, s, remote, url, peerinfo, auth, extra_headers) - , m_url(url) - , 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 - m_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(); - TORRENT_ASSERT(receive_buffer_size <= t->block_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 error = false; - int protocol = 0; - int payload = 0; - boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, error); - m_statistics.received_bytes(0, protocol); - bytes_transferred -= protocol; - if (payload > front_request.length) payload = front_request.length; - - if (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(errors::http_error, 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 >= 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 >= 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 - cut_receive_buffer(header_size, t->block_size() + 1024, 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()[m_chunk_pos + m_body_start] == 'H' - || (m_parser.chunked_encoding() && receive_buffer()[m_chunk_pos + m_body_start] == '\r')); - m_chunk_pos = -1; - } - } - } - - int payload = bytes_transferred; - if (payload > m_response_left) payload = 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(errors::http_error, 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/libtorrent_utp/src/http_stream.cpp b/libtorrent_utp/src/http_stream.cpp deleted file mode 100644 index fa089c713..000000000 --- a/libtorrent_utp/src/http_stream.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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/libtorrent_utp/src/http_tracker_connection.cpp b/libtorrent_utp/src/http_tracker_connection.cpp deleted file mode 100644 index 6e4ce9cd8..000000000 --- a/libtorrent_utp/src/http_tracker_connection.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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" - -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: authentication - 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) - { - m_ios.post(boost::bind(&http_tracker_connection::fail_disp, self() - , 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 += "?"; - - url += "info_hash="; - url += escape_string((const char*)&tracker_req().info_hash[0], 20); - - if (tracker_req().kind == tracker_request::announce_request) - { - char str[1024]; - const bool stats = tracker_req().send_stats; - snprintf(str, sizeof(str), "&peer_id=%s&port=%d&uploaded=%"PRId64 - "&downloaded=%"PRId64"&left=%"PRId64"&corrupt=%"PRId64"&redundant=%"PRId64 - "&compact=1&numwant=%d&key=%x&no_peer_id=1" - , 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 - , stats ? tracker_req().redundant: 0 - , tracker_req().num_want - , tracker_req().key); - url += str; -#ifndef TORRENT_DISABLE_ENCRYPTION - if (m_ses.get_pe_settings().in_enc_policy != pe_settings::disabled) - url += "&supportcrypto=1"; -#endif - if (!tracker_req().trackerid.empty()) - { - std::string id = tracker_req().trackerid; - url += "&trackerid="; - url += escape_string(id.c_str(), id.length()); - } - - if (tracker_req().event != tracker_request::none) - { - const char* event_string[] = {"completed", "started", "stopped", "paused"}; - url += "&event="; - url += event_string[tracker_req().event - 1]; - } - -#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 (!settings.announce_ip.empty()) - { - error_code ec; - if (!ec) url += "&ip=" + escape_string( - settings.announce_ip.c_str(), settings.announce_ip.size()); - } - - if (!tracker_req().ipv6.empty() && !i2p) - { - url += "&ipv6="; - url += tracker_req().ipv6; - } - - if (!tracker_req().ipv4.empty() && !i2p) - { - url += "&ipv4="; - url += tracker_req().ipv4; - } - } - - m_tracker_connection.reset(new http_connection(m_ios, m_cc - , boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4) - , true - , boost::bind(&http_tracker_connection::on_connect, self(), _1) - , boost::bind(&http_tracker_connection::on_filter, self(), _1, _2))); - - int timeout = tracker_req().event==tracker_request::stopped - ?settings.stop_tracker_timeout - :settings.tracker_completion_timeout; - - m_tracker_connection->get(url, seconds(timeout) - , 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: " + url + " ]"); - } -#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) - { - // 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(errors::http_error), 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 = e.dict_find_int_value("interval", 1800); - int min_interval = e.dict_find_int_value("min interval", 60); - - 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.c_str()); - if (scrape_data == 0) - { - fail(error_code(errors::invalid_hash_entry), -1, "" - , interval, min_interval); - return; - } - int complete = scrape_data->dict_find_int_value("complete", -1); - int incomplete = scrape_data->dict_find_int_value("incomplete", -1); - int downloaded = scrape_data->dict_find_int_value("downloaded", -1); - int downloaders = 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() == address_v4::bytes_type::static_size) - external_ip = detail::read_v4_address(p); -#if TORRENT_USE_IPV6 - else if (ip_ent->string_length() == address_v6::bytes_type::static_size) - external_ip = detail::read_v6_address(p); -#endif - } - - int complete = e.dict_find_int_value("complete", -1); - int incomplete = e.dict_find_int_value("incomplete", -1); - - std::list
ip_list; - if (m_tracker_connection) - { - 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, external_ip, trackerid); - } - -} - diff --git a/libtorrent_utp/src/i2p_stream.cpp b/libtorrent_utp/src/i2p_stream.cpp deleted file mode 100644 index 7013355f4..000000000 --- a/libtorrent_utp/src/i2p_stream.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - -Copyright (c) 2009, 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/pch.hpp" - -#include "libtorrent/i2p_stream.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/error_code.hpp" - -#include - -#if TORRENT_USE_I2P - -namespace libtorrent -{ - - i2p_error_category i2p_category; - - const char* i2p_error_category::name() const - { - return "i2p error"; - } - - std::string i2p_error_category::message(int ev) const - { - static char const* messages[] = - { - "no error", - "parse failed", - "cannot reach peer", - "i2p error", - "invalid key", - "invalid id", - "timeout", - "key not found" - }; - - if (ev < 0 || ev > i2p_error::num_errors) return "unknown error"; - return messages[ev]; - } - - i2p_connection::i2p_connection(io_service& ios) - : 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 - && is_open()) return; - - m_sam_router = s; - m_sam_router.type = proxy_settings::i2p_proxy; - - 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()); - - m_sam_socket->async_connect(tcp::endpoint() - , boost::bind(&i2p_connection::on_sam_connect, this, _1, handler)); - } - - void i2p_connection::on_sam_connect(error_code const& ec, i2p_stream::handler_type const& h) - { - m_state = sam_idle; - - do_name_lookup("ME", boost::bind(&i2p_connection::set_local_endpoint, this, _1, _2)); - h(ec); - } - - void i2p_connection::set_local_endpoint(error_code const& ec, char const* dest) - { - if (ec || dest == 0) - { - m_i2p_local_endpoint.clear(); - return; - } - m_i2p_local_endpoint = dest; - } - - void i2p_connection::async_name_lookup(char const* name - , i2p_connection::name_lookup_handler handler) - { - if (m_state == sam_idle && m_name_lookup.empty()) - 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->send_name_lookup(h); - } - - void i2p_connection::on_name_lookup(error_code const& ec - , name_lookup_handler handler) - { - 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()); - } - -// 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) - { - if (!e) return false; -// fprintf(stderr, "i2p error \"%s\"\n", e.message().c_str()); - (*h)(e); - error_code ec; - close(ec); - return true; - } - - void i2p_stream::do_connect(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( - &i2p_stream::connected, this, _1, h)); - } - - void i2p_stream::connected(error_code const& e, boost::shared_ptr h) - { - 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"; - async_write(m_sock, asio::buffer(cmd, sizeof(cmd) - 1) - , boost::bind(&i2p_stream::start_read_line, this, _1, h)); -// fputs(cmd, stderr); - } - - void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr h) - { - if (handle_error(e, h)) return; - - m_buffer.resize(1); - async_read(m_sock, asio::buffer(m_buffer) - , boost::bind(&i2p_stream::read_line, this, _1, h)); - } - - char* string_tokenize(char* last, char sep, char** next) - { - if (last == 0) return 0; - *next = strchr(last, sep); - if (*next == 0) return last; - **next = 0; - ++(*next); - while (**next == sep && **next) ++(*next); - return last; - } - - void i2p_stream::read_line(error_code const& e, boost::shared_ptr h) - { - if (handle_error(e, h)) return; - - int read_pos = m_buffer.size(); - -// fprintf(stderr, "%c", m_buffer[read_pos - 1]); - // look for \n which means end of the response - if (m_buffer[read_pos - 1] != '\n') - { - // 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 - , 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; - } - - ptr = string_tokenize(next, ' ', &next); - if (ptr == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } - ptr = string_tokenize(next, ' ', &next); - if (ptr == 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; - char* ptr = string_tokenize(next, ' ', &next); - if (ptr == 0) { handle_error(invalid_response, h); return; } - - 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 - result = i2p_error::num_errors; // unknown error - } - else if (strcmp("MESSAGE", name) == 0) - { - message = ptr; - } - else if (strcmp("VERSION", name) == 0) - { - version = 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, 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); - 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) - { - 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()); -// fputs(cmd, stderr); - 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) - { - m_state = read_accept_response; - char cmd[400]; - int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id); -// fputs(cmd, stderr); - 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) - { - 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); -// fputs(cmd, stderr); - 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) - { - 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()); -// fputs(cmd, stderr); - async_write(m_sock, asio::buffer(cmd, size) - , boost::bind(&i2p_stream::start_read_line, this, _1, h)); - } -} - -#endif - diff --git a/libtorrent_utp/src/identify_client.cpp b/libtorrent_utp/src/identify_client.cpp deleted file mode 100644 index 59fc09976..000000000 --- a/libtorrent_utp/src/identify_client.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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/escape_string.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"} - , {"BF", "Bitflu"} - , {"BG", "BTG"} - , {"BL", "BitBlinder"} - , {"BP", "BitTorrent Pro"} - , {"BR", "BitRocket"} - , {"BS", "BTSlave"} - , {"BW", "BitWombat"} - , {"BX", "BittorrentX"} - , {"CD", "Enhanced CTorrent"} - , {"CT", "CTorrent"} - , {"DE", "Deluge"} - , {"DP", "Propagate Data Client"} - , {"EB", "EBit"} - , {"ES", "electric sheep"} - , {"FC", "FileCroc"} - , {"FT", "FoxTorrent"} - , {"GS", "GSTorrent"} - , {"HK", "Hekate"} - , {"HL", "Halite"} - , {"HN", "Hydranode"} - , {"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"} - , {"SN", "ShareNet"} - , {"SS", "SwarmScope"} - , {"ST", "SymTorrent"} - , {"SZ", "Shareaza"} - , {"S~", "Shareaza (beta)"} - , {"T", "BitTornado"} - , {"TN", "Torrent.NET"} - , {"TR", "Transmission"} - , {"TS", "TorrentStorm"} - , {"TT", "TuoTu"} - , {"U", "UPnP"} - , {"UL", "uLeecher"} - , {"UM", "uTorrent Mac"} - , {"UT", "uTorrent"} - , {"VG", "Vagaa"} - , {"WT", "BitLet"} - , {"WY", "FireTorrent"} - , {"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, "XBT", "XBT"} - , {0, "OP", "Opera"} - , {2, "RS", "Rufus"} - , {0, "AZ2500BT", "BitTyrant"} - }; - - 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/libtorrent_utp/src/instantiate_connection.cpp b/libtorrent_utp/src/instantiate_connection.cpp deleted file mode 100644 index 8842fdbba..000000000 --- a/libtorrent_utp/src/instantiate_connection.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include "libtorrent/socket.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/socket_type.hpp" -#include "libtorrent/utp_socket_manager.hpp" -#include -#include - -namespace libtorrent -{ - - bool instantiate_connection(io_service& ios - , proxy_settings const& ps, socket_type& s - , void* ssl_context - , utp_socket_manager* sm) - { - if (sm) - { - s.instantiate(ios); - s.get()->set_impl(sm->new_utp_socket(s.get())); - } - else if (ps.type == proxy_settings::none) - { -#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().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().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); - } -#if TORRENT_USE_I2P - else if (ps.type == proxy_settings::i2p_proxy) - { - s.instantiate(ios); - s.get()->set_proxy(ps.hostname, ps.port); - } -#endif - else - { - TORRENT_ASSERT_VAL(false, ps.type); - return false; - } - return true; - } - -} - diff --git a/libtorrent_utp/src/ip_filter.cpp b/libtorrent_utp/src/ip_filter.cpp deleted file mode 100644 index 208e4f882..000000000 --- a/libtorrent_utp/src/ip_filter.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - -Copyright (c) 2005, 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/pch.hpp" - -#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/libtorrent_utp/src/kademlia/dht_tracker.cpp b/libtorrent_utp/src/kademlia/dht_tracker.cpp deleted file mode 100644 index e26791263..000000000 --- a/libtorrent_utp/src/kademlia/dht_tracker.cpp +++ /dev/null @@ -1,665 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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 - - 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(read_v4_endpoint(in)); -#if TORRENT_USE_IPV6 - else if (p.size() == 18) - epl.push_back(read_v6_endpoint(in)); -#endif - } - } - -} - -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())); - } - - bool send_callback(void* userdata, entry const& e, udp::endpoint const& addr, int flags) - { - dht_tracker* self = (dht_tracker*)userdata; - return self->send_packet(e, addr, flags); - } - - // 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, &send_callback, settings, extract_node_id(state), this) - , m_ses(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 - } - - void dht_tracker::start(entry const& bootstrap) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - std::vector initial_nodes; - - if (bootstrap.type() == entry::dictionary_t) - { -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - if (entry const* nodes = bootstrap.find_key("nodes")) - read_endpoint_list(nodes, initial_nodes); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } - - 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(10), 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, boost::bind(&dht_tracker::on_bootstrap, self(), _1)); - } - - void dht_tracker::stop() - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_dht.status(s); - } - - void dht_tracker::network_stats(int& sent, int& received) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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" - ":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; - boost::tie(active, passive) = m_dht.size(); - pc << (m_counter * tick_period) - << "\t" << active - << "\t" << passive; - 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 - , boost::function const&)> f) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_dht.announce(ih, listen_port, f); - } - - - void dht_tracker::on_unreachable(udp::endpoint const& ep) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_dht.unreachable(ep); - } - - // translate bittorrent kademlia message into the generice kademlia message - // used by the library - void dht_tracker::on_receive(udp::endpoint const& ep, char const* buf, int bytes_transferred) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - // account for IP and UDP overhead - m_received_bytes += bytes_transferred + (ep.address().is_v6() ? 48 : 28); - - 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; - } - - if (match) - { - ++match->count; - if (match->count >= 20) - { - if (now < match->limit) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - if (match->count == 20) - { - TORRENT_LOG(dht_tracker) << " BANNING PEER [ ip: " - << ep << " time: " << total_milliseconds((now - match->limit) + seconds(5)) / 1000.f - << " count: " << match->count << " ]"; - } -#endif - // we've received 20 messages in less than 5 seconds from - // this node. Ignore it until it's silent for 5 minutes - match->limit = now + minutes(5); - return; - } - - // we got 50 messages from this peer, but it was in - // more than 5 seconds. Reset the counter and the timer - match->count = 0; - match->limit = now + seconds(5); - } - } - else - { - min->count = 1; - min->limit = now + seconds(5); - min->src = ep.address(); - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ++m_total_message_input; - m_total_in_bytes += bytes_transferred; -#endif - - using libtorrent::entry; - using libtorrent::bdecode; - - TORRENT_ASSERT(bytes_transferred > 0); - - lazy_entry e; - int pos; - error_code ec; - int ret = lazy_bdecode(buf, buf + bytes_transferred, e, ec, &pos, 10, 500); - if (ret != 0) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << "<== " << ep << " ERROR: " - << ec.message() << " pos: " << pos; -#endif - return; - } - - 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 - entry r; - libtorrent::dht::incoming_error(r, "message is not a dictionary"); - send_packet(r, ep, 0); - return; - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - parse_dht_client(e); - TORRENT_LOG(dht_tracker) << "<== " << ep << " " << print_entry(e, true); -#endif - - m_dht.incoming(m); - } - - 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 - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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(udp::endpoint(i->addr, i->port), 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_dht.add_node(node); - } - - void dht_tracker::add_node(std::pair const& node) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - if (e || host == udp::resolver::iterator()) return; - add_node(host->endpoint()); - } - - void dht_tracker::add_router_node(udp::endpoint const& node) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_dht.add_router_node(node); - } - - void dht_tracker::on_bootstrap(std::vector > const&) - {} - - bool dht_tracker::send_packet(libtorrent::entry const& e, udp::endpoint const& addr, int send_flags) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - using libtorrent::bencode; - using libtorrent::entry; - - 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) 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") - { - // TODO: fix this stats logging -// ++m_replies_sent[e["r"]]; -// m_replies_bytes_sent[e["r"]] += 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/libtorrent_utp/src/kademlia/find_data.cpp b/libtorrent_utp/src/kademlia/find_data.cpp deleted file mode 100644 index 31ba7ec71..000000000 --- a/libtorrent_utp/src/kademlia/find_data.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - -Copyright (c) 2006, 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/pch.hpp" - -#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) -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING - std::stringstream log_line; - log_line << "[" << m_algorithm.get() << "] incoming get_peer response [ "; -#endif - - 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()); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " token: " << to_hex(token->string_value()); -#endif - } - - // 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 - log_line << " 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 - log_line << " p: " << n->list_size(); -#endif - } - static_cast(m_algorithm.get())->got_peers(peer_list); - } - - // look for nodes - n = r->dict_find_string("nodes"); - if (n) - { - std::vector node_list; - char const* nodes = n->string_ptr(); - char const* end = nodes + n->string_length(); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " nodes: " << ((end - nodes) / 26); -#endif - while (end - nodes >= 26) - { - node_id id; - std::copy(nodes, nodes + 20, id.begin()); - nodes += 20; - m_algorithm->traverse(id, read_v4_endpoint(nodes)); - } - } - - n = r->dict_find_list("nodes2"); - if (n) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " nodes2: " << n->list_size(); -#endif - for (int i = 0; i < n->list_size(); ++i) - { - lazy_entry const* p = n->list_at(0); - if (p->type() != lazy_entry::string_t) continue; - if (p->string_length() < 6 + 20) continue; - char const* in = p->string_ptr(); - - node_id id; - std::copy(in, in + 20, id.begin()); - in += 20; - if (p->string_length() == 6 + 20) - m_algorithm->traverse(id, read_v4_endpoint(in)); -#if TORRENT_USE_IPV6 - else if (p->string_length() == 18 + 20) - m_algorithm->traverse(id, read_v6_endpoint(in)); -#endif - } - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - log_line << " ]"; - TORRENT_LOG(traversal) << log_line.str(); -#endif - 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 - , data_callback const& dcallback - , nodes_callback const& ncallback) - : traversal_algorithm(node, target) - , m_data_callback(dcallback) - , m_nodes_callback(ncallback) - , m_target(target) - , m_done(false) - , m_got_peers(false) -{ - node.m_table.for_each_node(&add_entry_fun, 0, (traversal_algorithm*)this); -} - -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)); -#ifdef TORRENT_DEBUG - o->m_in_constructor = false; -#endif - return o; -} - -bool find_data::invoke(observer_ptr o) -{ - if (m_done) - { - m_invoke_count = -1; - return false; - } - - entry e; - e["y"] = "q"; - e["q"] = "get_peers"; - entry& a = e["a"]; - a["info_hash"] = m_target.to_string(); - return m_node.m_rpc.invoke(e, o->target_ep(), o); -} - -void find_data::got_peers(std::vector const& peers) -{ - if (!peers.empty()) m_got_peers = true; - m_data_callback(peers); -} - -void find_data::done() -{ - if (m_invoke_count != 0) return; - - m_done = true; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << time_now_string() << "[" << this << "] get_peers 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_no_id) continue; - if ((o->flags & observer::flag_queried) == 0) continue; - std::map::iterator j = m_write_tokens.find(o->id()); - if (j == m_write_tokens.end()) continue; - results.push_back(std::make_pair(node_entry(o->id(), o->target_ep()), j->second)); - --num_results; - } - m_nodes_callback(results, m_got_peers); - - traversal_algorithm::done(); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/kademlia/node.cpp b/libtorrent_utp/src/kademlia/node.cpp deleted file mode 100644 index 07955060f..000000000 --- a/libtorrent_utp/src/kademlia/node.cpp +++ /dev/null @@ -1,939 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include -#include -#include - -#include "libtorrent/io.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/socket.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/refresh.hpp" -#include "libtorrent/kademlia/find_data.hpp" - -namespace libtorrent { namespace dht -{ - -void incoming_error(entry& e, char const* msg); - -using detail::write_endpoint; - -int search_torrent_entry::match(char const* in_tags[], int num_tags) const -{ - int ret = 0; - for (int i = 0; i < num_tags; ++i) - { - char const* t = in_tags[i]; - std::map::const_iterator j = tags.find(t); - if (j == tags.end()) continue; - // weigh the score by how popular this tag is in this torrent - ret += 100 * j->second / total_tag_points; - } - return ret; -} - -bool search_torrent_entry::tick() -{ - int sum = 0; - for (std::map::iterator i = tags.begin() - , end(tags.end()); i != end;) - { - i->second = (i->second * 2) / 3; - sum += i->second; - if (i->second > 0) { ++i; continue; } - tags.erase(i++); - } - total_tag_points = sum; - - sum = 0; - for (std::map::iterator i = name.begin() - , end(name.end()); i != end;) - { - i->second = (i->second * 2) / 3; - sum += i->second; - if (i->second > 0) { ++i; continue; } - name.erase(i++); - } - total_name_points = sum; - - return total_tag_points == 0; -} - -void search_torrent_entry::publish(std::string const& torrent_name, char const* in_tags[] - , int num_tags) -{ - for (int i = 0; i < num_tags; ++i) - { - char const* t = in_tags[i]; - std::map::iterator j = tags.find(t); - if (j != tags.end()) - ++j->second; - else - tags[t] = 1; - ++total_tag_points; - // TODO: limit the number of tags - } - - name[torrent_name] += 1; - ++total_name_points; - - // TODO: limit the number of names -} - -void search_torrent_entry::get_name(std::string& t) const -{ - std::map::const_iterator max = name.begin(); - for (std::map::const_iterator i = name.begin() - , end(name.end()); i != end; ++i) - { - if (i->second > max->second) max = i; - } - t = max->first; -} - -void search_torrent_entry::get_tags(std::string& t) const -{ - for (std::map::const_iterator i = tags.begin() - , end(tags.end()); i != end; ++i) - { - if (i != tags.begin()) t += " "; - t += i->first; - } -} - -#ifdef _MSC_VER -namespace -{ - char rand() { return (char)std::rand(); } -} -#endif - -// TODO: configurable? -enum { announce_interval = 30 }; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(node) -#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() {} - -// TODO: the session_impl argument could be an alert reference -// instead, and make the dht_tracker less dependent on session_impl -// which would make it simpler to unit test -node_impl::node_impl(libtorrent::aux::session_impl& ses - , bool (*f)(void*, entry const&, udp::endpoint const&, int) - , dht_settings const& settings - , node_id nid - , void* userdata) - : m_settings(settings) - , m_id(nid == (node_id::min)() ? generate_id() : nid) - , m_table(m_id, 8, settings) - , m_rpc(m_id, m_table, f, userdata) - , m_last_tracker_tick(time_now()) - , m_ses(ses) - , m_send(f) - , m_userdata(userdata) -{ - m_secret[0] = std::rand(); - 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(), (signed 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(), (signed 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, (signed char*)&token[0]); - return token; -} - -void node_impl::refresh(node_id const& id - , find_data::nodes_callback const& f) -{ - boost::intrusive_ptr r(new dht::refresh(*this, id, f)); - r->start(); -} - -void node_impl::bootstrap(std::vector const& nodes - , find_data::nodes_callback const& f) -{ - boost::intrusive_ptr r(new dht::refresh(*this, m_id, f)); - -#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); - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(node) << "bootstrapping with " << count << " nodes"; -#endif - r->start(); -} -/* -void node_impl::refresh() -{ - boost::intrusive_ptr r(new dht::refresh(*this, m_id, boost::bind(&nop))); - 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] = std::rand(); -} - -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) - { - entry e; - incoming_error(e, "missing 'y' entry"); - m_send(m_userdata, e, m.addr, 0); - return; - } - - char y = *(y_ent->string_ptr()); - - switch (y) - { - case 'r': - { - node_id id; - if (m_rpc.incoming(m, &id)) - refresh(id, boost::bind(&nop)); - break; - } - case 'q': - { - TORRENT_ASSERT(m.message.dict_find_string_value("y") == "q"); - entry e; - incoming_request(m, e); - m_send(m_userdata, 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 - break; - } - } -} - -namespace -{ - void announce_fun(std::vector > const& v - , node_impl& node, int listen_port, sha1_hash const& ih) - { -#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) << " 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)); -#ifdef TORRENT_DEBUG - 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; - 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 - void* ptr = m_rpc.allocate_observer(); - if (ptr == 0) return; - - // 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) null_observer(algo, node, node_id(0))); -#ifdef TORRENT_DEBUG - o->m_in_constructor = false; -#endif - entry e; - e["y"] = "q"; - e["q"] = "ping"; - m_rpc.invoke(e, node, o); -} - -void node_impl::announce(sha1_hash const& info_hash, int listen_port - , 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(new find_data(*this, info_hash, f - , boost::bind(&announce_fun, _1, boost::ref(*this) - , listen_port, info_hash))); - ta->start(); -} - -void node_impl::tick() -{ - node_id target; - if (m_table.need_refresh(target)) - refresh(target, boost::bind(&nop)); -} - -time_duration node_impl::connection_timeout() -{ - time_duration d = m_rpc.tick(); - ptime now(time_now()); - if (now - m_last_tracker_tick < minutes(10)) return d; - m_last_tracker_tick = now; - - // 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); - } -} - -bool node_impl::lookup_torrents(sha1_hash const& target - , entry& reply, char* tags) const -{ -// if (m_ses.m_alerts.should_post()) -// m_ses.m_alerts.post_alert(dht_find_torrents_alert(info_hash)); - - search_table_t::const_iterator first, last; - first = m_search_map.lower_bound(std::make_pair(target, (sha1_hash::min)())); - last = m_search_map.upper_bound(std::make_pair(target, (sha1_hash::max)())); - - if (first == last) return false; - - std::string tags_copy(tags); - char const* in_tags[20]; - int num_tags = 0; - num_tags = split_string(in_tags, 20, &tags_copy[0]); - - typedef std::pair sort_item; - std::vector result; - for (; first != last; ++first) - { - result.push_back(std::make_pair( - first->second.match(in_tags, num_tags), first)); - } - - std::sort(result.begin(), result.end() - , boost::bind(&sort_item::first, _1) > boost::bind(&sort_item::first, _2)); - int num = (std::min)((int)result.size(), m_settings.max_torrent_search_reply); - - entry::list_type& pe = reply["values"].list(); - for (int i = 0; i < num; ++i) - { - pe.push_back(entry()); - entry::list_type& e = pe.back().list(); - // push name - e.push_back(entry()); - result[i].second->second.get_name(e.back().string()); - // push tags - e.push_back(entry()); - result[i].second->second.get_tags(e.back().string()); - // push info-hash - e.push_back(entry()); - e.back().string() = result[i].second->first.second.to_string(); - } - return true; -} - -bool node_impl::lookup_peers(sha1_hash const& info_hash, entry& reply) const -{ - if (m_ses.m_alerts.should_post()) - m_ses.m_alerts.post_alert(dht_get_peers_alert(info_hash)); - - table_t::const_iterator i = m_map.find(info_hash); - if (i == m_map.end()) return false; - - torrent_entry const& v = i->second; - if (v.peers.empty()) return false; - - if (!v.name.empty()) reply["n"] = v.name; - - int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); - int t = 0; - int m = 0; - std::set::const_iterator iter = v.peers.begin(); - entry::list_type& pe = reply["values"].list(); - std::string endpoint; - - while (m < num) - { - if ((std::rand() / (RAND_MAX + 1.f)) * (num - t) >= num - m) - { - ++iter; - ++t; - } - else - { - endpoint.resize(18); - std::string::iterator out = endpoint.begin(); - write_endpoint(iter->addr, out); - endpoint.resize(out - endpoint.begin()); - pe.push_back(entry(endpoint)); - - ++iter; - ++t; - ++m; - } - } - return true; -} - -namespace -{ - void 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)); - } - } - } -} - -// 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); - - if (msg->type() != lazy_entry::dict_t) - { - snprintf(error, error_size, "not a dictionary"); - return false; - } - for (int i = 0; i < size; ++i) - { - key_desc_t const& k = desc[i]; - ret[i] = msg->dict_find(k.name); - if (ret[i] && ret[i]->type() != k.type) ret[i] = 0; - if (ret[i] == 0 && (k.flags & key_desc_t::optional) == 0) - { - // the key was not found, and it's not an optiona key - snprintf(error, error_size, "missing '%s' key", k.name); - return false; - } - - if (k.size > 0 - && ret[i] - && k.type == lazy_entry::string_t - && ret[i]->string_length() != k.size) - { - // 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; - } - } - } - return true; -} - -void incoming_error(entry& e, char const* msg) -{ - e["y"] = "e"; - entry::list_type& l = e["e"].list(); - l.push_back(entry(203)); - l.push_back(entry(msg)); -} - -// 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}, - {"a", lazy_entry::dict_t, 0, 0}, - }; - - lazy_entry const* top_level[2]; - char error_string[200]; - if (!verify_message(&m.message, top_desc, top_level, 2, error_string, sizeof(error_string))) - { - incoming_error(e, error_string); - return; - } - - char const* query = top_level[0]->string_cstr(); - - lazy_entry const* arg_ent = top_level[1]; - - lazy_entry const* node_id_ent = arg_ent->dict_find_string("id"); - if (node_id_ent == 0 || node_id_ent->string_length() != 20) - { - incoming_error(e, "missing 'id' key"); - return; - } - - node_id id(node_id_ent->string_ptr()); - - m_table.heard_about(id, m.addr); - - entry& reply = e["r"]; - m_rpc.add_our_id(reply); - - 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}, - }; - - 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; - } - - 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 ret = lookup_peers(info_hash, reply); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - if (ret) 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: 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) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - extern int g_failed_announces; -#endif - 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}, - }; - - lazy_entry const* msg_keys[4]; - if (!verify_message(arg_ent, msg_desc, msg_keys, 4, error_string, sizeof(error_string))) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ++g_failed_announces; -#endif - incoming_error(e, error_string); - return; - } - - int port = msg_keys[1]->int_value(); - if (port < 0 || port >= 65536) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ++g_failed_announces; -#endif - incoming_error(e, "invalid 'port' in announce"); - return; - } - - sha1_hash info_hash(msg_keys[0]->string_ptr()); - - if (m_ses.m_alerts.should_post()) - m_ses.m_alerts.post_alert(dht_announce_alert( - m.addr.address(), port, info_hash)); - - 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 in announce"); - 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); - - if (!m_map.empty() && m_map.size() >= m_ses.settings().dht_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 (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 e; - e.addr = tcp::endpoint(m.addr.address(), port); - e.added = time_now(); - std::set::iterator i = v.peers.find(e); - if (i != v.peers.end()) v.peers.erase(i++); - v.peers.insert(i, e); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - extern int g_announces; - ++g_announces; -#endif - } - else if (strcmp(query, "find_torrent") == 0) - { - key_desc_t msg_desc[] = { - {"target", lazy_entry::string_t, 20, 0}, - {"tags", lazy_entry::string_t, 0, 0}, - }; - - 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; - } - - reply["token"] = generate_token(m.addr, msg_keys[0]->string_ptr()); - - sha1_hash target(msg_keys[0]->string_ptr()); - nodes_t n; - // always return nodes as well as torrents - m_table.find_node(target, n, 0); - write_nodes_entry(reply, n); - - lookup_torrents(target, reply, (char*)msg_keys[1]->string_cstr()); - } - else if (strcmp(query, "announce_torrent") == 0) - { - key_desc_t msg_desc[] = { - {"target", lazy_entry::string_t, 20, 0}, - {"info_hash", lazy_entry::string_t, 20, 0}, - {"name", lazy_entry::string_t, 0, 0}, - {"tags", lazy_entry::string_t, 0, 0}, - {"token", lazy_entry::string_t, 0, 0}, - }; - - lazy_entry const* msg_keys[5]; - if (!verify_message(arg_ent, msg_desc, msg_keys, 5, error_string, sizeof(error_string))) - { - incoming_error(e, error_string); - return; - } - -// if (m_ses.m_alerts.should_post()) -// m_ses.m_alerts.post_alert(dht_announce_torrent_alert( -// m.addr.address(), name, tags, info_hash)); - - if (!verify_token(msg_keys[4]->string_value(), msg_keys[0]->string_ptr(), m.addr)) - { - incoming_error(e, "invalid token in announce"); - return; - } - - sha1_hash target(msg_keys[0]->string_ptr()); - sha1_hash info_hash(msg_keys[1]->string_ptr()); - - // 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); - - search_table_t::iterator i = m_search_map.find(std::make_pair(target, info_hash)); - if (i == m_search_map.end()) - { - boost::tie(i, boost::tuples::ignore) - = m_search_map.insert(std::make_pair(std::make_pair(target, info_hash) - , search_torrent_entry())); - } - - char const* in_tags[20]; - int num_tags = 0; - num_tags = split_string(in_tags, 20, (char*)msg_keys[3]->string_cstr()); - - i->second.publish(msg_keys[2]->string_value(), in_tags, num_tags); - } - 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/libtorrent_utp/src/kademlia/node_id.cpp b/libtorrent_utp/src/kademlia/node_id.cpp deleted file mode 100644 index b823c8911..000000000 --- a/libtorrent_utp/src/kademlia/node_id.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include -#include - -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/assert.hpp" - -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(std::time(0)); } } static__; - -node_id generate_id() -{ - char random[20]; -#ifdef _MSC_VER - std::generate(random, random + 20, &rand); -#else - std::generate(random, random + 20, &std::rand); -#endif - - hasher h; - h.update(random, 20); - return h.final(); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/kademlia/refresh.cpp b/libtorrent_utp/src/kademlia/refresh.cpp deleted file mode 100644 index 54363189a..000000000 --- a/libtorrent_utp/src/kademlia/refresh.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Copyright (c) 2006, 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/pch.hpp" - -#include -#include -#include - -#include - -namespace libtorrent { namespace dht -{ - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_DECLARE_LOG(traversal); -#endif - -refresh::refresh( - node_impl& node - , node_id target - , done_callback const& callback) - : find_data(node, target, find_data::data_callback(), callback) -{ -} - -char const* refresh::name() const -{ - return "refresh"; -} - -observer_ptr refresh::new_observer(void* ptr - , udp::endpoint const& ep, node_id const& id) -{ - observer_ptr o(new (ptr) find_data_observer(this, ep, id)); -#ifdef TORRENT_DEBUG - o->m_in_constructor = false; -#endif - return o; -} - -bool refresh::invoke(observer_ptr o) -{ - entry e; - e["y"] = "q"; - e["q"] = "find_node"; - entry& a = e["a"]; - a["target"] = target().to_string(); - m_node.m_rpc.invoke(e, o->target_ep(), o); - return true; -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/kademlia/routing_table.cpp b/libtorrent_utp/src/kademlia/routing_table.cpp deleted file mode 100644 index 6398d495d..000000000 --- a/libtorrent_utp/src/kademlia/routing_table.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include -#include // std::distance() -#include -#include -#include -#include -#include - -#include "libtorrent/kademlia/routing_table.hpp" -#include "libtorrent/session_status.hpp" -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/time.hpp" - -using boost::uint8_t; - -namespace libtorrent { namespace dht -{ - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(table) -#endif - -routing_table::routing_table(node_id const& id, int bucket_size - , dht_settings const& settings) - : m_bucket_size(bucket_size) - , m_settings(settings) - , m_id(id) - , m_last_bootstrap(min_time()) -{ -} - -void routing_table::status(session_status& s) const -{ - boost::tie(s.dht_nodes, s.dht_node_cache) = size(); - s.dht_global_nodes = num_global_nodes(); -} - -boost::tuple routing_table::size() const -{ - int nodes = 0; - int replacements = 0; - for (table_t::const_iterator i = m_buckets.begin() - , end(m_buckets.end()); i != end; ++i) - { - nodes += i->live_nodes.size(); - replacements += i->replacements.size(); - } - return boost::make_tuple(nodes, replacements); -} - -size_type routing_table::num_global_nodes() const -{ - int num_nodes = 1; // we are one of the nodes - for (table_t::const_iterator i = m_buckets.begin() - , end(m_buckets.end()); i != end; ++i) - { - num_nodes += i->live_nodes.size(); - } - - return (2 << m_buckets.size()) * num_nodes; -} - -#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-- live "; - for (int i = 8; i < 160; ++i) - os << "-"; - os << "\n"; - - for (int k = 0; k < m_bucket_size; ++k) - { - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - os << (int(i->live_nodes.size()) > (m_bucket_size - 1 - k) ? "|" : " "); - } - os << "\n"; - } - for (int i = 0; i < 160; ++i) os << "+"; - os << "\n"; - - for (int k = 0; k < m_bucket_size; ++k) - { - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - os << (int(i->replacements.size()) > k ? "|" : " "); - } - os << "\n"; - } - os << "-- cached "; - for (int i = 10; i < 160; ++i) - os << "-"; - os << "\n\n"; - - os << "nodes:\n"; - int bucket_index = 0; - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i, ++bucket_index) - { -// if (i->live_nodes.empty()) continue; - os << "=== BUCKET = " << bucket_index - << " = " << total_seconds(time_now() - i->last_active) - << " seconds ago ===== \n"; - for (bucket_t::const_iterator j = i->live_nodes.begin() - , end(i->live_nodes.end()); j != end; ++j) - { - os << " id: " << j->id - << " ip: " << j->ep() - << " fails: " << j->fail_count() - << " pinged: " << j->pinged() - << " dist: " << distance_exp(m_id, j->id) - << "\n"; - } - } -} - -#endif - -void routing_table::touch_bucket(node_id const& target) -{ - table_t::iterator i = find_bucket(target); - i->last_active = time_now(); -} - -bool routing_table::need_refresh(node_id& target) const -{ - if (m_buckets.empty()) return false; - - table_t::const_iterator i = std::min_element(m_buckets.begin(), m_buckets.end() - , boost::bind(&routing_table_node::last_active, _1) - < boost::bind(&routing_table_node::last_active, _2)); - - if (time_now() - i->last_active < minutes(15)) return false; - - // generate a random node_id within the given bucket - target = generate_id(); - int num_bits = std::distance(m_buckets.begin(), i) + 1; - node_id mask(0); - for (int i = 0; i < num_bits; ++i) mask[i/8] |= 0x80 >> (i&7); - - // target = (target & ~mask) | (root & mask) - node_id root = m_id; - root &= mask; - target &= ~mask; - target |= root; - - // make sure this is in another subtree than m_id - // clear the (num_bits - 1) bit and then set it to the - // inverse of m_id's corresponding bit. - target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8)); - target[(num_bits - 1) / 8] |= - (~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8)); - - TORRENT_ASSERT(distance_exp(m_id, target) == 160 - num_bits); - return true; -} - -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 < m_buckets.size()); - TORRENT_ASSERT(bucket_index >= 0); - - table_t::iterator i = m_buckets.begin(); - std::advance(i, bucket_index); - return i; -} - -bool routing_table::add_node(node_entry const& e) -{ - if (m_router_nodes.find(e.ep()) != m_router_nodes.end()) return false; - - bool ret = need_bootstrap(); - - // don't add ourself - if (e.id == m_id) return ret; - - table_t::iterator i = find_bucket(e.id); - bucket_t& b = i->live_nodes; - bucket_t& rb = i->replacements; - - // if the replacement cache is full, we don't - // need another node. The table is fine the - // way it is. - if ((int)rb.size() >= m_bucket_size) return ret; - - // if the node already exists, we don't need it - bucket_t::iterator j = std::find_if(b.begin(), b.end() - , boost::bind(&node_entry::id, _1) == e.id); - - if (j != b.end()) - { - // we already have the node in our bucket - // just move it to the back since it was - // the last node we had any contact with - // in this bucket - *j = e; -// TORRENT_LOG(table) << "updating node: " << i->id << " " << i->addr; - return ret; - } - - if (std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::id, _1) == e.id) - != rb.end()) return ret; - - // if the node was not present in our list - // we will only insert it if there is room - // for it, or if some of our nodes have gone - // offline - if (b.size() < m_bucket_size) - { - if (b.empty()) b.reserve(m_bucket_size); - b.push_back(e); -// TORRENT_LOG(table) << "inserting node: " << e.id << " " << e.addr; - return ret; - } - - // 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. - - // can we split the bucket? - bool can_split = false; - - if (e.pinged() && e.fail_count() == 0) - { - // only nodes that are pinged and haven't failed - // can split the bucket, and we can only split - // the last bucket - can_split = (boost::next(i) == m_buckets.end() && m_buckets.size() < 160); - - // 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 - b.erase(j); - b.push_back(e); -// TORRENT_LOG(table) << "replacing unpinged node: " << e.id << " " << e.addr; - return ret; - } - - // 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)); - - if (j != b.end() && j->fail_count() > 0) - { - // i points to a node that has been marked - // as stale. Replace it with this new one - b.erase(j); - b.push_back(e); -// TORRENT_LOG(table) << "replacing stale node: " << e.id << " " << e.addr; - return ret; - } - } - - // 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()) - { - // make sure we mark this node as pinged - // and if its address has changed, update - // that as well - *j = e; - return ret; - } - - 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); - rb.erase(j != rb.end() ? j : rb.begin()); - } - - if (rb.empty()) rb.reserve(m_bucket_size); - rb.push_back(e); -// TORRENT_LOG(table) << "inserting node in replacement cache: " << e.id << " " << e.addr; - return ret; - } - - // 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; - - // move any node whose (160 - distane_exp(m_id, id)) >= (i - m_buckets.begin()) - // to the new bucket - int bucket_index = std::distance(m_buckets.begin(), i); - 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); - } - for (bucket_t::iterator j = rb.begin(); j != rb.end();) - { - if (distance_exp(m_id, j->id) >= 159 - bucket_index) - { - ++j; - continue; - } - // this entry belongs in the new bucket - new_replacement_bucket.push_back(*j); - j = rb.erase(j); - } - - // now insert the new node in the appropriate bucket - if (distance_exp(m_id, e.id) >= 159 - bucket_index) - { - if (b.size() < m_bucket_size) - b.push_back(e); - else if (rb.size() < m_bucket_size) - rb.push_back(e); - } - else - { - if (new_bucket.size() < m_bucket_size) - new_bucket.push_back(e); - else if (new_replacement_bucket.size() < m_bucket_size) - new_replacement_bucket.push_back(e); - } - return ret; -} - -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& id) -{ - // if messages to ourself fails, ignore it - if (id == m_id) return; - - table_t::iterator i = find_bucket(id); - 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) == id); - - if (j == b.end()) return; - - if (rb.empty()) - { - j->timed_out(); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(table) << " NODE FAILED" - " id: " << id << - " 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()) - b.erase(j); - return; - } - - b.erase(j); - - j = std::find_if(rb.begin(), rb.end(), boost::bind(&node_entry::pinged, _1) == true); - 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, false)); -} - -// 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) -{ - return add_node(node_entry(id, ep, true)); -} - -bool routing_table::need_bootstrap() const -{ - ptime now = time_now(); - if (now - m_last_bootstrap < seconds(30)) return false; - - for (table_t::const_iterator i = m_buckets.begin() - , end(m_buckets.end()); i != end; ++i) - { - for (bucket_t::const_iterator j = i->live_nodes.begin() - , end(i->live_nodes.end()); j != end; ++j) - { - if (j->confirmed()) return false; - } - } - m_last_bootstrap = now; - return true; -} - -template -DstIter copy_if_n(SrcIter begin, SrcIter end, DstIter target, size_t n, Pred p) -{ - for (; n > 0 && begin != end; ++begin) - { - if (!p(*begin)) continue; - *target = *begin; - --n; - ++target; - } - return target; -} - -template -DstIter copy_n(SrcIter begin, SrcIter end, DstIter target, size_t n) -{ - for (; n > 0 && begin != end; ++begin) - { - *target = *begin; - --n; - ++target; - } - return target; -} - -// 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; - l.reserve(count); - - table_t::iterator i = find_bucket(target); - bucket_t& b = i->live_nodes; - - // copy all nodes that hasn't failed into the target - // vector. - if (options & include_failed) - { - copy_n(b.begin(), b.end(), std::back_inserter(l) - , (std::min)(size_t(count), b.size())); - } - else - { - copy_if_n(b.begin(), b.end(), std::back_inserter(l) - , (std::min)(size_t(count), b.size()) - , boost::bind(&node_entry::confirmed, _1)); - } - TORRENT_ASSERT((int)l.size() <= count); - - if (int(l.size()) >= count) return; - - // if we didn't have enough nodes in that bucket - // we have to reply with nodes from buckets closer - // to us. - table_t::iterator j = i; - ++j; - - for (; j != m_buckets.end() && l.size() < count; ++j) - { - bucket_t& b = j->live_nodes; - size_t to_copy = (std::min)(count - l.size(), b.size()); - if (options & include_failed) - { - copy(b.begin(), b.begin() + to_copy - , std::back_inserter(l)); - } - else - { - std::remove_copy_if(b.begin(), b.begin() + to_copy - , std::back_inserter(l) - , !boost::bind(&node_entry::confirmed, _1)); - } - } - - if (int(l.size()) >= count) return; - - // if we still don't have enough nodes, copy nodes - // further away from us - - if (i == m_buckets.begin()) return; - j = i; - - do - { - --j; - bucket_t& b = j->live_nodes; - - size_t to_copy = (std::min)(count - l.size(), b.size()); - if (options & include_failed) - { - copy_n(b.begin(), b.end(), std::back_inserter(l), to_copy); - } - else - { - copy_if_n(b.begin(), b.end(), std::back_inserter(l) - , to_copy, boost::bind(&node_entry::confirmed, _1)); - } - } - while (j != m_buckets.begin() && l.size() < count); -} -/* -routing_table::iterator routing_table::begin() const -{ - // +1 to avoid ourself - return iterator(m_buckets.begin() + 1, m_buckets.end()); -} - -routing_table::iterator routing_table::end() const -{ - return iterator(m_buckets.end(), m_buckets.end()); -} -*/ -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/kademlia/rpc_manager.cpp b/libtorrent_utp/src/kademlia/rpc_manager.cpp deleted file mode 100644 index 4fac42f70..000000000 --- a/libtorrent_utp/src/kademlia/rpc_manager.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" -#include "libtorrent/socket.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // time() - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -#include -#endif - -using boost::shared_ptr; - -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->m_refs >= 0); - TORRENT_ASSERT(o != 0); - ++o->m_refs; -} - -void intrusive_ptr_release(observer const* o) -{ - TORRENT_ASSERT(o->m_refs > 0); - TORRENT_ASSERT(o != 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()) - { - m_is_v6 = true; - m_addr.v6 = ep.address().to_v6().to_bytes(); - } - else -#endif - { - m_is_v6 = false; - m_addr.v4 = ep.address().to_v4().to_bytes(); - } -} - -address observer::target_addr() const -{ -#if TORRENT_USE_IPV6 - if (m_is_v6) - 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 (m_done) return; - m_done = true; - m_algorithm->failed(observer_ptr(this), traversal_algorithm::prevent_request); -} - -void observer::done() -{ - if (m_done) return; - m_done = true; - m_algorithm->finished(observer_ptr(this)); -} - -void observer::short_timeout() -{ - if (m_short_timeout) return; - TORRENT_ASSERT(m_short_timeout == false); - m_short_timeout = true; - 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 (m_done) return; - m_done = true; - m_algorithm->failed(observer_ptr(this)); -} - -node_id generate_id(); - -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, send_fun const& sf - , void* userdata) - : m_pool_allocator(observer_size, 10) - , m_next_transaction_id(std::rand() % max_transaction_id) - , m_send(sf) - , m_userdata(userdata) - , m_our_id(our_id) - , m_table(table) - , m_timer(time_now()) - , m_random_number(generate_id()) - , m_allocated_observers(0) - , m_destructing(false) -{ - std::srand(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(observer, flags); - PRINT_OFFSETOF(observer, m_sent); - PRINT_OFFSETOF(observer, m_refs); - PRINT_OFFSETOF(observer, m_algorithm); - PRINT_OFFSETOF(observer, m_id); - PRINT_OFFSETOF(observer, m_addr); - PRINT_OFFSETOF(observer, m_port); - PRINT_OFFSETOF(observer, m_transaction_id); - - 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; - m_pool_allocator.free(ptr); -} - -#ifdef TORRENT_DEBUG -size_t rpc_manager::allocation_size() const -{ - return observer_size; -} - -void rpc_manager::check_invariant() const -{ - TORRENT_ASSERT(m_next_transaction_id >= 0); - TORRENT_ASSERT(m_next_transaction_id < max_transaction_id); - - 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; - 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); - -bool rpc_manager::incoming(msg const& m, node_id* id) -{ - INVARIANT_CHECK; - - if (m_destructing) return false; - - // we only deal with replies, not queries - TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r"); - - // 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"); - - 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; ++i) - { - TORRENT_ASSERT(*i); - if ((*i)->transaction_id() != tid) continue; - if (m.addr.address() != (*i)->target_addr()) continue; - o = *i; - m_transactions.erase(i); - break; - } - - if (!o) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " - << transaction_id.size() << " from " << m.addr; -#endif - entry e; - incoming_error(e, "invalid transaction id"); - m_send(m_userdata, e, m.addr, 0); - return false; - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - std::ofstream reply_stats("round_trip_ms.log", std::ios::app); - reply_stats << m.addr << "\t" << total_milliseconds(time_now_hires() - o->sent()) - << std::endl; -#endif - - lazy_entry const* ret_ent = m.message.dict_find_dict("r"); - if (ret_ent == 0) - { - entry e; - incoming_error(e, "missing 'r' key"); - m_send(m_userdata, 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) - { - entry e; - incoming_error(e, "missing 'id' key"); - m_send(m_userdata, 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 = node_id(node_id_ent->string_ptr()); - - // 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); -} - -time_duration rpc_manager::tick() -{ - INVARIANT_CHECK; - - const static int short_timeout = 3; - const static int timeout = 20; - - // 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(); - - 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 - 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; - } - - if (o->has_short_timeout()) continue; - - // TODO: don't call short_timeout() again if we've - // already called it once - 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]; - io::write_uint16(m_next_transaction_id, out); - e["t"] = transaction_id; - - o->set_target(target_addr); - o->set_transaction_id(m_next_transaction_id); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] invoking " - << e["q"].string() << " -> " << target_addr; -#endif - - if (m_send(m_userdata, e, target_addr, 1)) - { - m_transactions.push_back(o); - ++m_next_transaction_id; - m_next_transaction_id %= max_transaction_id; -#ifdef TORRENT_DEBUG - o->m_was_sent = true; -#endif - } - return true; -} - -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 == m_done); - TORRENT_ASSERT(!m_in_constructor); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/kademlia/traversal_algorithm.cpp b/libtorrent_utp/src/kademlia/traversal_algorithm.cpp deleted file mode 100644 index 7e5c45ce4..000000000 --- a/libtorrent_utp/src/kademlia/traversal_algorithm.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - -Copyright (c) 2006, 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/pch.hpp" - -#include -#include -#include -#include -#include - -#include - -namespace libtorrent { namespace dht -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(traversal) -#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)); -#ifdef TORRENT_DEBUG - o->m_in_constructor = false; -#endif - return o; -} - -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_id()); - o->flags |= observer::flag_no_id; - } - - o->flags |= flags; - - 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) - { - TORRENT_ASSERT(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 << "] adding result: " << id << " " << addr; -#endif - i = m_results.insert(i, o); - } - - if (m_results.size() > 100) 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.empty()) add_router_entries(); - init(); - add_requests(); -} - -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); -} - -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 - 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) - --m_branch_factor; - - o->flags |= observer::flag_alive; - - ++m_responses; - --m_invoke_count; - TORRENT_ASSERT(m_invoke_count >= 0); - add_requests(); - if (m_invoke_count == 0) 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); - - 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 << "] first chance timeout: " - << o->id() << " " << o->target_ep() - << " branch-factor: " << m_branch_factor - << " invoke-count: " << m_invoke_count; -#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 << "] failed: " - << o->id() << " " << o->target_ep() - << " branch-factor: " << m_branch_factor - << " invoke-count: " << m_invoke_count; -#endif - // 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()); - ++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; - } - add_requests(); - if (m_invoke_count == 0) done(); -} - -void traversal_algorithm::done() -{ - // delete all our references to the observer objects so - // they will in turn release the traversal algorithm - m_results.clear(); -} - -namespace -{ - bool bitwise_nand(unsigned char lhs, unsigned char rhs) - { - return (lhs & rhs) == 0; - } -} - -void traversal_algorithm::add_requests() -{ - int results_target = m_node.m_table.bucket_size(); - - // Find the first node that hasn't already been queried. - for (std::vector::iterator i = m_results.begin() - , end(m_results.end()); i != end - && results_target > 0 && m_invoke_count < m_branch_factor; ++i) - { - if ((*i)->flags & observer::flag_alive) --results_target; - if ((*i)->flags & observer::flag_queried) continue; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << " [" << this << "]" - << " nodes-left: " << (m_results.end() - i) - << " invoke-count: " << m_invoke_count - << " branch-factor: " << m_branch_factor; -#endif - - if (invoke(*i)) - { - TORRENT_ASSERT(m_invoke_count >= 0); - ++m_invoke_count; - (*i)->flags |= observer::flag_queried; - } - } -} - -void traversal_algorithm::add_router_entries() -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << " 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() -{ - // update the last activity of this bucket - m_node.m_table.touch_bucket(m_target); - 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; - for (std::vector::iterator i = m_results.begin() - , end(m_results.end()); i != end; ++i) - { - if ((*i)->flags & observer::flag_queried) continue; - ++l.nodes_left; - } -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent_utp/src/lazy_bdecode.cpp b/libtorrent_utp/src/lazy_bdecode.cpp deleted file mode 100644 index 09d8f0e39..000000000 --- a/libtorrent_utp/src/lazy_bdecode.cpp +++ /dev/null @@ -1,548 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/lazy_entry.hpp" -#include "libtorrent/escape_string.hpp" -#include - -#if TORRENT_USE_IOSTREAM -#include -#endif - -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 -{ - -#define TORRENT_FAIL_BDECODE(code) \ - { \ - ec = code; \ - if (error_pos) *error_pos = start - orig_start; \ - ret.clear(); \ - return -1; \ - } - // 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) - { - while (start < end && *start != delimiter) - { - if (!is_digit(*start)) { return 0; } - val *= 10; - val += *start - '0'; - ++start; - } - return start; - } - - char const* find_char(char const* start, char const* end, char delimiter) - { - while (start < end && *start != delimiter) ++start; - return start; - } - - // 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(errors::depth_exceeded); - if (start >= end) TORRENT_FAIL_BDECODE(errors::unexpected_eof); - char t = *start; - ++start; - if (start >= end && t != 'e') TORRENT_FAIL_BDECODE(errors::unexpected_eof); - - switch (top->type()) - { - case lazy_entry::dict_t: - { - if (t == 'e') - { - top->set_end(start); - stack.pop_back(); - continue; - } - if (!is_digit(t)) TORRENT_FAIL_BDECODE(errors::expected_string); - boost::int64_t len = t - '0'; - start = parse_int(start, end, ':', len); - if (start == 0 || start + len + 3 > end || *start != ':') - TORRENT_FAIL_BDECODE(errors::expected_colon); - ++start; - if (start == end) TORRENT_FAIL_BDECODE(errors::unexpected_eof); - lazy_entry* ent = top->dict_append(start); - if (ent == 0) TORRENT_FAIL_BDECODE(errors::no_memory); - start += len; - if (start >= end) TORRENT_FAIL_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(errors::no_memory); - stack.push_back(ent); - break; - } - default: break; - } - - --item_limit; - if (item_limit <= 0) TORRENT_FAIL_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(errors::unexpected_eof); - TORRENT_ASSERT(*start == 'e'); - ++start; - stack.pop_back(); - continue; - } - default: - { - if (!is_digit(t)) TORRENT_FAIL_BDECODE(errors::expected_value); - - boost::int64_t len = t - '0'; - start = parse_int(start, end, ':', len); - if (start == 0 || start + len + 1 > end || *start != ':') - TORRENT_FAIL_BDECODE(errors::expected_colon); - ++start; - top->construct_string(start, int(len)); - stack.pop_back(); - start += len; - continue; - } - } - return 0; - } - return 0; - } - - size_type 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; - parse_int(negative?m_data.start+1:m_data.start, m_data.start + m_size, 'e', val); - 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; - } - - 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; - } - - size_type lazy_entry::dict_find_int_value(char const* name, size_type 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_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::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(); - } - - size_type lazy_entry::list_int_value_at(int i, size_type 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); - } - -#if TORRENT_USE_IOSTREAM - std::ostream& operator<<(std::ostream& os, lazy_entry const& e) - { - return os << print_entry(e); - } -#endif // TORRENT_USE_IOSTREAM - - 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: - { - size_type val = e.int_value(); - while (val > 0) - { - ++line_len; - val /= 10; - } - line_len += 2; - } - break; - } - - if (line_len > limit) return -1; - return line_len; - } - - 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: - { - bool printable = true; - char const* str = e.string_ptr(); - for (int i = 0; i < e.string_length(); ++i) - { - using namespace std; - if (is_print((unsigned char)str[i])) continue; - printable = false; - break; - } - ret += "'"; - if (printable) - { - ret += e.string_value(); - ret += "'"; - return ret; - } - for (int i = 0; i < e.string_length(); ++i) - { - char tmp[5]; - snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]); - ret += tmp; - } - ret += "'"; - 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); - ret += "'"; - ret += ent.first; - 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; - } -}; - diff --git a/libtorrent_utp/src/logger.cpp b/libtorrent_utp/src/logger.cpp deleted file mode 100644 index f47ac7a77..000000000 --- a/libtorrent_utp/src/logger.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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 - -#include -#include "libtorrent/file.hpp" -#include "libtorrent/time.hpp" -#include "libtorrent/lazy_entry.hpp" -#include "libtorrent/peer_connection.hpp" - -namespace libtorrent { - - struct 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 | 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" << 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(std::vector 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, char const*) - { - 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/libtorrent_utp/src/lsd.cpp b/libtorrent_utp/src/lsd.cpp deleted file mode 100644 index 4b3a53ed7..000000000 --- a/libtorrent_utp/src/lsd.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include "libtorrent/lsd.hpp" -#include "libtorrent/io.hpp" -#include "libtorrent/http_tracker_connection.hpp" -#include "libtorrent/buffer.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_retry_count(1) - , m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771) - , boost::bind(&lsd::on_announce, self(), _1, _2, _3)) - , m_broadcast_timer(ios) - , m_disabled(false) -{ -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc); -#endif -} - -lsd::~lsd() {} - -void lsd::announce(sha1_hash const& ih, int listen_port) -{ - if (m_disabled) return; - - char ih_hex[41]; - to_hex((char const*)&ih[0], 20, ih_hex); - char msg[200]; - int msg_len = snprintf(msg, sizeof(msg), - "BT-SEARCH * HTTP/1.1\r\n" - "Host: 239.192.152.143:6771\r\n" - "Port: %d\r\n" - "Infohash: %s\r\n" - "\r\n\r\n", listen_port, ih_hex); - - m_retry_count = 1; - error_code ec; - m_socket.send(msg, msg_len, ec); - if (ec) - { - m_disabled = true; - return; - } - -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - { - char msg[200]; - snprintf(msg, sizeof(msg), "%s ==> announce: ih: %s port: %u" - , time_now_string(), ih_hex, listen_port); - m_log << msg << std::endl; - } -#endif - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("lsd::resend_announce"); -#endif - m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); - m_broadcast_timer.async_wait(boost::bind(&lsd::resend_announce, self(), _1 - , std::string(msg))); -} - -void lsd::resend_announce(error_code const& e, std::string msg) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("lsd::resend_announce"); -#endif - if (e) return; - - error_code ec; - m_socket.send(msg.c_str(), int(msg.size()), ec); - - ++m_retry_count; - if (m_retry_count >= 5) - return; - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("lsd::resend_announce"); -#endif - m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); - m_broadcast_timer.async_wait(boost::bind(&lsd::resend_announce, self(), _1, msg)); -} - -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) - m_log << time_now_string() - << " <== announce: incomplete HTTP message" << std::endl; -#endif - return; - } - - if (p.method() != "bt-search") - { -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log << time_now_string() - << " <== announce: invalid HTTP method: " << p.method() << std::endl; -#endif - return; - } - - std::string const& port_str = p.header("port"); - if (port_str.empty()) - { -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log << time_now_string() - << " <== announce: invalid BT-SEARCH, missing port" << std::endl; -#endif - return; - } - - std::string const& ih_str = p.header("infohash"); - if (ih_str.empty()) - { -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log << time_now_string() - << " <== announce: invalid BT-SEARCH, missing infohash" << std::endl; -#endif - return; - } - - sha1_hash ih(0); - from_hex(ih_str.c_str(), 40, (char*)&ih[0]); - int port = std::atoi(port_str.c_str()); - - if (!ih.is_all_zeros() && port != 0) - { -#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - char msg[200]; - snprintf(msg, 200, "%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 -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - m_callback(tcp::endpoint(from.address(), port), ih); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception&) {} -#endif - } -} - -void lsd::close() -{ - m_socket.close(); - error_code ec; - m_broadcast_timer.cancel(ec); - m_disabled = true; - m_callback.clear(); -} - -void lsd::use_broadcast(bool b) -{ - m_socket.enable_ip_broadcast(b); -} - diff --git a/libtorrent_utp/src/lt_trackers.cpp b/libtorrent_utp/src/lt_trackers.cpp deleted file mode 100644 index 446f83652..000000000 --- a/libtorrent_utp/src/lt_trackers.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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" -#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_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; } - - 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; - }; - - - 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 = 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"); - -#ifdef TORRENT_VERBOSE_LOGGING - std::stringstream log_line; - log_line << time_now_string() << " <== LT_TEX [ " - "added: "; -#endif - - // 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; - } - - for (int i = 0; i < added->list_size(); ++i) - { - announce_entry e(added->list_string_value_at(i)); - if (e.url.empty()) continue; - e.fail_limit = 3; - e.send_stats = false; - e.source = announce_entry::source_tex; - m_torrent.add_tracker(e); -#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) - { - 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; - - std::vector const& tex_msg = m_tp.get_lt_tex_msg(); - - buffer::interval i = m_pc.allocate_send_buffer(6 + tex_msg.size()); - - detail::write_uint32(1 + 1 + tex_msg.size(), i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - std::copy(tex_msg.begin(), tex_msg.end(), i.begin); - i.begin += tex_msg.size(); - - TORRENT_ASSERT(i.begin == i.end); - m_pc.setup_send(); - } - - void send_full_tex_list() const - { - if (m_tp.trackers().empty()) return; - -#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 - - buffer::interval i = m_pc.allocate_send_buffer(6 + tex_msg.size()); - - detail::write_uint32(1 + 1 + tex_msg.size(), i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - std::copy(tex_msg.begin(), tex_msg.end(), i.begin); - i.begin += tex_msg.size(); - - TORRENT_ASSERT(i.begin == i.end); - m_pc.setup_send(); - } - - // 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(); - - 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)); - } - -} - - diff --git a/libtorrent_utp/src/magnet_uri.cpp b/libtorrent_utp/src/magnet_uri.cpp deleted file mode 100644 index a46b41cca..000000000 --- a/libtorrent_utp/src/magnet_uri.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/magnet_uri.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/torrent_handle.hpp" -#include "libtorrent/escape_string.hpp" - -#include - -namespace libtorrent -{ - std::string make_magnet_uri(torrent_handle const& handle) - { - if (!handle.is_valid()) return ""; - - char ret[1024]; - sha1_hash const& ih = handle.info_hash(); - int num_chars = snprintf(ret, sizeof(ret), "magnet:?xt=urn:btih:%s" - , base32encode(std::string((char const*)&ih[0], 20)).c_str()); - - std::string name = handle.name(); - - if (!name.empty()) - num_chars += snprintf(ret + num_chars, sizeof(ret) - num_chars, "&dn=%s" - , escape_string(name.c_str(), name.length()).c_str()); - - char const* tracker = 0; - torrent_status st = handle.status(); - if (!st.current_tracker.empty()) - { - tracker = st.current_tracker.c_str(); - } - else - { - std::vector const& tr = handle.trackers(); - if (!tr.empty()) tracker = tr[0].url.c_str(); - } - if (tracker) - num_chars += snprintf(ret + num_chars, sizeof(ret) - num_chars, "&tr=%s" - , escape_string(tracker, strlen(tracker)).c_str()); - - return ret; - } - - std::string make_magnet_uri(torrent_info const& info) - { - char ret[1024]; - sha1_hash const& ih = info.info_hash(); - int num_chars = snprintf(ret, sizeof(ret), "magnet:?xt=urn:btih:%s" - , base32encode(std::string((char*)&ih[0], 20)).c_str()); - - std::string const& name = info.name(); - - if (!name.empty()) - num_chars += snprintf(ret + num_chars, sizeof(ret) - num_chars, "&dn=%s" - , escape_string(name.c_str(), name.length()).c_str()); - - std::vector const& tr = info.trackers(); - if (!tr.empty()) - { - num_chars += snprintf(ret + num_chars, sizeof(ret) - num_chars, "&tr=%s" - , escape_string(tr[0].url.c_str(), tr[0].url.length()).c_str()); - } - - return ret; - } - -#ifndef BOOST_NO_EXCEPTIONS -#ifndef TORRENT_NO_DEPRECATE - 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) - { - std::string name; - std::string tracker; - - error_code ec; - std::string display_name = url_has_argument(uri, "dn"); - if (!display_name.empty()) name = unescape_string(display_name.c_str(), ec); - std::string tracker_string = url_has_argument(uri, "tr"); - if (!tracker_string.empty()) tracker = 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(); - - 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))); - - return ses.add_torrent(tracker.empty() ? 0 : tracker.c_str(), info_hash - , name.empty() ? 0 : name.c_str(), save_path, entry() - , storage_mode, paused, sc, userdata); - } -#endif - - torrent_handle add_magnet_uri(session& ses, std::string const& uri - , add_torrent_params p) - { - error_code ec; - torrent_handle ret = add_magnet_uri(ses, uri, p, ec); - if (ec) throw libtorrent_exception(ec); - return ret; - } -#endif - torrent_handle add_magnet_uri(session& ses, std::string const& uri - , add_torrent_params p, error_code& ec) - { - std::string name; - std::string tracker; - - error_code e; - std::string display_name = url_has_argument(uri, "dn"); - if (!display_name.empty()) name = unescape_string(display_name.c_str(), e); - std::string::size_type pos = std::string::npos; - std::string tracker_string = url_has_argument(uri, "tr", &pos); - if (!tracker_string.empty()) tracker = unescape_string(tracker_string.c_str(), e); - - std::string btih = url_has_argument(uri, "xt"); - if (btih.empty()) - { - ec = errors::missing_info_hash_in_uri; - return torrent_handle(); - } - - if (btih.compare(0, 9, "urn:btih:") != 0) - { - ec = errors::missing_info_hash_in_uri; - return torrent_handle(); - } - -#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) - ses.add_dht_node(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))); - - if (!tracker.empty()) p.tracker_url = tracker.c_str(); - p.info_hash = info_hash; - if (!name.empty()) p.name = name.c_str(); - torrent_handle ret = ses.add_torrent(p, ec); - - int tier = 1; - // there might be more trackers in the url - while (pos != std::string::npos) - { - pos = uri.find("&tr=", pos); - if (pos == std::string::npos) break; - pos += 4; - announce_entry ae(uri.substr(pos, uri.find('&', pos) - pos)); - ae.tier = tier++; - ret.add_tracker(ae); - } - return ret; - } -} - - diff --git a/libtorrent_utp/src/metadata_transfer.cpp b/libtorrent_utp/src/metadata_transfer.cpp deleted file mode 100644 index f13bb5606..000000000 --- a/libtorrent_utp/src/metadata_transfer.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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; - } - - 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 = 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.m_logger) << time_now_string() - << " ==> METADATA_REQUEST [ start: " << start << " | size: " << size << " ]\n"; -#endif - - buffer::interval i = m_pc.allocate_send_buffer(9); - - detail::write_uint32(1 + 1 + 3, i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - // means 'request data' - detail::write_uint8(0, i.begin); - detail::write_uint8(start, i.begin); - detail::write_uint8(size - 1, i.begin); - TORRENT_ASSERT(i.begin == i.end); - 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()); - - // TODO: don't allocate send buffer for the metadata part - // just tag it on as a separate buffer like ut_metadata - buffer::interval i = m_pc.allocate_send_buffer(15 + offset.second); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_pc.m_logger) << time_now_string() - << " ==> METADATA [ start: " << req.first - << " | size: " << req.second - << " | offset: " << offset.first - << " | byte_size: " << offset.second - << " ]\n"; -#endif - // yes, we have metadata, send it - detail::write_uint32(11 + offset.second, i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - // means 'data packet' - detail::write_uint8(1, i.begin); - detail::write_uint32((int)m_tp.metadata().left(), i.begin); - detail::write_uint32(offset.first, i.begin); - char const* metadata = m_tp.metadata().begin; - std::copy(metadata + offset.first - , metadata + offset.first + offset.second, i.begin); - i.begin += offset.second; - TORRENT_ASSERT(i.begin == i.end); - } - else - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_pc.m_logger) << time_now_string() - << " ==> DONT HAVE METADATA\n"; -#endif - buffer::interval i = m_pc.allocate_send_buffer(4 + 3); - // we don't have the metadata, reply with - // don't have-message - detail::write_uint32(1 + 2, i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - // means 'have no data' - detail::write_uint8(2, i.begin); - TORRENT_ASSERT(i.begin == i.end); - } - 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.m_logger) << time_now_string() - << " <== METADATA_REQUEST [ start: " << start - << " | size: " << size - << " ]\n"; -#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.m_logger) << time_now_string() - << " <== METADATA [ total_size: " << total_size - << " | offset: " << offset - << " | data_size: " << data_size - << " ]\n"; -#endif - - if (total_size > 500 * 1024) - { - 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.m_logger) << time_now_string() - << " <== DONT HAVE METADATA\n"; -#endif - break; - default: - { - m_pc.disconnect(errors::invalid_metadata_message, 2); - } - } - return true; - } - - virtual void tick() - { - // 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(); - - bt_peer_connection* c = static_cast(pc); - 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; - if (num_blocks < 1) num_blocks = 1; - 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)); - } - -} - - diff --git a/libtorrent_utp/src/mpi.c b/libtorrent_utp/src/mpi.c deleted file mode 100644 index e6d8b386c..000000000 --- a/libtorrent_utp/src/mpi.c +++ /dev/null @@ -1,9514 +0,0 @@ -/* 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/libtorrent_utp/src/natpmp.cpp b/libtorrent_utp/src/natpmp.cpp deleted file mode 100644 index 9c1b45b0b..000000000 --- a/libtorrent_utp/src/natpmp.cpp +++ /dev/null @@ -1,643 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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" - -#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) -{ - 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", 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)); - - 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); - } -} - -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, 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 == 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, 12), 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", e.message().c_str()); - log(msg, 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)); - - // simulate packet loss -/* - if ((rand() % 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); - - char* in = m_response_buffer; - int version = read_uint8(in); - int cmd = read_uint8(in); - int result = read_uint16(in); - int time = read_uint32(in); - 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, 0, error_code(ev, get_libtorrent_category())); - l.lock(); - } - else if (m->action == mapping_t::action_add) - { - l.unlock(); - m_callback(index, 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; -#endif - if (m_disabled) return; - ptime now = time_now(); - 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/libtorrent_utp/src/packet_buffer.cpp b/libtorrent_utp/src/packet_buffer.cpp deleted file mode 100644 index b48a04e41..000000000 --- a/libtorrent_utp/src/packet_buffer.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - -Copyright (c) 2010, 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" - -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) - {} - - packet_buffer::~packet_buffer() - { - free(m_storage); - } - - void* packet_buffer::insert(index_type idx, void* value) - { - TORRENT_ASSERT_VAL(idx <= 0xffff, idx); - // you're not allowed to insert NULLs! - TORRENT_ASSERT(value); - - 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 - ((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; - } - - TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); - return old_value; - } - - void* packet_buffer::at(index_type idx) const - { - if (idx >= m_first + m_capacity) - return 0; - - if (compare_less_wrap(idx, m_first, 0xffff)) - { - return 0; - } - - return m_storage[idx & (m_capacity - 1)]; - } - - void packet_buffer::reserve(std::size_t size) - { - 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) - { - // 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; - - void* old_value = m_storage[idx & (m_capacity - 1)]; - m_storage[idx & (m_capacity - 1)] = 0; - - if (old_value) - { - --m_size; - if (m_size == 0) m_last = m_first; - } - - if (idx == m_first && m_size != 0) - { - while (!m_storage[++m_first & (m_capacity - 1)]); - m_first &= 0xffff; - } - - if (((idx + 1) & 0xffff) == m_last && m_size != 0) - { - while (!m_storage[--m_last & (m_capacity - 1)]); - ++m_last; - m_last &= 0xffff; - } - - TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); - return old_value; - } - -} - diff --git a/libtorrent_utp/src/parse_url.cpp b/libtorrent_utp/src/parse_url.cpp deleted file mode 100644 index f0f238365..000000000 --- a/libtorrent_utp/src/parse_url.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - -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. - -*/ - -#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 = 80; - - 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 (protocol == "https") port = 443; - - 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; - 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/libtorrent_utp/src/pe_crypto.cpp b/libtorrent_utp/src/pe_crypto.cpp deleted file mode 100644 index 30d550f6b..000000000 --- a/libtorrent_utp/src/pe_crypto.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/* - -Copyright (c) 2007, 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 -#elif defined TORRENT_USE_TOMMATH -extern "C" { -#include "libtorrent/tommath.h" -} -#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 - RAND_bytes((unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)); - - 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 < sizeof(m_dh_local_secret); ++i) - m_dh_local_secret[i] = rand(); - - 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: - 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(key != 0); - 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/libtorrent_utp/src/peer_connection.cpp b/libtorrent_utp/src/peer_connection.cpp deleted file mode 100644 index fbfd39970..000000000 --- a/libtorrent_utp/src/peer_connection.cpp +++ /dev/null @@ -1,5603 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#include -#include -#include - -#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" - -#ifdef TORRENT_DEBUG -#include -#endif - -//#define TORRENT_CORRUPT_DATA - -using boost::shared_ptr; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - // outbound connection - peer_connection::peer_connection( - session_impl& ses - , boost::weak_ptr tor - , shared_ptr s - , tcp::endpoint const& endp - , policy::peer* peerinfo) - : -#ifdef TORRENT_DEBUG - m_last_choke(time_now() - hours(1)) - , -#endif - m_ses(ses) - , m_max_out_request_queue(m_ses.settings().max_out_request_queue) - , 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_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_free_upload(0) - , m_downloaded_at_last_unchoke(0) - , m_uploaded_at_last_unchoke(0) - , m_disk_recv_buffer(ses, 0) - , m_socket(s) - , m_remote(endp) - , m_torrent(tor) - , m_receiving_block(piece_block::invalid) - , m_last_seen_complete(0) - , 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_peer_info(peerinfo) - , m_speed(slow) - , m_connection_ticket(-1) - , m_superseed_piece(-1) - , m_remote_bytes_dled(0) - , m_remote_dl_rate(0) - , m_outstanding_writing_bytes(0) - , m_download_rate_peak(0) - , m_upload_rate_peak(0) - , m_rtt(0) - , m_prefer_whole_pieces(0) - , m_desired_queue_size(2) - , m_choke_rejects(0) - , m_read_recurse(0) - , m_fast_reconnect(false) - , m_active(true) - , m_peer_interested(false) - , m_peer_choked(true) - , m_interesting(false) - , m_choked(true) - , m_failed(false) - , m_ignore_bandwidth_limits(false) - , m_ignore_unchoke_slots(false) - , m_have_all(false) - , m_disconnecting(false) - , m_connecting(true) - , m_queued(true) - , m_request_large_blocks(false) - , m_share_mode(false) - , m_upload_only(false) - , m_snubbed(false) - , m_bitfield_received(false) - , m_no_download(false) - , m_sent_suggests(false) - , m_holepunch_mode(false) - , m_ignore_stats(false) -#ifdef TORRENT_DEBUG - , m_in_constructor(true) - , m_disconnect_started(false) - , m_initialized(false) - , m_received_in_piece(0) -#endif - { - 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; - m_logger = m_ses.create_log(m_remote.address().to_string(ec) + "_" - + to_string(m_remote.port()).elems, m_ses.listen_port()); - (*m_logger) << time_now_string() << " *** OUTGOING CONNECTION: " - << print_endpoint(m_remote) << "\n"; - if (m_socket->get()) (*m_logger) << "uTP connection\n"; - else (*m_logger) << "TCP connection\n"; -#endif -#ifdef TORRENT_DEBUG - piece_failed = false; -#endif -#ifndef TORRENT_DISABLE_GEO_IP - m_inet_as_name = m_ses.as_name_for_ip(m_remote.address()); -#endif - - std::fill(m_peer_id.begin(), m_peer_id.end(), 0); - } - - // incoming connection - peer_connection::peer_connection( - session_impl& ses - , shared_ptr s - , tcp::endpoint const& endp - , policy::peer* peerinfo) - : -#ifdef TORRENT_DEBUG - m_last_choke(time_now() - hours(1)) - , -#endif - m_ses(ses) - , m_max_out_request_queue(m_ses.settings().max_out_request_queue) - , 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_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_free_upload(0) - , m_downloaded_at_last_unchoke(0) - , m_uploaded_at_last_unchoke(0) - , m_disk_recv_buffer(ses, 0) - , m_socket(s) - , m_remote(endp) - , m_receiving_block(piece_block::invalid) - , m_last_seen_complete(0) - , 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_peer_info(peerinfo) - , m_speed(slow) - , m_connection_ticket(-1) - , m_superseed_piece(-1) - , m_remote_bytes_dled(0) - , m_remote_dl_rate(0) - , m_outstanding_writing_bytes(0) - , m_download_rate_peak(0) - , m_upload_rate_peak(0) - , m_rtt(0) - , m_prefer_whole_pieces(0) - , m_desired_queue_size(2) - , m_choke_rejects(0) - , m_read_recurse(0) - , m_fast_reconnect(false) - , m_active(false) - , m_peer_interested(false) - , m_peer_choked(true) - , m_interesting(false) - , m_choked(true) - , m_failed(false) - , m_ignore_bandwidth_limits(false) - , m_ignore_unchoke_slots(false) - , m_have_all(false) - , m_disconnecting(false) - , m_connecting(false) - , m_queued(false) - , m_request_large_blocks(false) - , m_share_mode(false) - , m_upload_only(false) - , m_snubbed(false) - , m_bitfield_received(false) - , m_no_download(false) - , m_sent_suggests(false) - , m_holepunch_mode(false) - , m_ignore_stats(false) -#ifdef TORRENT_DEBUG - , m_in_constructor(true) - , m_disconnect_started(false) - , m_initialized(false) - , m_received_in_piece(0) -#endif - { - 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; - -#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); - m_logger = m_ses.create_log(remote().address().to_string(ec) + "_" - + to_string(remote().port()).elems, m_ses.listen_port()); - (*m_logger) << time_now_string() << " *** INCOMING CONNECTION: " - << print_endpoint(m_remote) << "\n"; - if (m_socket->get()) (*m_logger) << "uTP connection\n"; - else (*m_logger) << "TCP connection\n"; -#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); - } - -#if defined TORRENT_STATS && defined 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 = m_statistics.total_payload_download() - m_downloaded_at_last_unchoke; - d2 = rhs.m_statistics.total_payload_download() - rhs.m_downloaded_at_last_unchoke; - // divided by the number of bytes we've sent them - u1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; - u2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke; - - 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 (m_ses.settings().seed_choking_algorithm == session_settings::round_robin) - { - // in order to not switch back and forth too often, - // unchoked peers must be at least one piece ahead - // of a choked peer to be sorted at a lower unchoke-priority - int pieces = m_ses.settings().seeding_piece_quota; - bool c1_done = is_choked() || u1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024); - bool c2_done = rhs.is_choked() || u2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024); - - if (!c1_done && c2_done) return true; - if (c1_done && !c2_done) return false; - } - else if (m_ses.settings().seed_choking_algorithm == session_settings::fastest_upload) - { - size_type c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; - size_type c2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke; - - // 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 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; - } - - // 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; - } - - bool peer_connection::unchoke_compare(boost::intrusive_ptr const& p) const - { - TORRENT_ASSERT(p); - peer_connection const& rhs = *p; - - size_type c1; - size_type c2; - - // first compare how many bytes they've sent us - c1 = m_statistics.total_payload_download() - m_downloaded_at_last_unchoke; - c2 = rhs.m_statistics.total_payload_download() - rhs.m_downloaded_at_last_unchoke; - - 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 - c1 *= 1 + t1->priority(); - c2 *= 1 + t2->priority(); - - if (c1 > c2) return true; - if (c1 < c2) return false; - - if (m_ses.settings().seed_choking_algorithm == session_settings::round_robin) - { - // if they are equal, compare how much we have uploaded - c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; - c2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke; - - // in order to not switch back and forth too often, - // unchoked peers must be at least one piece ahead - // of a choked peer to be sorted at a lower unchoke-priority - int pieces = m_ses.settings().seeding_piece_quota; - bool c1_done = is_choked() || c1 > (std::max)(t1->torrent_file().piece_length() * pieces, 256 * 1024); - bool c2_done = rhs.is_choked() || c2 > (std::max)(t2->torrent_file().piece_length() * pieces, 256 * 1024); - - if (!c1_done && c2_done) return true; - if (c1_done && !c2_done) return false; - } - else if (m_ses.settings().seed_choking_algorithm == session_settings::fastest_upload) - { - c1 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; - c2 = rhs.m_statistics.total_payload_upload() - rhs.m_uploaded_at_last_unchoke; - - // take torrent priority into account - c1 *= 1 + t1->priority(); - c2 *= 1 + t2->priority(); - - if (c1 > c2) return true; - if (c2 > c1) return false; - } - // if both peers have 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; - } - - 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 = m_statistics.total_payload_upload() - m_uploaded_at_last_unchoke; - c2 = p->m_statistics.total_payload_upload() - p->m_uploaded_at_last_unchoke; - - // 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_unchoke = m_statistics.total_payload_download(); - m_uploaded_at_last_unchoke = 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 (!t) - { - 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); - TORRENT_ASSERT(m_remote.address() != address_v4::any()); - if (ec) - { - disconnect(ec); - return; - } - if (m_remote.address().is_v4()) - m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); - } - else if (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) return; - if (!t->ready_for_connections()) return; - - bool interested = false; - if (!t->is_finished()) - { - 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; - break; - } - } - } - 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, ...) - { - 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.push_back(i); - } - 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.push_back(piece); - 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); - } - - if (m_num_pieces == int(m_have_piece.size())) - { -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("*** on_metadata(): THIS IS A SEED ***"); -#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(); - disconnect_if_redundant(); - if (m_disconnecting) return; - - on_metadata(); - if (m_disconnecting) return; - - if (!t->is_finished()) - t->get_policy().peer_is_interesting(*this); - - return; - } - TORRENT_ASSERT(!m_have_all); - - on_metadata(); - if (m_disconnecting) return; - - // let the torrent know which pieces the - // peer has - // if we're a seed, we don't keep track of piece availability - bool interesting = false; - if (!t->is_seed()) - { - t->peer_has(m_have_piece); - - for (int i = 0; i < (int)m_have_piece.size(); ++i) - { - if (m_have_piece[i]) - { - if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) - interesting = true; - } - } - } - - if (interesting) t->get_policy().peer_is_interesting(*this); - else if (upload_only()) disconnect(errors::upload_upload_connection); - } - - 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(); -#ifdef TORRENT_DEBUG - 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 ***"); -#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(); - if (t->is_finished()) 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); - 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); - - 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()); -#ifdef TORRENT_DEBUG - 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); - - boost::shared_ptr t = m_torrent.lock(); -#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->is_sequential_download()) - { - ret |= piece_picker::sequential | piece_picker::ignore_whole_pieces; - } - 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(bool(ret & piece_picker::rarest_first) - + bool(ret & piece_picker::sequential) <= 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 = 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; - } - } - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log(" ==> HAVE [ piece: %d ]", index); -#endif - write_have(index); -#ifdef TORRENT_DEBUG - boost::shared_ptr t = m_torrent.lock(); - TORRENT_ASSERT(t); - TORRENT_ASSERT(t->have_piece(index)); -#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 - { - int rate = m_statistics.transfer_rate(stat::download_payload) - + m_statistics.transfer_rate(stat::download_protocol); - // avoid division by zero - if (rate < 50) rate = 50; - boost::shared_ptr t = m_torrent.lock(); - TORRENT_ASSERT(t); - return seconds((m_outstanding_bytes + m_queued_time_critical * t->block_size()) / 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) - { - INVARIANT_CHECK; - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { -#ifdef BOOST_NO_EXCEPTIONS - (*i)->on_piece_pass(index); -#else - try { (*i)->on_piece_pass(index); } catch (std::exception&) {} -#endif - } -#endif - } - - void peer_connection::received_invalid_data(int index) - { - INVARIANT_CHECK; - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { -#ifdef BOOST_NO_EXCEPTIONS - (*i)->on_piece_failed(index); -#else - try { (*i)->on_piece_failed(index); } catch (std::exception&) {} -#endif - } -#endif - if (is_disconnecting()) return; - - if (peer_info_struct()) - { - if (m_ses.settings().use_parole_mode) - peer_info_struct()->on_parole = true; - - int hashfails = peer_info_struct()->hashfails; - int trust_points = peer_info_struct()->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; - peer_info_struct()->trust_points = trust_points; - if (hashfails > 255) hashfails = 255; - peer_info_struct()->hashfails = hashfails; - } - } - - size_type peer_connection::total_free_upload() const - { - return m_free_upload; - } - - void peer_connection::add_free_upload(size_type free_upload) - { - INVARIANT_CHECK; - - m_free_upload += free_upload; - } - - // 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) - { - 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 (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 - disconnect(errors::invalid_info_hash, 2); - return; - } - - if ((t->is_paused() && (!t->is_auto_managed() - || !m_ses.m_settings.incoming_starts_queued_torrents)) - || t->has_error()) - { - // 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; - - 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()); - } - - // 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; - - 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->is_seed()) - { - 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 (b.piece_index != r.piece) return false; - if (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); - -#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())); - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("<== REJECT_PIECE [ piece: %d | s: %d | l: %d ]" - , r.piece, r.start, r.length); -#endif - - 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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - 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) - { - 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_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 (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()) - { - 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 (is_choked()) - { - 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 - && (t->ratio() == 0 - || share_diff() >= size_type(-free_upload_amount) - || t->is_finished())) - { - // 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 - { - std::string reason; - if (m_ses.num_uploads() >= m_ses.settings().unchoke_slots_limit) - { - 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); - } - else - { - peer_log("DID NOT UNCHOKE [ the share ratio (%d) is <= " - "free_upload_amount (%d) and we are not seeding and the ratio (%d) is non-zero" - , share_diff(), int(free_upload_amount), t->ratio()); - } - } -#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; - } - } - - if (t->ratio() != 0.f) - { - TORRENT_ASSERT(share_diff() < (std::numeric_limits::max)()); - size_type diff = share_diff(); - if (diff > 0 && is_seed()) - { - // the peer is a seed and has sent - // us more than we have sent it back. - // consider the download as free download - t->add_free_upload(diff); - add_free_upload(-diff); - } - } - - if (t->super_seeding() && m_superseed_piece != -1) - { - // assume the peer has the piece we're superseeding to it - // and give it another one - if (!m_have_piece[m_superseed_piece]) incoming_have(m_superseed_piece); - } - } - - // ----------------------------- - // ----------- 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 < 65536) - { - // 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 (m_superseed_piece == index) - { - superseed_piece(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; - - // 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 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_bytes_dled += t->torrent_file().piece_size(index); - } - - // 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()) - { - t->seen_complete(); - t->get_policy().set_seed(m_peer_info, true); - m_upload_only = true; - disconnect_if_redundant(); - if (is_disconnecting()) return; - } - - if (!t->have_piece(index) - && !t->is_seed() - && !is_interesting() - && t->picker().piece_priority(index) != 0) - t->get_policy().peer_is_interesting(*this); - - // 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 - && (index != m_superseed_piece || t->num_peers() == 1)) - { - for (torrent::peer_iterator i = t->begin() - , end(t->end()); i != end; ++i) - { - peer_connection* p = *i; - if (p->superseed_piece() != index) continue; - if (!p->has_piece(index)) continue; - p->superseed_piece(t->get_piece_to_super_seed(p->get_bitfield())); - } - } - } - - // ----------------------------- - // --------- 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; - } - - 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()) - { - 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 ***"); -#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(); - if (!t->is_finished()) - 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 - bool interesting = false; - if (!t->is_seed()) - { - t->peer_has(bits); - - for (int i = 0; i < (int)m_have_piece.size(); ++i) - { - bool have = bits[i]; - if (have && !m_have_piece[i]) - { - if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) - interesting = true; - } - else if (!have && m_have_piece[i]) - { - // this should probably not be allowed - t->peer_lost(i); - } - } - } - - m_have_piece = bits; - m_num_pieces = num_pieces; - - if (interesting) t->get_policy().peer_is_interesting(*this); - else if (upload_only()) disconnect(errors::upload_upload_connection); - } - - 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; - - // 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_finished()) - { - disconnect(errors::upload_upload_connection); - return; - } - - if (m_upload_only - && !m_interesting - && m_bitfield_received - && t->are_files_checked()) - { - disconnect(errors::uninteresting_upload_peer); - return; - } - } - - // ----------------------------- - // ---------- REQUEST ---------- - // ----------------------------- - - void peer_connection::incoming_request(peer_request const& r) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - TORRENT_ASSERT(t); - - if (m_superseed_piece != -1 - && r.piece != m_superseed_piece) - { - ++m_num_invalid_requests; -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("<== INVALID_SUPER_SEED_REQUEST [ " - "piece: %d | s: %d | l: %d | i: %d | " - "t: %d | n: %d | h: %d | ss: %d ]" - , r.piece, r.start, r.length, m_peer_interested - , int(t->torrent_file().piece_size(r.piece)) - , t->torrent_file().num_pieces() - , t->have_piece(r.piece) - , m_superseed_piece); -#endif - - 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()) - { - // if we don't have valid metadata yet, - // we shouldn't get a request -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() - << " <== UNEXPECTED_REQUEST [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " ]\n"; - - (*m_logger) << time_now_string() - << " ==> REJECT_PIECE [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " ]\n"; -#endif - write_reject_request(r); - return; - } - - if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue) - { - // 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 - (*m_logger) << time_now_string() - << " <== TOO MANY REQUESTS [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " ]\n"; - - (*m_logger) << time_now_string() - << " ==> REJECT_PIECE [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " ]\n"; -#endif - write_reject_request(r); - return; - } - - // 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()) - { -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("<== REQUEST [ piece: %d | s: %d | l: %d ]" - , r.piece, r.start, r.length); -#endif - // if we have choked the client - // ignore the request - if (m_choked && std::find(m_accept_fast.begin(), m_accept_fast.end() - , r.piece) == m_accept_fast.end()) - { - write_reject_request(r); - ++m_choke_rejects; -#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 - - if (m_choke_rejects > m_ses.settings().max_rejects) - { - disconnect(errors::too_many_requests_when_choked); - return; - } - else if ((m_choke_rejects & 0xf) == 0) - { - // tell the peer it's choked again - // every 16 requests in a row - write_choke(); - } - } - else - { - m_choke_rejects = 0; - m_requests.push_back(r); - m_last_incoming_request = time_now(); - fill_send_buffer(); - } - } - else - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() - << " <== INVALID_REQUEST [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " | " - "h: " << t->have_piece(r.piece) << " | " - "block_limit: " << t->block_size() << " ]\n"; - - (*m_logger) << time_now_string() - << " ==> REJECT_PIECE [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " ]\n"; -#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; -#ifdef TORRENT_DEBUG - boost::shared_ptr t = associated_torrent().lock(); - TORRENT_ASSERT(m_received_in_piece + bytes <= t->block_size()); - m_received_in_piece += bytes; -#endif -#if !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - check_invariant(); -#endif - } - - void peer_connection::start_receive_piece(peer_request const& r) - { -#if !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - check_invariant(); -#endif -#ifdef TORRENT_DEBUG - 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); - piece_block b(r.piece, r.start / t->block_size()); - m_receiving_block = b; - - if (!verify_piece(r)) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() - << " <== INVALID_PIECE [ piece: " << r.piece << " | " - "start: " << r.start << " | " - "length: " << r.length << " ]\n"; -#endif - disconnect(errors::invalid_piece, 2); - return; - } - - 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; - 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_index = -1; - m_receiving_block.block_index = -1; - -#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)) - { -#ifdef TORRENT_DEBUG - 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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS - t->check_invariant(); -#endif -#endif - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("<== PIECE [ piece: %d | s: %d | l: %d | ds: %d | qs: %d | q: %d ]" - , p.piece, p.start, p.length, statistics().download_rate() - , int(m_desired_queue_size), int(m_download_queue.size())); -#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()) - { -#ifdef TORRENT_DEBUG - 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); - return; - } - - ptime now = time_now(); - - piece_picker& picker = t->picker(); - piece_manager& fs = t->filesystem(); - - std::vector finished_blocks; - 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 -#ifdef TORRENT_DEBUG - TORRENT_ASSERT(m_received_in_piece == p.length); - m_received_in_piece = 0; -#endif - t->add_redundant_bytes(p.length); - return; - } - -#ifdef TORRENT_DEBUG - 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 - (*m_logger) << time_now_string() - << " *** SKIPPED_PIECE [ piece: " << qe.block.piece_index << " | " - "b: " << qe.block.block_index << " | skip: " << qe.skipped << " | " - "dqs: " << int(m_desired_queue_size) << "] ***\n"; -#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) - { - 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 - (*m_logger) << time_now_string() - << " *** DROPPED_PIECE [ piece: " << qe.block.piece_index << " | " - "b: " << qe.block.block_index << " | skip: " << qe.skipped << " | " - "dqs: " << int(m_desired_queue_size) << "] ***\n"; -#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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - check_invariant(); -#endif - } - } - TORRENT_ASSERT(int(m_download_queue.size()) > block_index); - b = m_download_queue.begin() + block_index; - TORRENT_ASSERT(*b == pending_b); - -#ifdef TORRENT_DEBUG - TORRENT_ASSERT(m_received_in_piece == p.length); - m_received_in_piece = 0; -#endif - // if the block we got is already finished, then ignore it - if (picker.is_downloaded(block_finished)) - { - t->add_redundant_bytes(p.length); - - m_download_queue.erase(b); - m_timeout_extend = 0; - - if (!m_download_queue.empty()) - m_requested = now; - - 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)); - } - } - - fs.async_write(p, data, boost::bind(&peer_connection::on_disk_write_complete - , self(), _1, _2, p, t)); - m_outstanding_writing_bytes += p.length; - TORRENT_ASSERT(m_channel_state[download_channel] == peer_info::bw_idle); - m_download_queue.erase(b); - - if (t->filesystem().queued_bytes() >= m_ses.settings().max_queued_disk_bytes - && m_ses.settings().max_queued_disk_bytes - && t->alerts().should_post()) - { - 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 defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS \ - && defined TORRENT_EXPENSIVE_INVARIANT_CHECKS - t->check_invariant(); -#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)); - } - - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - - 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 - // in case the outstanding bytes just dropped down - // to allow to receive more data - setup_receive(); - - piece_block block_finished(p.piece, p.start / t->block_size()); - - if (ret == -1 || !t) - { - if (!t) - { - disconnect(j.error); - return; - } - - // handle_disk_error may disconnect us - t->handle_disk_error(j, this); - return; - } - - if (t->is_seed()) 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()) - { - m_requests.erase(i); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> REJECT_PIECE [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " ]\n"; -#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; - - m_have_all = true; - - t->get_policy().set_seed(m_peer_info, true); - m_upload_only = true; - m_bitfield_received = true; - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("*** THIS IS A SEED ***"); -#endif - - // 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(); - - // if we're finished, we're not interested - if (t->is_finished()) 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; - t->get_policy().set_seed(m_peer_info, false); - m_bitfield_received = 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; - return true; - } - - void 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; - // ignore it if it's already time critical - if (rit - m_request_queue.begin() < m_queued_time_critical) return; - 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; - } - - 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.piece_index >= 0); - TORRENT_ASSERT(block.piece_index < t->torrent_file().num_pieces()); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(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) - { - // 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 - 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; - 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 - (*m_logger) << time_now_string() - << " ==> CANCEL [ piece: " << b.piece_index << " | s: " - << block_offset << " | l: " << block_size << " | " << b.block_index << " ]\n"; -#endif - write_cancel(r); - } - } - - void peer_connection::cancel_request(piece_block const& block) - { - 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.piece_index >= 0); - TORRENT_ASSERT(block.piece_index < t->torrent_file().num_pieces()); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(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; - - 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()); - - 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 - (*m_logger) << time_now_string() - << " ==> CANCEL [ piece: " << block.piece_index << " | s: " - << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; -#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; - write_choke(); - m_choked = true; - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("==> CHOKE"); -#endif -#ifdef TORRENT_DEBUG - m_last_choke = time_now(); -#endif - 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; - write_reject_request(r); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> REJECT_PIECE [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " ]\n"; -#endif - 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) - send_suggest(*i); - - m_sent_suggests = true; - } - - m_last_unchoke = time_now(); - write_unchoke(); - m_choked = false; - -#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; - } - - 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->is_seed()) 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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - 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 - (*m_logger) << time_now_string() - << " *** MERGING REQUEST ** [ " - "piece: " << block.block.piece_index << " | " - "block: " << block.block.block_index << " ]\n"; -#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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - 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()); - - TORRENT_ASSERT(m_connecting); - connect_failed(errors::timed_out); - } - - void peer_connection::connect_failed(error_code const& e) - { - TORRENT_ASSERT(m_connecting); - 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) << "CONNECTION FAILED: " << print_endpoint(m_remote) << "\n"; -#endif - - if (m_connection_ticket != -1) - { - m_ses.m_half_open.done(m_connection_ticket); - m_connecting = false; - } - - // 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 (m_socket->get() - && 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); - - if ((!m_socket->get() || !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); - } - - 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) - { -#ifdef TORRENT_DEBUG - m_disconnect_started = true; -#endif - -#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 - // we cannot do this in a constructor - TORRENT_ASSERT(m_in_constructor == false); - if (error > 0) m_failed = true; - if (m_disconnecting) return; - boost::intrusive_ptr me(this); - - INVARIANT_CHECK; - - if (m_connecting && m_connection_ticket >= 0) - { - m_ses.m_half_open.done(m_connection_ticket); - m_connection_ticket = -1; - } - - boost::shared_ptr t = m_torrent.lock(); - torrent_handle handle; - if (t) handle = t->get_handle(); - - if (ec) - { - if ((error > 1 - || ec.category() == get_libtorrent_category() - || ec.category() == 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); - } - } - - 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 !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG - 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; - m_socket->close(e); - 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); - } - - size_type peer_connection::share_diff() const - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - TORRENT_ASSERT(t); - - float ratio = t->ratio(); - - // if we have an infinite ratio, just say we have downloaded - // much more than we have uploaded. And we'll keep uploading. - if (ratio == 0.f) - return (std::numeric_limits::max)(); - - return m_free_upload - + static_cast(m_statistics.total_payload_download() * ratio) - - m_statistics.total_payload_upload(); - } - - 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 = 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]; -#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.load_balancing = total_free_upload(); - - p.download_queue_length = int(download_queue().size() + m_request_queue.size()); - p.requests_in_buffer = int(m_requests_in_buffer.size()); - p.target_dl_queue_length = int(desired_queue_size()); - p.upload_queue_length = int(upload_queue().size()); - - 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_holepunch_mode ? peer_info::holepunched : 0; - if (peer_info_struct()) - { - policy::peer* pi = peer_info_struct(); - 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->first; -#endif - } - else - { - p.source = 0; - p.failcount = 0; - p.num_hashfails = 0; -#ifndef TORRENT_DISABLE_GEO_IP - p.inet_as = 0xffff; -#endif - } - - 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 = p.pieces.count() * 1000000 / p.pieces.size(); - } - - p.estimated_reciprocation_rate = m_est_reciprocation_rate; - int upload_capacity = m_ses.settings().upload_rate_limit; - if (upload_capacity == 0) - upload_capacity = (std::max)(20000, m_ses.m_peak_up_rate + 10000); - - 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); - - if (size > 0) - std::memmove(&m_recv_buffer[0] + offset, &m_recv_buffer[0] + offset + size, m_recv_pos - size - offset); - - m_recv_pos -= size; - -#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 index) - { - if (index == -1) - { - if (m_superseed_piece == -1) return; - m_superseed_piece = -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(index)); - -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("==> HAVE [ piece: %d ] (super seed)", index); -#endif - write_have(index); - m_superseed_piece = index; - } - - void peer_connection::update_desired_queue_size() - { - if (m_snubbed) - { - m_desired_queue_size = 1; - return; - } - - int download_rate = statistics().download_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; - } - - 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) - { - m_ses.m_half_open.done(m_connection_ticket); - m_connecting = false; - disconnect(errors::torrent_aborted); - return; - } - - 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 = now - m_last_receive; - // 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 = can_read() -; - if (may_timeout && d > seconds(m_timeout) && !m_connecting) - { -#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_finished() - && d > seconds(20)) - { -#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()))) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " *** MUTUAL NO INTEREST [ " - "t1: " << total_seconds(d1) << " | " - "t2: " << total_seconds(d2) << " ] ***\n"; -#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 - (*m_logger) << time_now_string() - << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size() - << " time: " << total_seconds(now - m_last_piece) - << " to: " << piece_timeout - << " extened: " << m_timeout_extend << " ] ***\n"; -#endif - - snub_peer(); - } - - // If the client sends more data - // we send it data faster, otherwise, slower. - // It will also depend on how much data the - // client has sent us. This is the mean to - // maintain the share ratio given by m_ratio - // with all peers. - - if (t->is_finished() || is_choked() || t->ratio() == 0.0f) - { - // if we have downloaded more than one piece more - // than we have uploaded OR if we are a seed - // have an unlimited upload rate - m_bandwidth_channel[upload_channel].throttle(m_upload_limit); - } - else - { - size_type bias = 0x10000 + 2 * t->block_size() + m_free_upload; - - const int break_even_time = 15; // seconds. - size_type have_uploaded = m_statistics.total_payload_upload(); - size_type have_downloaded = m_statistics.total_payload_download(); - int download_speed = m_statistics.download_rate(); - - size_type soon_downloaded = - have_downloaded + (size_type)(download_speed * (break_even_time + break_even_time / 2)); - - if (t->ratio() != 1.f) - soon_downloaded = size_type(soon_downloaded * t->ratio()); - - int upload_speed_limit = (soon_downloaded - have_uploaded - + bias) / break_even_time; - - if (m_upload_limit > 0 && m_upload_limit < upload_speed_limit) - upload_speed_limit = m_upload_limit; - - upload_speed_limit = (std::min)(upload_speed_limit, (std::numeric_limits::max)()); - - m_bandwidth_channel[upload_channel].throttle( - (std::min)((std::max)(upload_speed_limit, 10), m_upload_limit)); - } - - // update once every minute - if (now - m_remote_dl_update >= seconds(60)) - { - if (m_remote_dl_rate > 0) - m_remote_dl_rate = (m_remote_dl_rate * 2 / 3) + - ((m_remote_bytes_dled / 3) / 60); - else - m_remote_dl_rate = m_remote_bytes_dled / 60; - - m_remote_bytes_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(); - - int prev_request_queue = m_request_queue.size(); - - // 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; - request_a_block(*t, *this); - m_desired_queue_size = 1; - - piece_block r(piece_block::invalid); - // time out the last request in the queue - if (prev_request_queue > 0) - { - std::vector::iterator i - = m_request_queue.begin() + (prev_request_queue - 1); - r = i->block; - m_request_queue.erase(i); - if (prev_request_queue <= m_queued_time_critical) - --m_queued_time_critical; - } - else - { - TORRENT_ASSERT(!m_download_queue.empty()); - pending_block& qe = m_download_queue.back(); - if (!qe.timed_out && !qe.not_wanted) - 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; - } - if (!m_download_queue.empty() || !m_request_queue.empty()) - m_timeout_extend += m_ses.settings().request_timeout; - - if (r != piece_block::invalid) - 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_finished()) 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) 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! - - int upload_rate = int(m_statistics.upload_rate()); - - int buffer_size_watermark = upload_rate - * m_ses.settings().send_buffer_watermark_factor; - - if (buffer_size_watermark < 512) buffer_size_watermark = 512; - else if (buffer_size_watermark > m_ses.settings().send_buffer_watermark) - { - buffer_size_watermark = m_ses.settings().send_buffer_watermark; - } - - 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) - { - 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 (ret != r.length || m_torrent.expired()) - { - if (!t) - { - disconnect(j.error); - return; - } - - if (ret == -3) - { - if (t->seed_mode()) t->leave_seed_mode(false); - write_reject_request(r); - } - 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); - } - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start - << " | l: " << r.length << " ]\n"; -#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); - setup_send(); - } - - 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); - m_quota[channel] += amount; - TORRENT_ASSERT(m_channel_state[channel] == peer_info::bw_limit); - m_channel_state[channel] = peer_info::bw_idle; - if (channel == upload_channel) - { - setup_send(); - } - else if (channel == download_channel) - { - setup_receive(); - } - } - - void peer_connection::request_upload_bandwidth( - bandwidth_channel* bwc1 - , bandwidth_channel* bwc2 - , bandwidth_channel* bwc3 - , bandwidth_channel* bwc4) - { - shared_ptr t = m_torrent.lock(); - int priority; - if (m_ses.m_settings.choking_algorithm == session_settings::bittyrant_choker - && !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); - } - priority = (boost::uint64_t(m_est_reciprocation_rate) << 10) / upload_capacity; - } - else - { - priority = 1 + is_interesting() * 2 + m_requests_in_buffer.size(); - if (priority > 255) priority = 255; - priority += t->priority() << 8; - } - TORRENT_ASSERT(priority < 0xffff); - - // peers that we are not interested in are non-prioritized - m_channel_state[upload_channel] = peer_info::bw_limit; - m_ses.m_upload_rate.request_bandwidth(self() - , (std::max)(m_send_buffer.size(), m_statistics.upload_rate() * 2 - / (1000 / m_ses.m_settings.tick_interval)) - , priority - , bwc1, bwc2, bwc3, bwc4); -#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 - } - - void peer_connection::request_download_bandwidth( - bandwidth_channel* bwc1 - , bandwidth_channel* bwc2 - , bandwidth_channel* bwc3 - , bandwidth_channel* bwc4) - { - 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->priority() << 8); - - TORRENT_ASSERT(m_channel_state[download_channel] == peer_info::bw_idle - || m_channel_state[download_channel] == peer_info::bw_disk); - TORRENT_ASSERT(m_outstanding_bytes >= 0); - m_channel_state[download_channel] = peer_info::bw_limit; - m_ses.m_download_rate.request_bandwidth(self() - , (std::max)((std::max)(m_outstanding_bytes, m_packet_size - m_recv_pos) + 30 - , m_statistics.download_rate() * 2 - / (1000 / m_ses.m_settings.tick_interval)) - , priority , bwc1, bwc2, bwc3, bwc4); - } - - void peer_connection::setup_send() - { - if (m_channel_state[upload_channel] != peer_info::bw_idle - && m_channel_state[upload_channel] != peer_info::bw_disk) return; - - shared_ptr t = m_torrent.lock(); - - if (m_quota[upload_channel] == 0 - && !m_send_buffer.empty() - && !m_connecting - && t) - { - if (!m_ignore_bandwidth_limits) - { - bool utp = m_socket->get(); - - // 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_settings.rate_limit_utp || !utp) ? &m_ses.m_upload_channel : 0 - , &t->m_bandwidth_channel[upload_channel] - , &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]); - } - return; - } - - int quota_left = m_quota[upload_channel]; - - if (m_send_buffer.empty() - && m_reading_bytes > 0 - && quota_left > 0) - { - 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 (t->alerts().should_post()) - { - t->alerts().post_alert(performance_alert(t->get_handle() - , performance_alert::send_buffer_watermark_too_low)); - } - } - } - - 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); - -#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::setup_receive(sync_t sync) - { - INVARIANT_CHECK; - - if (m_channel_state[download_channel] != peer_info::bw_idle - && m_channel_state[download_channel] != peer_info::bw_disk) return; - - shared_ptr t = m_torrent.lock(); - - if (m_quota[download_channel] == 0 - && !m_connecting - && t) - { - if (!m_ignore_bandwidth_limits) - { - bool utp = m_socket->get(); - - // 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_settings.rate_limit_utp || !utp) ? &m_ses.m_download_channel : 0 - , &t->m_bandwidth_channel[download_channel] - , &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]); - } - return; - } - - if (!can_read(&m_channel_state[download_channel])) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " *** CANNOT READ [" - " quota: " << m_quota[download_channel] << - " ignore: " << (m_ignore_bandwidth_limits?"yes":"no") << - " queue-size: " << ((t && t->get_storage())?t->filesystem().queued_bytes():0) << - " queue-limit: " << m_ses.settings().max_queued_disk_bytes << - " disconnecting: " << (m_disconnecting?"yes":"no") << - " ]\n"; -#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; - - if (sync == read_sync && m_read_recurse < 10) - { - size_t bytes_transferred = try_read(read_sync, ec); - - if (ec != asio::error::would_block) - { -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("peer_connection::on_receive_data"); -#endif - ++m_read_recurse; - m_channel_state[download_channel] = peer_info::bw_network; - on_receive_data(ec, bytes_transferred); - --m_read_recurse; - return; - } - } - -#ifdef TORRENT_VERBOSE_LOGGING - if (m_read_recurse >= 10) - { - peer_log("*** reached recursion limit"); - } -#endif - 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(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) - { - 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); -#ifdef TORRENT_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) - { - 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) - { - m_send_buffer.append(buf, free_space); - 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; - - std::pair buffer = m_ses.allocate_buffer(size); - if (buffer.first == 0) - { - disconnect(errors::no_memory); - return; - } - TORRENT_ASSERT(buffer.second >= size); - std::memcpy(buffer.first, buf, size); - m_send_buffer.append_buffer(buffer.first, buffer.second, size - , boost::bind(&session_impl::free_buffer, boost::ref(m_ses), _1, buffer.second)); -#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 - setup_send(); - } - -// TODO: change this interface to automatically call setup_send() when the -// return value is destructed - buffer::interval peer_connection::allocate_send_buffer(int size) - { - TORRENT_ASSERT(size > 0); - char* insert = m_send_buffer.allocate_appendix(size); - if (insert == 0) - { - std::pair buffer = m_ses.allocate_buffer(size); - if (buffer.first == 0) - { - disconnect(errors::no_memory); - return buffer::interval(0, 0); - } - TORRENT_ASSERT(buffer.second >= size); - m_send_buffer.append_buffer(buffer.first, buffer.second, size - , boost::bind(&session_impl::free_buffer, boost::ref(m_ses), _1, buffer.second)); - buffer::interval ret(buffer.first, buffer.first + size); -#if defined TORRENT_STATS && defined TORRENT_DISK_STATS - m_ses.m_buffer_usage_logger << log_time() << " allocate_buffer_alloc: " << size << std::endl; - m_ses.log_buffer_usage(); -#endif - return ret; - } - else - { -#if defined TORRENT_STATS && defined TORRENT_DISK_STATS - m_ses.m_buffer_usage_logger << log_time() << " allocate_buffer: " << size << std::endl; - m_ses.log_buffer_usage(); -#endif - buffer::interval ret(insert, insert + size); - return ret; - } - } - - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " *** ON_RECEIVE_DATA [" - " bytes: " << bytes_transferred << - " error: " << error.message() << - " ]\n"; -#endif -#if defined TORRENT_ASIO_DEBUGGING - complete_async("peer_connection::on_receive_data"); -#endif - // keep ourselves alive in until this function exits in - // case we disconnect - boost::intrusive_ptr me(self()); - - TORRENT_ASSERT(m_channel_state[download_channel] == peer_info::bw_network); - m_channel_state[download_channel] = peer_info::bw_idle; - - 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 - (*m_logger) << time_now_string() << " **ERROR**: " - << error.message() << "[in peer_connection::on_receive_data]\n"; -#endif - m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6()); - on_receive(error, bytes_transferred); - disconnect(error); - return; - } - - int max_receive = 0; - int num_loops = 0; - do - { - TORRENT_ASSERT(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)); - -#ifdef TORRENT_DEBUG - 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); - } -#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 == 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) - { - buffer(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()); - 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(char* 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 - || !t || t->get_storage() == 0 - || t->filesystem().queued_bytes() < m_ses.settings().max_queued_disk_bytes; - - if (!disk) - { - if (state) *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()); -#ifdef TORRENT_DEBUG - // 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 - - m_connection_ticket = ticket; - boost::shared_ptr t = m_torrent.lock(); - - m_queued = false; - TORRENT_ASSERT(m_connecting); - - if (!t) - { - disconnect(errors::torrent_aborted); - return; - } - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " OPEN " << (m_remote.address().is_v4()?"IPv4":"IPv6") << "\n"; -#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 - (*m_logger) << time_now_string() << " SET REUSE ADDRESS\n"; -#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 - (*m_logger) << time_now_string() << " BIND: " << print_endpoint(bind_interface) << "\n"; -#endif - m_socket->bind(bind_interface, ec); - if (ec) - { - disconnect(ec); - return; - } -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " ASYNC_CONNECT: " << print_endpoint(m_remote) << "\n"; -#endif -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " ASYNC_CONNECT: " << print_endpoint(m_remote) << "\n"; -#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(); - 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())); - } - } - - 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(); - - TORRENT_ASSERT(m_ses.is_network_thread()); - - INVARIANT_CHECK; - - m_rtt = total_milliseconds(completed - m_connect); - - if (m_disconnecting) return; - - error_code ec; - if (e) - { - connect_failed(e); - return; - } - - m_connecting = false; - m_ses.m_half_open.done(m_connection_ticket); - - if (m_disconnecting) return; - m_last_receive = time_now(); - - if (m_socket->get() && 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 || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " COMPLETED: " << print_endpoint(m_remote) - << " rtt = " << m_rtt << "\n"; -#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 || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " SET NON-BLOCKING\n"; -#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 - boost::shared_ptr t = m_torrent.lock(); - 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()) - { - error_code ec; - m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec); - } - - on_connected(); - setup_send(); - setup_receive(); - } - - // -------------------------- - // SEND DATA - // -------------------------- - - void peer_connection::on_send_data(error_code const& error - , std::size_t bytes_transferred) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " *** ON_SEND_DATA [" - " bytes: " << bytes_transferred << - " error: " << error.message() << - " ]\n"; -#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_idle; - - 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(); - -#ifdef TORRENT_DEBUG - 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); -#ifdef TORRENT_DEBUG - 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(); - } - -#ifdef TORRENT_DEBUG - 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_queued_time_critical <= m_request_queue.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); - - boost::shared_ptr t = m_torrent.lock(); - 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); - int last_block_size = t->torrent_file().piece_size(ti.num_pieces()-1) - last_block.block_index * 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); - } - - if (m_channel_state[download_channel] == peer_info::bw_limit) - TORRENT_ASSERT(m_quota[download_channel] == 0); - if (m_channel_state[upload_channel] == peer_info::bw_limit) - TORRENT_ASSERT(m_quota[upload_channel] == 0); - - 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()) - { - // make sure upload only peers are disconnected - if (t->is_finished() && m_upload_only) - TORRENT_ASSERT(m_disconnect_started); - if (m_upload_only - && !m_interesting - && m_bitfield_received - && t->are_files_checked()) - TORRENT_ASSERT(m_disconnect_started); - } - - if (!m_disconnect_started && m_initialized) - { - // none of this matters if we're disconnecting anyway - if (t->is_finished()) - TORRENT_ASSERT(!m_interesting); - if (is_seed()) - TORRENT_ASSERT(m_upload_only); - } - - 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; - int picker_count = t->picker().num_peers(b); - if (!t->picker().is_downloaded(b)) - TORRENT_ASSERT(picker_count == count); - } - } -#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS - if (m_peer_info) - { - 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; - } -/* -// this invariant is not valid anymore since the completion event -// might be queued in the io service - if (complete && !piece_failed) - { - disk_io_job ret = m_ses.m_disk_thread.find_job( - &t->filesystem(), -1, i->index); - TORRENT_ASSERT(ret.action == disk_io_job::hash || ret.action == disk_io_job::write); - TORRENT_ASSERT(ret.piece == i->index); - } -*/ - } - } - -// 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_idle) return; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " ==> KEEPALIVE\n"; -#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/libtorrent_utp/src/piece_picker.cpp b/libtorrent_utp/src/piece_picker.cpp deleted file mode 100644 index 32bbc695d..000000000 --- a/libtorrent_utp/src/piece_picker.cpp +++ /dev/null @@ -1,2397 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#include -#include -#include -#include - -#include "libtorrent/piece_picker.hpp" -#include "libtorrent/aux_/session_impl.hpp" -#include "libtorrent/bitfield.hpp" - -#ifdef TORRENT_DEBUG -#include "libtorrent/peer_connection.hpp" -#include "libtorrent/torrent.hpp" -#endif - -#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(0x3FFFF, 0x3FFF); - - 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_dirty(false) - { -#ifdef TORRENT_PICKER_LOG - std::cerr << "new piece_picker" << std::endl; -#endif -#ifdef TORRENT_DEBUG - 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; - } - - 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 = std::find_if( - m_downloads.begin(), m_downloads.end() - , boost::bind(&downloading_piece::index, _1) == index); - TORRENT_ASSERT(piece != m_downloads.end()); - st = *piece; - st.info = 0; - 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 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]; - } - } - m_downloads.push_back(downloading_piece()); - downloading_piece& ret = m_downloads.back(); - ret.info = &m_block_info[block_index]; - 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; - } - return ret; - } - - 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); - } - -#ifdef TORRENT_DEBUG - - 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 < int(bits.size())); - TORRENT_ASSERT(bits[i->piece_index]); - TORRENT_ASSERT(!m_piece_map[i->piece_index].have()); - } - } - - 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 - - void piece_picker::check_invariant(const torrent* t) const - { - TORRENT_ASSERT(sizeof(piece_pos) == 4); - 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); - } - } - - 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) - { - 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); - } - 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; - } - 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 = rand() % (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); - -#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 = rand() % (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 - = std::find_if(m_downloads.begin(), m_downloads.end() - , has_index(index)); - - TORRENT_ASSERT(i != m_downloads.end()); -#ifdef TORRENT_DEBUG - int num_blocks = blocks_in_piece(i->index); - for (int k = 0; k < num_blocks; ++k) - { - 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() - { -#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; - } - } - - void piece_picker::dec_refcount_all() - { -#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; - } - return; - } - TORRENT_ASSERT(m_seeds == 0); - - for (std::vector::iterator i = m_piece_map.begin() - , end(m_piece_map.end()); i != end; ++i) - { - TORRENT_ASSERT(i->peer_count > 0); - --i->peer_count; - } - - m_dirty = true; - } - - void piece_picker::inc_refcount(int index) - { -#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS - TORRENT_PIECE_PICKER_INVARIANT_CHECK; -#endif - - piece_pos& p = m_piece_map[index]; - - 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); - } - - void piece_picker::dec_refcount(int index) - { -#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS - TORRENT_PIECE_PICKER_INVARIANT_CHECK; -#endif - - piece_pos& p = m_piece_map[index]; - 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) - { -#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) - { - ++m_piece_map[index].peer_count; - updated = true; - } - } - - if (updated) m_dirty = true; - } - - void piece_picker::dec_refcount(bitfield const& bitmask) - { -#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) - { - --m_piece_map[index].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 - = std::find_if(m_downloads.begin() - , m_downloads.end() - , has_index(index)); - TORRENT_ASSERT(i != m_downloads.end()); - erase_download_piece(i); - } - - TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() - , has_index(index)) == m_downloads.end()); - - if (p.have()) return; - -// maintain sparse_regions - if (index == 0) - { - if (index == 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) const - { - // prevent the number of partial pieces to grow indefinitely - if (m_downloads.size() > 20) options |= prioritize_partials; - - if (options & ignore_whole_pieces) prefer_whole_pieces = 0; - - // only one of rarest_first and sequential can be set. - TORRENT_ASSERT(bool(options & rarest_first) - + bool(options & sequential) <= 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) - { - if (!pieces[i->index]) 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) - { - 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 (options & reverse) - { - for (int i = m_reverse_cursor - 1; i >= m_cursor; --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 - { - for (int i = m_cursor; i < m_reverse_cursor; ++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 if (options & rarest_first) - { - if (m_dirty) update_pieces(); - TORRENT_ASSERT(!m_dirty); - - if (options & reverse) - { - // 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) - { - 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 = std::rand() % 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) - { - 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; - -#ifdef TORRENT_DEBUG - verify_pick(interesting_blocks, pieces); - verify_pick(backup_blocks, pieces); - verify_pick(backup_blocks2, pieces); -#endif - - 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; - - 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 - for (int j = 0; j < num_blocks_in_piece; ++j) - { - block_info const& info = i->info[j]; - if (info.state != block_info::state_requested - || info.peer == peer) - continue; - interesting_blocks.push_back(piece_block(i->index, j)); - } - } - -#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; - - 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]; - 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()) - { -// print_pieces(); - for (int i = 0; i < num_pieces(); ++i) - { - if (!pieces[i]) continue; - if (piece_priority(i) == 0) continue; - if (have_piece(i)) continue; - - std::vector::const_iterator k - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(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]; - if (info.state == block_info::state_finished) continue; - TORRENT_ASSERT(info.peer != 0); - } - */ - } - } -#endif - - } - - 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) - 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]; - 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 we're prioritizing partials, we've already - // looked through the downloading pieces - if (options & prioritize_partials) return num_blocks; - - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(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; - 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); - for (int j = 0; j < num_blocks_in_piece; ++j) - { - interesting_blocks.push_back(piece_block(k, j)); - --num_blocks; - } - } - } -#ifdef TORRENT_DEBUG - 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; - - int num_blocks_in_piece = blocks_in_piece(dp.index); - - // 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]; - 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]; - if (info.state != block_info::state_none) continue; - - TORRENT_ASSERT(dp.info[j].state == block_info::state_none); - - // 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; - -#ifdef TORRENT_DEBUG - 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(std::find_if(m_downloads.begin(), m_downloads.end() - , has_index(index)) == m_downloads.end()); - return false; - } - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(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].state == block_info::state_finished - || i->info[k].state == block_info::state_writing); - } -#endif - - return true; - } - - bool piece_picker::is_requested(piece_block block) const - { - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - - if (m_piece_map[block.piece_index].downloading == 0) return false; - std::vector::const_iterator i - = std::find_if( - m_downloads.begin() - , m_downloads.end() - , has_index(block.piece_index)); - - TORRENT_ASSERT(i != m_downloads.end()); - return i->info[block.block_index].state == block_info::state_requested; - } - - bool piece_picker::is_downloaded(piece_block block) const - { - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - 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 - { - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - 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(state != piece_picker::none); - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - TORRENT_ASSERT(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); - TORRENT_ASSERT(prio >= 0); - p.downloading = 1; - if (prio >= 0 && !m_dirty) update(prio, p.index); - - downloading_piece& dp = add_download_piece(); - dp.state = state; - dp.index = block.piece_index; - block_info& info = dp.info[block.block_index]; - info.state = block_info::state_requested; - info.peer = peer; - info.num_peers = 1; - ++dp.requested; - dp.last_request = time_now(); - } - else - { -#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS - TORRENT_PIECE_PICKER_INVARIANT_CHECK; -#endif - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - block_info& info = i->info[block.block_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; - } - ++info.num_peers; - if (i->state == none) i->state = state; - i->last_request = time_now(); - } - return true; - } - - int piece_picker::num_peers(piece_block block) const - { - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - TORRENT_ASSERT(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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - - block_info const& info = i->info[block.block_index]; - return info.num_peers; - } - - ptime piece_picker::last_request(int piece) const - { - TORRENT_ASSERT(piece >= 0); - TORRENT_ASSERT(piece < (int)m_piece_map.size()); - - piece_pos const& p = m_piece_map[piece]; - if (!p.downloading) return min_time(); - - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(piece)); - TORRENT_ASSERT(i != m_downloads.end()); - // just to play it safe - if (i == m_downloads.end()) return min_time(); - - return i->last_request; - } - - 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(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - TORRENT_ASSERT(block.block_index < blocks_in_piece(block.piece_index)); - - 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); - TORRENT_ASSERT(prio >= 0); - p.downloading = 1; - if (prio >= 0 && !m_dirty) update(prio, p.index); - - downloading_piece& dp = add_download_piece(); - dp.index = block.piece_index; - dp.state = none; - block_info& info = dp.info[block.block_index]; - info.state = block_info::state_writing; - info.peer = peer; - info.num_peers = 0; - dp.writing = 1; - sort_piece(m_downloads.end()-1); - } - else - { - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - block_info& info = i->info[block.block_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; - - // 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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(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.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; - - 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(p.index); - else update(prev_priority, p.index); - } - else - { - sort_piece(i); - } - } - - void piece_picker::mark_as_finished(piece_block block, void* peer) - { - TORRENT_ASSERT(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - TORRENT_ASSERT(block.block_index < blocks_in_piece(block.piece_index)); - - piece_pos& p = m_piece_map[block.piece_index]; - - if (p.downloading == 0) - { -#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(); - dp.state = none; - dp.index = block.piece_index; - block_info& info = dp.info[block.block_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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - block_info& info = i->info[block.block_index]; - - if (info.state == block_info::state_finished) return; - - TORRENT_ASSERT(info.num_peers == 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 - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); - TORRENT_ASSERT(i != m_downloads.end()); - - d.clear(); - for (int j = 0; j < blocks_in_piece(index); ++j) - { - d.push_back(i->info[j].peer); - } - } - - void* piece_picker::get_downloader(piece_block block) const - { - std::vector::const_iterator i = std::find_if( - m_downloads.begin() - , m_downloads.end() - , has_index(block.piece_index)); - - if (i == m_downloads.end()) return 0; - - TORRENT_ASSERT(block.block_index >= 0); - - if (i->info[block.block_index].state == block_info::state_none) - return 0; - - return i->info[block.block_index].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(block.piece_index >= 0); - TORRENT_ASSERT(block.block_index >= 0); - TORRENT_ASSERT(block.piece_index < (int)m_piece_map.size()); - TORRENT_ASSERT(block.block_index < blocks_in_piece(block.piece_index)); - - if (m_piece_map[block.piece_index].downloading == 0) - { - TORRENT_ASSERT(std::find_if(m_downloads.begin(), m_downloads.end() - , has_index(block.piece_index)) == m_downloads.end()); - return; - } - - std::vector::iterator i = std::find_if(m_downloads.begin() - , m_downloads.end(), has_index(block.piece_index)); - TORRENT_ASSERT(i != m_downloads.end()); - - block_info& info = i->info[block.block_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(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; - } - - // 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(std::find_if(m_downloads.begin(), m_downloads.end() - , has_index(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/libtorrent_utp/src/policy.cpp b/libtorrent_utp/src/policy.cpp deleted file mode 100644 index ebe188b77..000000000 --- a/libtorrent_utp/src/policy.cpp +++ /dev/null @@ -1,1624 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#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" - -#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 - { return p->address() == m_ep.address() && p->port == m_ep.port(); } - - tcp::endpoint const& m_ep; - }; - -#ifdef TORRENT_DEBUG - struct match_peer_connection - { - match_peer_connection(peer_connection const& c) : m_conn(c) {} - - bool operator()(policy::peer const* p) const - { 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 - { - return p->connection == &m_conn - || (p->ip() == m_conn.remote() - && p->connectable); - } - - peer_connection const& m_conn; - }; -#endif - -} - -namespace libtorrent -{ - // 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); - int num_requests = c.desired_queue_size() - - (int)c.download_queue().size() - - (int)c.request_queue().size(); - -#ifdef TORRENT_VERBOSE_LOGGING - c.peer_log("*** PIECE_PICKER [ req: %d ]", num_requests); -#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) - { - 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 - - 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; - - // this vector is filled with the interesting pieces - // that some other peer is currently downloading - // we should then compare this peer's download speed - // with the other's, to see if we should abort another - // peer_connection in favour of this one - std::vector busy_pieces; - busy_pieces.reserve(num_requests); - - std::vector const& suggested = c.suggested_pieces(); - bitfield const& bits = c.get_bitfield(); - - 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 - bitfield mask(c.get_bitfield().size(), false); - for (std::vector::const_iterator i = allowed_fast.begin() - , end(allowed_fast.end()); i != end; ++i) - if (bits[*i]) mask.set_bit(*i); - - p.pick_pieces(mask, interesting_pieces - , num_requests, prefer_whole_pieces, c.peer_info_struct() - , state, c.picker_options(), suggested); - } - else - { - // 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); - } - -#ifdef TORRENT_VERBOSE_LOGGING - c.peer_log("*** PIECE_PICKER [ prefer_whole: %d picked: %d ]" - , prefer_whole_pieces, int(interesting_pieces.size())); -#endif - std::vector const& dq = c.download_queue(); - std::vector const& rq = c.request_queue(); - for (std::vector::iterator i = interesting_pieces.begin(); - i != interesting_pieces.end(); ++i) - { - if (prefer_whole_pieces == 0 && num_requests <= 0) break; - - if (p.is_requested(*i)) - { - if (num_requests <= 0) break; - // don't request pieces we already have in our request 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()) - continue; - - TORRENT_ASSERT(p.num_peers(*i) > 0); - busy_pieces.push_back(*i); - continue; - } - - TORRENT_ASSERT(p.num_peers(*i) == 0); - - // don't request pieces we already have in our request 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()) - 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--; - } - - // if we don't have any potential busy blocks to request - // or if we have picked as many blocks as we should - // or if we already have outstanding requests, don't - // pick a busy piece - if (busy_pieces.empty() - || num_requests <= 0 - || dq.size() + rq.size() > 0) - { - return; - } - - // 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 - if (t.settings().strict_end_game_mode - && p.num_have() + p.get_download_queue().size() - < t.torrent_file().num_pieces()) - return; - - // if all blocks has the same number of peers on them - // we want to pick a random block - std::random_shuffle(busy_pieces.begin(), busy_pieces.end()); - - // find the block with the fewest requests to it - std::vector::iterator i = std::min_element( - busy_pieces.begin(), busy_pieces.end() - , boost::bind(&piece_picker::num_peers, boost::cref(p), _1) < - bind(&piece_picker::num_peers, boost::cref(p), _2)); -#ifdef TORRENT_DEBUG - piece_picker::downloading_piece st; - p.piece_info(i->piece_index, st); - TORRENT_ASSERT(st.requested + st.finished + st.writing == p.blocks_in_piece(i->piece_index)); -#endif - TORRENT_ASSERT(p.is_requested(*i)); - TORRENT_ASSERT(p.num_peers(*i) > 0); - - ptime last_request = p.last_request(i->piece_index); - ptime now = time_now(); - - // don't re-request from a piece more often than once every 5 seconds - // TODO: make configurable - if (now - last_request < seconds(5)) - return; - - c.add_request(*i, peer_connection::req_busy); - } - - policy::policy(torrent* t) - : m_torrent(t) - , m_round_robin(0) - , m_num_connect_candidates(0) - , m_num_seeds(0) - , m_finished(false) - { TORRENT_ASSERT(t); } - - // disconnects and removes all peers that are now filtered - void policy::ip_filter_updated() - { - INVARIANT_CHECK; - - aux::session_impl& ses = m_torrent->session(); - piece_picker* p = 0; - if (m_torrent->has_picker()) p = &m_torrent->picker(); - - 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)->connection) - { - (*i)->connection->disconnect(errors::banned_by_ip_filter); - if (ses.m_alerts.should_post()) - ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle(), (*i)->address())); - TORRENT_ASSERT((*i)->connection == 0 - || (*i)->connection->peer_info_struct() == 0); - } - else - { - if (ses.m_alerts.should_post()) - ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle(), (*i)->address())); - } - int current = i - m_peers.begin(); - erase_peer(i); - i = m_peers.begin() + current; - } - } - - void policy::erase_peer(policy::peer* p) - { - INVARIANT_CHECK; - - 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()); - - 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; - -#ifdef TORRENT_DEBUG - 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 - { - return p.source == peer_info::resume_data - && p.failcount > 0 - && !p.banned; - } - - bool policy::is_erase_candidate(peer const& pe, bool finished) const - { - return pe.connection == 0 - && pe.last_connected != 0 - && !pe.banned - && !is_connect_candidate(pe, m_finished); - } - - void policy::erase_peers() - { - 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; - - TORRENT_ASSERT(m_finished == m_torrent->is_finished()); - - int round_robin = rand() % m_peers.size(); - - for (int iterations = (std::min)(int(m_peers.size()), 300); - iterations > 0; --iterations) - { - if (int(m_peers.size()) < max_peerlist_size * 0.95) - break; - - if (round_robin == int(m_peers.size())) round_robin = 0; - - peer& pe = *m_peers[round_robin]; - 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; - TORRENT_ASSERT(current >= 0 && current < int(m_peers.size())); - --round_robin; - erase_peer(m_peers.begin() + current); - } - else - { - 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); - } - } - - void policy::ban_peer(policy::peer* p) - { - INVARIANT_CHECK; - - if (is_connect_candidate(*p, m_finished)) - --m_num_connect_candidates; - - 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(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; - - 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 - { - if (p.connection - || p.banned - || !p.connectable - || (p.seed && finished) - || 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; - 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; - address external_ip = m_torrent->session().external_address(); - - // don't bias any particular peers when seeding - if (m_finished || external_ip == address()) - { - // set external_ip to a random value, to - // radomize which peers we prefer - address_v4::bytes_type bytes; - std::generate(bytes.begin(), bytes.end(), &std::rand); - external_ip = address_v4(bytes); - } - - 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]; - 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; - --m_round_robin; - erase_peer(m_peers.begin() + current); - } - 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_ip)) 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_ip, m_peers[candidate]->address()) << - " external: " << external_ip << - " t: " << (session_time - m_peers[candidate]->last_connected) << - " ]\n"; - } -#endif - - if (candidate == -1) return m_peers.end(); - return m_peers.begin() + candidate; - } - - void policy::pulse() - { - INVARIANT_CHECK; - - erase_peers(); - } - - bool policy::new_connection(peer_connection& c, int session_time) - { - TORRENT_ASSERT(!c.is_local()); - - 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()); - - aux::session_impl& ses = m_torrent->session(); - - if (m_torrent->num_peers() >= m_torrent->max_connections() - && ses.num_connections() >= ses.settings().connections_limit - && c.remote().address() != m_torrent->current_tracker().address()) - { - c.disconnect(errors::too_many_connections); - return false; - } - -#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) 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()) found = true; - } - - if (found) - { - i = *iter; - - 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) - { - i->connection->disconnect(ec2); - TORRENT_ASSERT(i->connection == 0); - } - else if (!i->connection->is_connecting() || c.is_local()) - { - c.disconnect(errors::duplicate_peer_id); - return false; - } - else - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - m_torrent->debug_log("duplicate connection. existing connection" - " is connecting and this connection is incoming. closing existing " - "connection in favour of this one"); -#endif - i->connection->disconnect(errors::duplicate_peer_id); - TORRENT_ASSERT(i->connection == 0); - } - } - - 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); - - if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size) - { - c.disconnect(errors::too_many_connections); - return false; - } - -#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; - -// TORRENT_ASSERT(p->in_use == 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); - -#ifdef TORRENT_DEBUG - 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(i->prev_amount_download, i->prev_amount_upload); - - // 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)); - return true; - } - - bool policy::update_peer_port(int port, policy::peer* p, int src) - { - TORRENT_ASSERT(p != 0); - TORRENT_ASSERT(p->connection); - - 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; - 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; - p->connection->disconnect(errors::duplicate_peer_id); - erase_peer(p); - return false; - } - erase_peer(i); - } - } -#ifdef TORRENT_DEBUG - else - { - 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 - { - // 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; - 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 (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); - - 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; - - 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); - - 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; - aux::session_impl& ses = m_torrent->session(); - - 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); - -#ifdef TORRENT_DEBUG - p->in_use = true; -#endif - - if (!insert_peer(p, iter, flags)) - { -#ifdef TORRENT_DEBUG - p->in_use = false; -#endif - - m_torrent->session().m_i2p_peer_pool.free((i2p_peer*)p); - return 0; - } - } - else - { - p = *iter; - update_peer(p, src, flags, tcp::endpoint(), destination); - } - 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; - - 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())); - 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())); - return 0; - } - - // if the IP is blocked, don't add it - if (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())); - return 0; - } - - iterator iter; - peer* p = 0; - - int max_peerlist_size = m_torrent->is_paused() - ?m_torrent->settings().max_paused_peerlist_size - :m_torrent->settings().max_peerlist_size; - - 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); - -#ifdef TORRENT_DEBUG - p->in_use = true; -#endif - - if (!insert_peer(p, iter, flags)) - { -#ifdef TORRENT_DEBUG - p->in_use = false; -#endif -#if TORRENT_USE_IPV6 - if (is_v6) m_torrent->session().m_ipv6_peer_pool.free((ipv6_peer*)p); - else -#endif - m_torrent->session().m_ipv4_peer_pool.free((ipv4_peer*)p); - return 0; - } - } - else - { - p = *iter; - update_peer(p, src, flags, remote, 0); - } - - 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.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(); - - TORRENT_ASSERT((std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_connection(c)) - != m_peers.end()) == (p != 0)); - - // if we couldn't find the connection in our list, just ignore it. - if (p == 0) return; - - 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. - } - - 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); - } - } - -#ifdef TORRENT_DEBUG - 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(); - } - - 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; - 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; - if (p->connection == 0) 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() - {} - - policy::peer::peer(boost::uint16_t port, bool conn, int src) - : prev_amount_upload(0) - , prev_amount_download(0) - , connection(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 - , pe_support(true) -#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) - { - TORRENT_ASSERT((src & 0xff) == src); - } - - size_type policy::peer::total_download() const - { - if (connection != 0) - { - TORRENT_ASSERT(prev_amount_download == 0); - return connection->statistics().total_payload_download(); - } - else - { - return prev_amount_download; - } - } - - size_type policy::peer::total_upload() const - { - if (connection != 0) - { - TORRENT_ASSERT(prev_amount_upload == 0); - return connection->statistics().total_payload_upload(); - } - else - { - return prev_amount_upload; - } - } - - // 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 - { - 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; - - // prefer peers with higher failcount - return lhs.failcount > rhs.failcount; - } - - // this returns true if lhs is a better connect candidate than rhs - bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs - , address const& external_ip) 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 - int lhs_distance = cidr_distance(external_ip, lhs.address()); - int rhs_distance = cidr_distance(external_ip, rhs.address()); - if (lhs_distance < rhs_distance) return true; - return false; - } -} - diff --git a/libtorrent_utp/src/puff.cpp b/libtorrent_utp/src/puff.cpp deleted file mode 100644 index 82347e779..000000000 --- a/libtorrent_utp/src/puff.cpp +++ /dev/null @@ -1,842 +0,0 @@ -/* - * 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/libtorrent_utp/src/session.cpp b/libtorrent_utp/src/session.cpp deleted file mode 100644 index 2afc612b7..000000000 --- a/libtorrent_utp/src/session.cpp +++ /dev/null @@ -1,945 +0,0 @@ -/* - -Copyright (c) 2006, 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 "libtorrent/pch.hpp" - -#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/lt_trackers.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" - -using boost::shared_ptr; -using boost::weak_ptr; -using libtorrent::aux::session_impl; - -#ifdef TORRENT_MEMDEBUG -void start_malloc_debug(); -void stop_malloc_debug(); -#endif - -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. - session_settings min_memory_usage() - { - session_settings set; - // 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; - - // 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.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; - - return set; - } - - session_settings high_performance_seed() - { - session_settings set; - - // allow 500 files open at a time - set.file_pool_size = 500; - - // as a seed box, we must accept multiple peers behind - // the same NAT - set.allow_multiple_connections_per_ip = true; - - // 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 = 512; - set.write_cache_line_size = 512; - 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; - - // flush write cache based on largest contiguous block - set.disk_cache_algorithm = session_settings::largest_contiguous; - - // explicitly cache rare pieces - set.explicit_read_cache = true; - // 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.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 10 MB/s, the upper - // limit should be 5 MB - set.send_buffer_watermark = 5 * 1024 * 1024; - - // put 10 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 = 10; - - // 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; - - 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* e, mutex* m, boost::function f) - { - *ret = f(); - mutex::scoped_lock l(*m); - *done = true; - e->signal_all(l); - } - - void fun_wrap(bool* done, condition* e, mutex* m, boost::function f) - { - f(); - mutex::scoped_lock l(*m); - *done = true; - e->signal_all(l); - } - -#define TORRENT_ASYNC_CALL(x) \ - m_impl->m_io_service.post(boost::bind(&session_impl:: x, m_impl.get())) - -#define TORRENT_ASYNC_CALL1(x, a1) \ - m_impl->m_io_service.post(boost::bind(&session_impl:: x, m_impl.get(), a1)) - -#define TORRENT_ASYNC_CALL2(x, a1, a2) \ - m_impl->m_io_service.post(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)) - -#define TORRENT_SYNC_CALL(x) \ - bool done = false; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL1(x, a1) \ - bool done = false; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL2(x, a1, a2) \ - bool done = false; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL_RET(type, x) \ - bool done = false; \ - type r; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get())))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL_RET1(type, x, a1) \ - bool done = false; \ - type r; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1)))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL_RET2(type, x, a1, a2) \ - bool done = false; \ - type r; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(boost::bind(&fun_ret, &r, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2)))); \ - do { m_impl->cond.wait(l); } while(!done) - -#define TORRENT_SYNC_CALL_RET3(type, x, a1, a2, a3) \ - bool done = false; \ - type r; \ - mutex::scoped_lock l(m_impl->mut); \ - m_impl->m_io_service.post(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)))); \ - do { m_impl->cond.wait(l); } while(!done) - - // 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() {} - - void session::init(std::pair listen_range, char const* listen_interface - , fingerprint const& id, int flags, int alert_mask TORRENT_LOGPATH_ARG) - { - m_impl.reset(new session_impl(listen_range, id, listen_interface TORRENT_LOGPATH)); - -#ifdef TORRENT_MEMDEBUG - start_malloc_debug(); -#endif - set_alert_mask(alert_mask); -#ifndef TORRENT_DISABLE_EXTENSIONS - if (flags & add_default_plugins) - { - add_extension(create_ut_pex_plugin); - add_extension(create_ut_metadata_plugin); - add_extension(create_lt_trackers_plugin); - add_extension(create_smart_ban_plugin); - } -#endif - if (flags & start_default_features) - { - start_upnp(); - start_natpmp(); -#ifndef TORRENT_DISABLE_DHT - start_dht(); -#endif - start_lsd(); - } - } - - session::~session() - { -#ifdef TORRENT_MEMDEBUG - stop_malloc_debug(); -#endif - 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); - } - -#ifndef TORRENT_DISABLE_EXTENSIONS - void session::add_extension(boost::function(torrent*, void*)> ext) - { - TORRENT_ASYNC_CALL1(add_extension, ext); - } -#endif - -#ifndef TORRENT_DISABLE_GEO_IP - void session::load_asnum_db(char const* file) - { - TORRENT_ASYNC_CALL1(load_asnum_db, std::string(file)); - } - - void session::load_country_db(char const* file) - { - TORRENT_ASYNC_CALL1(load_country_db, std::string(file)); - } - - int session::as_for_ip(address const& addr) - { - return m_impl->as_for_ip(addr); - } - -#if TORRENT_USE_WSTRING - void session::load_asnum_db(wchar_t const* file) - { - TORRENT_ASYNC_CALL1(load_asnum_dbw, std::wstring(file)); - } - - void session::load_country_db(wchar_t const* file) - { - TORRENT_ASYNC_CALL1(load_country_dbw, std::wstring(file)); - } -#endif // TORRENT_USE_WSTRING -#endif // TORRENT_DISABLE_GEO_IP - -#ifndef TORRENT_NO_DEPRECATE - void session::load_state(entry const& ses_state) - { - std::vector buf; - bencode(std::back_inserter(buf), ses_state); - lazy_entry e; - error_code ec; - lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec); - 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); - } - - 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, ec); - if (ec) throw libtorrent_exception(ec); - return r; - } -#endif - - torrent_handle session::add_torrent(add_torrent_params const& params, error_code& ec) - { - TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, ec); - return r; - } - -#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; - std::vector buf; - if (resume_data.type() != entry::undefined_t) - { - bencode(std::back_inserter(buf), resume_data); - p.resume_data = &buf; - } - 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; - std::vector buf; - if (resume_data.type() != entry::undefined_t) - { - bencode(std::back_inserter(buf), resume_data); - p.resume_data = &buf; - } - 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) - { - TORRENT_ASYNC_CALL2(remove_torrent, h, options); - } - - bool session::listen_on( - std::pair const& port_range - , const char* net_interface, int flags) - { - TORRENT_SYNC_CALL_RET3(bool, listen_on, port_range, net_interface, flags); - return r; - } - - unsigned short session::listen_port() const - { - TORRENT_SYNC_CALL_RET(unsigned short, 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(); - } - -#ifndef TORRENT_DISABLE_DHT - - void session::start_dht() - { - // the state is loaded in load_state() - TORRENT_ASYNC_CALL(start_dht); - } - - void session::stop_dht() - { - TORRENT_ASYNC_CALL(stop_dht); - } - - void session::set_dht_settings(dht_settings const& settings) - { - TORRENT_ASYNC_CALL1(set_dht_settings, settings); - } - -#ifndef TORRENT_NO_DEPRECATE - void session::start_dht(entry const& startup_state) - { - TORRENT_ASYNC_CALL1(start_dht, startup_state); - } - - entry session::dht_state() const - { - TORRENT_SYNC_CALL_RET(entry, dht_state); - return r; - } -#endif - - void session::add_dht_node(std::pair const& node) - { - TORRENT_ASYNC_CALL1(add_dht_node_name, node); - } - - void session::add_dht_router(std::pair const& node) - { - TORRENT_ASYNC_CALL1(add_dht_router, node); - } - - bool session::is_dht_running() const - { - TORRENT_SYNC_CALL_RET(bool, is_dht_running); - return r; - } - -#endif - -#ifndef TORRENT_DISABLE_ENCRYPTION - void session::set_pe_settings(pe_settings const& settings) - { - TORRENT_ASYNC_CALL1(set_pe_settings, settings); - } - - pe_settings session::get_pe_settings() const - { - TORRENT_SYNC_CALL_RET(pe_settings, get_pe_settings); - return r; - } -#endif - - 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() - { - 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; - } - - -#ifndef TORRENT_DISABLE_DHT - void session::set_dht_proxy(proxy_settings const& s) - { - TORRENT_ASYNC_CALL1(set_dht_proxy, s); - } - - proxy_settings session::dht_proxy() const - { - TORRENT_SYNC_CALL_RET(proxy_settings, dht_proxy); - return r; - } -#endif -#endif // TORRENT_NO_DEPRECATE - -#if TORRENT_USE_I2P - void session::set_i2p_proxy(proxy_settings const& s) - { - TORRENT_ASYNC_CALL1(set_i2p_proxy, s); - } - - proxy_settings session::i2p_proxy() const - { - TORRENT_SYNC_CALL_RET(proxy_settings, i2p_proxy); - return r; - } -#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 - - std::auto_ptr session::pop_alert() - { - return m_impl->pop_alert(); - } - - void session::set_alert_dispatch(boost::function)> const& fun) - { - TORRENT_ASYNC_CALL1(set_alert_dispatch, fun); - } - - alert const* session::wait_for_alert(time_duration max_wait) - { - return m_impl->wait_for_alert(max_wait); - } - - void session::set_alert_mask(int m) - { - TORRENT_ASYNC_CALL1(set_alert_mask, m); - } - - 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; - } - -#ifndef TORRENT_NO_DEPRECATE - 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); - } - - natpmp* session::start_natpmp() - { - TORRENT_SYNC_CALL_RET(natpmp*, start_natpmp); - return r; - } - - upnp* session::start_upnp() - { - TORRENT_SYNC_CALL_RET(upnp*, start_upnp); - return r; - } - - 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; - } -} - diff --git a/libtorrent_utp/src/session_impl.cpp b/libtorrent_utp/src/session_impl.cpp deleted file mode 100644 index 50fdc6a25..000000000 --- a/libtorrent_utp/src/session_impl.cpp +++ /dev/null @@ -1,4465 +0,0 @@ -/* - -Copyright (c) 2006, 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 "libtorrent/pch.hpp" - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include - -#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" -#include "libtorrent/kademlia/dht_tracker.hpp" -#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" - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING -#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 -#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 - -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; -#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 { - - struct seed_random_generator - { - seed_random_generator() - { - std::srand(total_microseconds(time_now_hires() - min_time())); - } - }; - -#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, 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_watermark) -#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, 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(integer, default_peer_upload_rate) - TORRENT_SETTING(integer, default_peer_download_rate) - 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, anonymous_mode) - TORRENT_SETTING(integer, tick_interval) - 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, 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) - TORRENT_SETTING(integer, utp_delayed_ack) - 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) - }; - -#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(integer, port) - TORRENT_SETTING(std_string, username) - TORRENT_SETTING(std_string, password) - TORRENT_SETTING(integer, type) - }; -#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_torrent_search_reply) - }; -#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(integer, out_enc_policy) - TORRENT_SETTING(integer, in_enc_policy) - TORRENT_SETTING(integer, 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 - }; - -#undef lenof - -#ifdef TORRENT_STATS - int session_impl::logging_allocator::allocations = 0; - int session_impl::logging_allocator::allocated_bytes = 0; -#endif - - session_impl::session_impl( - std::pair listen_port_range - , fingerprint const& cl_fprint - , char const* listen_interface -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - , std::string const& logpath -#endif - ) - : 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_client) -#endif - , m_alerts(m_io_service) - , 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_listen_port_retries(listen_port_range.second - listen_port_range.first) -#if TORRENT_USE_I2P - , m_i2p_conn(m_io_service) -#endif - , m_abort(false) - , m_paused(false) - , 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_incoming_connection(false) - , m_created(time_now_hires()) - , m_last_tick(m_created) - , m_last_second_tick(m_created - milliseconds(900)) - , m_last_choke(m_created) -#ifndef TORRENT_DISABLE_DHT - , m_dht_announce_timer(m_io_service) -#endif - , m_external_udp_port(0) - , m_udp_socket(m_io_service - , boost::bind(&session_impl::on_receive_udp, this, _1, _2, _3, _4) - , boost::bind(&session_impl::on_receive_udp_hostname, this, _1, _2, _3, _4) - , m_half_open) - , m_utp_socket_manager(m_settings, m_udp_socket - , boost::bind(&session_impl::incoming_connection, this, _1)) - , m_timer(m_io_service) - , m_lsd_announce_timer(m_io_service) - , m_host_resolver(m_io_service) - , m_tick_residual(0) -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - , m_logpath(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) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - m_logger = create_log("main_session", listen_port(), false); - (*m_logger) << time_now_string() << "\n"; -#endif - - error_code ec; -#ifdef TORRENT_USE_OPENSSL - m_ssl_ctx.set_verify_mode(asio::ssl::context::verify_none, ec); -#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(); - - TORRENT_ASSERT_VAL(listen_interface, listen_interface); - m_listen_interface = tcp::endpoint(address::from_string(listen_interface, ec), listen_port_range.first); - TORRENT_ASSERT_VAL(!ec, ec); - - m_tcp_mapping[0] = -1; - m_tcp_mapping[1] = -1; - m_udp_mapping[0] = -1; - m_udp_mapping[1] = -1; -#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: %d\n" - "libtorrent revision: %d\n\n" - , TORRENT_CFG_STRING - , LIBTORRENT_VERSION - , LIBTORRENT_REVISION); - (*m_logger) << tmp; - - -#define PRINT_SIZEOF(x) snprintf(tmp, sizeof(tmp), "sizeof(" #x ") = %d\n", int(sizeof(x))); (*m_logger) << tmp; -#define PRINT_OFFSETOF(x, y) snprintf(tmp, sizeof(tmp), " offsetof(" #x "," #y "): %d\n", int(offsetof(x, y))); \ - (*m_logger) << tmp; - - PRINT_SIZEOF(announce_entry) - PRINT_OFFSETOF(announce_entry, url) - PRINT_OFFSETOF(announce_entry, message) - PRINT_OFFSETOF(announce_entry, last_error) - PRINT_OFFSETOF(announce_entry, next_announce) - PRINT_OFFSETOF(announce_entry, min_announce) - PRINT_OFFSETOF(announce_entry, tier) - PRINT_OFFSETOF(announce_entry, fail_limit) - - PRINT_SIZEOF(torrent_info) - PRINT_OFFSETOF(torrent_info, m_files) - PRINT_OFFSETOF(torrent_info, m_orig_files) - PRINT_OFFSETOF(torrent_info, m_web_seeds) - PRINT_OFFSETOF(torrent_info, m_nodes) - PRINT_OFFSETOF(torrent_info, m_merkle_tree) - PRINT_OFFSETOF(torrent_info, m_info_section) - PRINT_OFFSETOF(torrent_info, m_piece_hashes) - PRINT_OFFSETOF(torrent_info, m_info_dict) - PRINT_OFFSETOF(torrent_info, m_creation_date) - PRINT_OFFSETOF(torrent_info, m_comment) - PRINT_OFFSETOF(torrent_info, m_created_by) - PRINT_OFFSETOF(torrent_info, m_info_hash) - - PRINT_SIZEOF(union_endpoint) - PRINT_SIZEOF(request_callback) - PRINT_SIZEOF(stat) - PRINT_SIZEOF(bandwidth_channel) - PRINT_SIZEOF(policy) - (*m_logger) << "sizeof(utp_socket_impl): " << socket_impl_size() << "\n"; - - PRINT_SIZEOF(file_entry) - -// PRINT_SIZEOF(stat_channel) -// PRINT_OFFSETOF(stat_channel, m_counter) -// PRINT_OFFSETOF(stat_channel, m_average) -// PRINT_OFFSETOF(stat_channel, m_total_counter) - - torrent::print_size(*m_logger); - - PRINT_SIZEOF(peer_connection) - PRINT_SIZEOF(bt_peer_connection) - PRINT_SIZEOF(address) - PRINT_SIZEOF(address_v4) - PRINT_SIZEOF(address_v4::bytes_type) -#if TORRENT_USE_IPV6 - PRINT_SIZEOF(address_v6) - PRINT_SIZEOF(address_v6::bytes_type) -#endif - PRINT_SIZEOF(void*) -#ifndef TORRENT_DISABLE_DHT - PRINT_SIZEOF(dht::node_entry) -#endif - - PRINT_SIZEOF(policy::peer) - PRINT_OFFSETOF(policy::peer, connection) - PRINT_OFFSETOF(policy::peer, last_optimistically_unchoked) - PRINT_OFFSETOF(policy::peer, last_connected) - PRINT_OFFSETOF(policy::peer, port) - PRINT_OFFSETOF(policy::peer, hashfails) - - PRINT_SIZEOF(policy::ipv4_peer) -#if TORRENT_USE_IPV6 - PRINT_SIZEOF(policy::ipv6_peer) -#endif - - PRINT_SIZEOF(udp_socket) - PRINT_OFFSETOF(udp_socket, m_callback) - PRINT_OFFSETOF(udp_socket, m_ipv4_sock) - PRINT_OFFSETOF(udp_socket, m_v4_ep) - PRINT_OFFSETOF(udp_socket, m_v4_buf) -#if TORRENT_USE_IPV6 - PRINT_OFFSETOF(udp_socket, m_ipv6_sock) - PRINT_OFFSETOF(udp_socket, m_v6_ep) - PRINT_OFFSETOF(udp_socket, m_v6_buf) -#endif - PRINT_OFFSETOF(udp_socket, m_bind_port) - PRINT_OFFSETOF(udp_socket, m_outstanding) - PRINT_OFFSETOF(udp_socket, m_socks5_sock) - PRINT_OFFSETOF(udp_socket, m_connection_ticket) - PRINT_OFFSETOF(udp_socket, m_proxy_settings) - PRINT_OFFSETOF(udp_socket, m_cc) - PRINT_OFFSETOF(udp_socket, m_resolver) - PRINT_OFFSETOF(udp_socket, m_tmp_buf) - PRINT_OFFSETOF(udp_socket, m_queue_packets) - PRINT_OFFSETOF(udp_socket, m_tunnel_packets) - PRINT_OFFSETOF(udp_socket, m_abort) - PRINT_OFFSETOF(udp_socket, m_proxy_addr) - PRINT_OFFSETOF(udp_socket, m_queue) -#ifdef TORRENT_DEBUG - PRINT_OFFSETOF(udp_socket, m_started) - PRINT_OFFSETOF(udp_socket, m_magic) - PRINT_OFFSETOF(udp_socket, m_outstanding_when_aborted) -#endif - - PRINT_SIZEOF(tracker_connection) - PRINT_SIZEOF(http_tracker_connection) - - PRINT_SIZEOF(udp_tracker_connection) - PRINT_OFFSETOF(udp_tracker_connection, m_man) - PRINT_OFFSETOF(udp_tracker_connection, m_abort) - PRINT_OFFSETOF(udp_tracker_connection, m_hostname) - PRINT_OFFSETOF(udp_tracker_connection, m_target) - PRINT_OFFSETOF(udp_tracker_connection, m_endpoints) - PRINT_OFFSETOF(udp_tracker_connection, m_transaction_id) - PRINT_OFFSETOF(udp_tracker_connection, m_ses) - PRINT_OFFSETOF(udp_tracker_connection, m_attempts) - PRINT_OFFSETOF(udp_tracker_connection, m_state) - PRINT_OFFSETOF(udp_tracker_connection, m_proxy) - -#ifndef TORRENT_DISABLE_DHT - PRINT_SIZEOF(dht::find_data_observer) - PRINT_SIZEOF(dht::announce_observer) - PRINT_SIZEOF(dht::null_observer) -#endif -#undef PRINT_OFFSETOF -#undef PRINT_SIZEOF - -#endif - -#ifdef TORRENT_STATS - m_stats_logger.open("session_stats.log", std::ios::trunc); - m_stats_logger << - "second:upload rate:download rate:downloading torrents:seeding torrents" - ":peers:connecting peers:disk block buffers:unchoked peers:num list peers" - ":peer allocations:peer storage bytes\n\n"; - m_buffer_usage_logger.open("buffer_stats.log", std::ios::trunc); - m_second_counter = 0; - 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 - (*m_logger) << time_now_string() << " max number of open files: " << rl.rlim_cur << "\n"; -#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 - - - // ---- generate a peer id ---- - static seed_random_generator seeder; - - m_key = rand() + (rand() << 15) + (rand() << 30); - 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(); - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " spawning network thread\n"; -#endif - m_thread.reset(new thread(boost::bind(&session_impl::main_thread, this))); - } - - void session_impl::start() - { -#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " *** session start\n"; -#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 - 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 - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("session_impl::on_dht_announce"); -#endif - 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 - -#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " open listen port\n"; -#endif - // no reuse_address - open_listen_port(false); -#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() << " done starting session\n"; -#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 < 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 (flags & session::save_dht_settings) - { - } -#endif -#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 - - } - - 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 < 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(); - - // 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; - - if (m_settings.broadcast_lsd && m_lsd) - m_lsd->use_broadcast(true); - - update_disk_thread_settings(); - } - -#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 - 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_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 - void session_impl::add_extension( - boost::function(torrent*, void*)> ext) - { - TORRENT_ASSERT(is_network_thread()); - TORRENT_ASSERT_VAL(ext, ext); - - typedef boost::shared_ptr(*function_t)(torrent*, void*); - function_t const* f = ext.target(); - - if (f) - { - for (extension_list_t::iterator i = m_extensions.begin(); i != m_extensions.end(); ++i) - if (function_equal(*i, *f)) return; - } - - m_extensions.push_back(ext); - } -#endif - -#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); - } -#endif - - void session_impl::pause() - { - TORRENT_ASSERT(is_network_thread()); - - if (m_paused) return; -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_logger) << time_now_string() << " *** session paused ***\n"; -#endif - m_paused = true; - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) - { - torrent& t = *i->second; - if (!t.is_torrent_paused()) 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(); - } - } - - void session_impl::abort() - { - TORRENT_ASSERT(is_network_thread()); - - if (m_abort) return; -#if defined TORRENT_LOGGING - (*m_logger) << time_now_string() << " *** ABORT CALLED ***\n"; -#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(); - if (m_lsd) m_lsd->close(); - if (m_upnp) m_upnp->close(); - if (m_natpmp) m_natpmp->close(); -#ifndef TORRENT_DISABLE_DHT - if (m_dht) - { - m_dht->stop(); - m_dht = 0; - } - m_dht_announce_timer.cancel(ec); -#endif - m_timer.cancel(ec); - m_lsd_announce_timer.cancel(ec); - - // 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); - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " aborting all torrents (" << m_torrents.size() << ")\n"; -#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) - (*m_logger) << time_now_string() << " aborting all tracker requests\n"; -#endif - m_tracker_manager.abort_all_requests(); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " sending event=stopped to trackers\n"; -#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) - (*m_logger) << time_now_string() << " aborting all connections (" << m_connections.size() << ")\n"; -#endif - // closing all the connections needs to be done from a callback, - // when the session mutex is not held - m_io_service.post(boost::bind(&connection_queue::close, &m_half_open)); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " connection queue: " << m_half_open.size() << "\n"; -#endif - - // abort all connections - while (!m_connections.empty()) - { -#ifdef TORRENT_DEBUG - 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) - (*m_logger) << time_now_string() << " connection queue: " << m_half_open.size() << "\n"; -#endif - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " shutting down connection queue\n"; -#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; - -#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*)&m_settings; - j.action = disk_io_job::update_settings; - m_disk_thread.add_job(j); - } - - void session_impl::set_settings(session_settings const& s) - { - INVARIANT_CHECK; - - 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.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.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.low_prio_disk != s.low_prio_disk) - 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(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) - m_auto_manage_time_scaler = 2; - - // if anonymous mode was enabled, clear out the peer ID - bool anonymous = (m_settings.anonymous_mode != s.anonymous_mode && s.anonymous_mode); - - 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); - } - } - - m_settings = s; - - update_rate_settings(); - - if (connections_limit_changed) update_connections_limit(); - if (unchoke_limit_changed) update_unchoke_limit(); - - // enable anonymous mode. We don't want to accept any incoming - // connections, except through a proxy. - if (anonymous) - { - m_settings.user_agent.clear(); - url_random((char*)&m_peer_id[0], (char*)&m_peer_id[0] + 20); - 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 (m_settings.broadcast_lsd && m_lsd) - m_lsd->use_broadcast(true); - - 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; - - // 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 = ' '; - } - - tcp::endpoint session_impl::get_ipv6_interface() const - { - return m_ipv6_interface; - } - - tcp::endpoint session_impl::get_ipv4_interface() const - { - return m_ipv4_interface; - } - - session_impl::listen_socket_t session_impl::setup_listener(tcp::endpoint ep - , int retries, bool v6_only, bool reuse_address) - { - error_code ec; - listen_socket_t s; - s.sock.reset(new socket_acceptor(m_io_service)); - s.sock->open(ep.protocol(), ec); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - if (ec) - { - (*m_logger) << "failed to open socket: " << print_endpoint(ep) - << ": " << ec.message() << "\n" << "\n"; - } -#endif - if (reuse_address) - s.sock->set_option(socket_acceptor::reuse_address(true), ec); -#if TORRENT_USE_IPV6 - if (ep.protocol() == tcp::v6()) - { - s.sock->set_option(v6only(v6_only), ec); -#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), ec); -#endif - } -#endif - s.sock->bind(ep, ec); - while (ec && retries > 0) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - char msg[200]; - snprintf(msg, 200, "failed to bind to interface \"%s\": %s" - , print_endpoint(ep).c_str(), ec.message().c_str()); - (*m_logger) << time_now_string() << " " << msg << "\n"; -#endif - ec = error_code(); - TORRENT_ASSERT_VAL(!ec, ec); - --retries; - ep.port(ep.port() + 1); - s.sock->bind(ep, ec); - } - if (ec) - { - // instead of giving up, try - // let the OS pick a port - ep.port(0); - ec = error_code(); - s.sock->bind(ep, ec); - } - if (ec) - { - // not even that worked, give up - if (m_alerts.should_post()) - m_alerts.post_alert(listen_failed_alert(ep, ec)); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - char msg[200]; - snprintf(msg, 200, "cannot bind to interface \"%s\": %s" - , print_endpoint(ep).c_str(), ec.message().c_str()); - (*m_logger) << time_now_string() << msg << "\n"; -#endif - return listen_socket_t(); - } - s.external_port = s.sock->local_endpoint(ec).port(); - s.sock->listen(m_settings.listen_queue_size, ec); - if (ec) - { - if (m_alerts.should_post()) - m_alerts.post_alert(listen_failed_alert(ep, ec)); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - char msg[200]; - snprintf(msg, 200, "cannot listen on interface \"%s\": %s" - , print_endpoint(ep).c_str(), ec.message().c_str()); - (*m_logger) << time_now_string() << msg << "\n"; -#endif - return listen_socket_t(); - } - - if (m_alerts.should_post()) - m_alerts.post_alert(listen_succeeded_alert(ep)); - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " listening on: " << ep - << " external port: " << s.external_port << "\n"; -#endif - return s; - } - - void session_impl::open_listen_port(bool reuse_address) - { - TORRENT_ASSERT(is_network_thread()); - - // close the open listen sockets - m_listen_sockets.clear(); - m_incoming_connection = false; - - m_ipv6_interface = tcp::endpoint(); - m_ipv4_interface = tcp::endpoint(); - - 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( - tcp::endpoint(address_v4::any(), m_listen_interface.port()) - , m_listen_port_retries, false, reuse_address); - - 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 - error_code ec; - m_listen_interface.port(s.sock->local_endpoint(ec).port()); - - m_listen_sockets.push_back(s); - async_accept(s.sock); - } - -#if TORRENT_USE_IPV6 - // only try to open the IPv6 port if IPv6 is installed - if (supports_ipv6()) - { - s = setup_listener( - tcp::endpoint(address_v6::any(), m_listen_interface.port()) - , m_listen_port_retries, true, reuse_address); - - if (s.sock) - { - m_listen_sockets.push_back(s); - async_accept(s.sock); - } - } -#endif // TORRENT_USE_IPV6 - - // set our main IPv4 and IPv6 interfaces - // used to send to the tracker - error_code ec; - 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( - m_listen_interface, m_listen_port_retries, false, reuse_address); - - if (s.sock) - { - m_listen_sockets.push_back(s); - async_accept(s.sock); - - if (m_listen_interface.address().is_v6()) - m_ipv6_interface = m_listen_interface; - else - m_ipv4_interface = m_listen_interface; - } - - } - - error_code ec; - m_udp_socket.bind(udp::endpoint(m_listen_interface.address(), m_listen_interface.port()), ec); - if (ec) - { - if (m_alerts.should_post()) - m_alerts.post_alert(listen_failed_alert(m_listen_interface, ec)); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - char msg[200]; - snprintf(msg, sizeof(msg), "cannot bind to UDP interface \"%s\": %s" - , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); - (*m_logger) << msg << "\n"; -#endif - } - 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()); - } - - open_new_incoming_socks_connection(); -#if TORRENT_USE_I2P - open_new_incoming_i2p_connection(); -#endif - - if (!m_listen_sockets.empty()) - { - error_code ec; - tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); - if (!ec) - { - if (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 - , local.port(), local.port()); - } - if (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 - , local.port(), local.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::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 + rand() % 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::on_i2p_open(error_code const& ec) - { - 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()), e)); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - char msg[200]; - snprintf(msg, sizeof(msg), "cannot bind to port %d: %s" - , m_listen_interface.port(), e.message().c_str()); - (*m_logger) << msg << "\n"; -#endif - return; - } - open_new_incoming_i2p_connection(); - incoming_connection(s); - } -#endif - -#ifndef TORRENT_DISABLE_DHT - - void session_impl::on_receive_udp(error_code const& e - , udp::endpoint const& ep, char const* buf, int len) - { - if (e) - { - if (e == asio::error::connection_refused - || e == asio::error::connection_reset - || e == asio::error::connection_aborted) - { - if (m_dht) m_dht->on_unreachable(ep); - if (m_tracker_manager.incoming_udp(e, ep, buf, len)) - m_stat.received_tracker_bytes(len + 28); - } - - if (m_alerts.should_post()) - m_alerts.post_alert(udp_error_alert(ep, e)); - return; - } - - if (len > 20 && *buf == 'd' && buf[len-1] == 'e' && m_dht) - { - // this is probably a dht message - m_dht->on_receive(ep, buf, len); - return; - } - - if (m_utp_socket_manager.incoming_packet(buf, len, ep)) - return; - - // maybe it's a udp tracker response - if (m_tracker_manager.incoming_udp(e, ep, buf, len)) - m_stat.received_tracker_bytes(len + 28); - } - - void session_impl::on_receive_udp_hostname(error_code const& e - , char const* hostname, char const* buf, int len) - { - // it's probably a udp tracker response - if (m_tracker_manager.incoming_udp(e, hostname, buf, len)) - { - m_stat.received_tracker_bytes(len + 28); - } - } - - -#endif - - void session_impl::async_accept(boost::shared_ptr const& listener) - { - shared_ptr c(new socket_type(m_io_service)); - c->instantiate(m_io_service); -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("session_impl::on_accept_connection"); -#endif - listener->async_accept(*c->get() - , boost::bind(&session_impl::on_accept_connection, this, c - , boost::weak_ptr(listener), _1)); - } - - void session_impl::on_accept_connection(shared_ptr const& s - , weak_ptr listen_socket, error_code const& e) - { -#if defined TORRENT_ASIO_DEBUGGING - complete_async("session_impl::on_accept_connection"); -#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) - std::string msg = "error accepting connection on '" - + print_endpoint(ep) + "' " + e.message(); - (*m_logger) << msg << "\n"; -#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); - 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); - 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) - --m_settings.connections_limit; - // try again, but still alert the user of the problem - async_accept(listener); - } - if (m_alerts.should_post()) - m_alerts.post_alert(listen_failed_alert(ep, e)); - return; - } - async_accept(listener); - - incoming_connection(s); - } - - void session_impl::incoming_connection(boost::shared_ptr const& s) - { - 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) - (*m_logger) << endp << " <== INCOMING CONNECTION FAILED, could " - "not retrieve remote endpoint " << ec.message() << "\n"; -#endif - return; - } - - TORRENT_ASSERT(endp.address() != address_v4::any()); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " <== INCOMING CONNECTION " << endp << "\n"; -#endif - - if (!m_settings.enable_incoming_utp - && s->get()) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << " rejected uTP connection\n"; -#endif - if (m_alerts.should_post()) - m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address())); - return; - } - - if (!m_settings.enable_incoming_tcp - && s->get()) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << " rejected TCP connection\n"; -#endif - if (m_alerts.should_post()) - m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address())); - 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; - - if (m_ip_filter.access(endp.address()) & ip_filter::blocked) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << "filtered blocked ip\n"; -#endif - if (m_alerts.should_post()) - m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address())); - 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; - - 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) - (*m_logger) << "number of connections limit exceeded (conns: " - << num_connections() << ", limit: " << m_settings.connections_limit - << "), connection rejected\n"; -#endif - 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) - (*m_logger) << " There are no torrents, disconnect\n"; -#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) - (*m_logger) << " There are no _active_ torrents, disconnect\n"; -#endif - return; - } - } - - setup_socket_buffers(*s); - - boost::intrusive_ptr c( - new bt_peer_connection(*this, s, endp, 0)); -#ifdef TORRENT_DEBUG - c->m_in_constructor = false; -#endif - - if (!c->is_disconnecting()) - { - m_connections.insert(c); - c->start(); - if (m_settings.default_peer_upload_rate) - c->set_upload_limit(m_settings.default_peer_upload_rate); - if (m_settings.default_peer_download_rate) - c->set_download_limit(m_settings.default_peer_download_rate); - } - } - - void session_impl::setup_socket_buffers(socket_type& s) - { - error_code ec; - if (m_settings.send_socket_buffer_size) - { - stream_socket::send_buffer_size option( - m_settings.send_socket_buffer_size); - s.set_option(option, ec); - } - if (m_settings.recv_socket_buffer_size) - { - stream_socket::receive_buffer_size option( - m_settings.recv_socket_buffer_size); - s.set_option(option, 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()), e)); - return; - } - open_new_incoming_socks_connection(); - incoming_connection(s); - } - - void session_impl::close_connection(peer_connection const* p - , error_code const& ec) - { -// 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) - (*m_logger) << time_now_string() << " CLOSING CONNECTION " - << p->remote() << " : " << ec.message() << "\n"; -#endif - - TORRENT_ASSERT(p->is_disconnecting()); - - if (!p->is_choked() && !p->ignore_unchoke_slots()) --m_num_unchoked; -// connection_map::iterator i = std::lower_bound(m_connections.begin(), m_connections.end() -// , p, boost::bind(&boost::intrusive_ptr::get, _1) < p); -// if (i->get() != p) i == m_connections.end(); - connection_map::iterator i = std::find_if(m_connections.begin(), m_connections.end() - , boost::bind(&boost::intrusive_ptr::get, _1) == p); - if (i != m_connections.end()) m_connections.erase(i); - } - - 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 - (*m_logger) << time_now_string() << " *** BINDING OUTGOING CONNECTION [ " - "port: " << port << " ]\n"; -#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() - { - TORRENT_ASSERT(is_network_thread()); - - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end();) - { - boost::intrusive_ptr p = *i; - ++i; - if (p->m_channel_state[peer_connection::download_channel] - != peer_info::bw_disk) continue; - - // setup_receive() may disconnect the connection - // and clear it out from the m_connections list - p->setup_receive(); - } - } - - // 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 - TORRENT_ASSERT(is_network_thread()); - - ptime now = time_now_hires(); - aux::g_current_time = now; -// too expensive -// INVARIANT_CHECK; - -#if defined TORRENT_VERBOSE_LOGGING -// (*m_logger) << time_now_string() << " session_impl::on_tick\n"; -#endif - if (m_abort) return; - - if (e == asio::error::operation_aborted) return; - - if (e) - { -#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING - (*m_logger) << "*** TICK TIMER FAILED " << e.message() << "\n"; -#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; - - int tick_interval_ms = total_milliseconds(now - m_last_second_tick); - m_last_second_tick = now; - m_tick_residual += tick_interval_ms - 1000; - - int 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; - } - } - } - - 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_tcp_peers = 0; - int num_peers = 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; - if (!p.get_socket()->get()) ++num_tcp_peers; - ++num_peers; - } - - if (num_peers == 0) - { - m_tcp_upload_channel.throttle(0); - m_tcp_download_channel.throttle(0); - } - else - { - if (num_tcp_peers == 0) num_tcp_peers = 1; - int upload_rate = (std::max)(m_stat.upload_rate(), 5000); - int download_rate = (std::max)(m_stat.download_rate(), 5000); - if (m_upload_channel.throttle()) upload_rate = m_upload_channel.throttle(); - if (m_download_channel.throttle()) download_rate = m_download_channel.throttle(); - - m_tcp_upload_channel.throttle(upload_rate * num_tcp_peers / num_peers); - m_tcp_download_channel.throttle(download_rate * num_tcp_peers / num_peers); - } - } - break; - } - -#ifdef TORRENT_STATS - ++m_second_counter; - int downloading_torrents = 0; - int seeding_torrents = 0; - static size_type downloaded = 0; - static size_type uploaded = 0; - size_type download_rate = (m_stat.total_download() - downloaded) * 1000 / tick_interval_ms; - size_type upload_rate = (m_stat.total_upload() - uploaded) * 1000 / tick_interval_ms; - downloaded = m_stat.total_download(); - uploaded = m_stat.total_upload(); - size_type num_peers = 0; - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) - { - num_peers += i->second->get_policy().num_peers(); - if (i->second->is_seed()) - ++seeding_torrents; - else - ++downloading_torrents; - } - int num_complete_connections = 0; - int num_half_open = 0; - int unchoked_peers = 0; - for (connection_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) - { - if ((*i)->is_connecting()) - ++num_half_open; - else - { - ++num_complete_connections; - if (!(*i)->is_choked()) ++unchoked_peers; - } - } - - m_stats_logger - << m_second_counter << "\t" - << upload_rate << "\t" - << download_rate << "\t" - << downloading_torrents << "\t" - << seeding_torrents << "\t" - << num_complete_connections << "\t" - << num_half_open << "\t" - << m_disk_thread.disk_allocations() << "\t" - << unchoked_peers << "\t" - << num_peers << "\t" - << logging_allocator::allocations << "\t" - << logging_allocator::allocated_bytes << "\t" - << std::endl; -#endif - - // -------------------------------------------------------------- - // auto managed torrent - // -------------------------------------------------------------- - 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; - 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; - 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() > t.upload_limit() * 9 / 10) - ++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(); - } - - t.second_tick(m_stat, tick_interval_ms); - ++i; - } - - // 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) - { - 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(m_stat.download_dht() - + m_stat.download_tracker()); - - m_upload_channel.use_quota(m_stat.upload_dht() - + 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())); - - // -------------------------------------------------------------- - // 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 >= 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 - // -------------------------------------------------------------- - - // 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(); - if (!m_torrents.empty() - && free_slots > -m_half_open.limit() - && num_connections() < m_settings.connections_limit - && !m_abort - && m_settings.connection_speed > 0) - { - // this is the maximum number of connections we will - // attempt this tick - int max_connections = m_settings.connection_speed; - 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()) - { - int connect_points = 100; - // have a bias against torrents with more peers - // than average - if (!t.is_seed() && t.num_peers() > average_peers) - connect_points /= 2; - // if this is a seed and there is a torrent that - // is downloading, lower the rate at which this - // torrent gets connections. - // dividing by num_seeds will have the effect - // that all seed will get as many connections - // together, as a single downloading torrent. - if (t.is_seed() && num_downloads > 0) - connect_points /= num_seeds + 1; - if (connect_points <= 0) connect_points = 1; - t.give_connect_points(connect_points); -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - if (t.try_connect_peer()) - { - --max_connections; - --free_slots; - steps_since_last_connect = 0; - } -#ifndef BOOST_NO_EXCEPTIONS - } - 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; - } -#endif - } - - ++m_next_connect_torrent; - ++steps_since_last_connect; - if (m_next_connect_torrent == m_torrents.end()) - m_next_connect_torrent = m_torrents.begin(); - - // if we have gone two whole loops without - // handing out a single connection, break - if (steps_since_last_connect > num_torrents * 2) 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; - } - } - - // -------------------------------------------------------------- - // 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 (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; - if (t->num_peers() < t->max_connections() * m_settings.peer_turnover_cutoff) - 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(); - } - -#ifndef TORRENT_DISABLE_DHT - - 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; - -#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); - 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_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 - 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(); - } - - namespace - { - bool is_active(torrent* t, session_settings const& s) - { - // if we count slow torrents, every torrent - // is considered active - if (!s.dont_count_slow_torrents) return true; - - // if the torrent started less than 2 minutes - // ago (default), let it count as active since - // the rates are probably not accurate yet - if (time_now() - t->started() < seconds(s.auto_manage_startup)) return true; - - return t->statistics().upload_payload_rate() != 0.f - || t->statistics().download_payload_rate() != 0.f; - } - } - - 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->is_paused() && !is_active(t, settings()) - && hard_limit > 0) - { - --hard_limit; - continue; - } - - if (type_limit > 0 && hard_limit > 0) - { - --hard_limit; - --type_limit; - --dht_limit; - --tracker_limit; -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING - t->log_to_all_peers(("AUTO MANAGER STARTING TORRENT: " + t->torrent_file().name()).c_str()); -#endif - t->set_announce_to_dht(dht_limit >= 0); - t->set_announce_to_trackers(tracker_limit >= 0); - t->set_allow_peers(true); - } - else - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING - t->log_to_all_peers(("AUTO MANAGER PAUSING TORRENT: " + t->torrent_file().name()).c_str()); -#endif - // use graceful pause for auto-managed torrents - t->set_allow_peers(false, true); - } - } - } - - void session_impl::recalculate_auto_managed_torrents() - { - // 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)(); - - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) - { - torrent* t = i->second.get(); - TORRENT_ASSERT(t); - if (t->is_auto_managed() && !t->has_error()) - { - // 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()) - { - --hard_limit; - if (is_active(t, settings())) - { - // this is not an auto managed torrent, - // if it's running and active, decrease the - // counters. - if (t->is_finished()) - --num_seeds; - else - --num_downloaders; - } - } - } - - bool handled_by_extension = false; - -#ifndef TORRENT_DISABLE_EXTENSIONS - // TODO: 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() - { - 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; - 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)); - - 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); - TORRENT_ASSERT(ret); - if (ret) - { - pi->optimistically_unchoked = true; - ++m_num_unchoked; - pi->last_optimistically_unchoked = 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::recalculate_unchoke_slots(int congested_torrents - , int uncongested_torrents) - { - 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 - // unchoke:able. - 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 || t->is_paused()) continue; - - if (m_settings.choking_algorithm == session_settings::bittyrant_choker) - { - if (!p->is_choked() && p->is_interesting()) - { - policy::peer* pi = p->peer_info_struct(); - 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() - || (p->share_diff() < -free_upload_amount - && !t->is_seed())) - { - // 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; - } - 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_since_unchoke() * 1000 - * (1 + t1->priority()) / total_milliseconds(unchoke_interval) - >= (*i)->uploaded_since_unchoke() * 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 = p.uploaded_since_unchoke() - * 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_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; - } - } - } - - void session_impl::main_thread() - { -#ifdef TORRENT_DEBUG -#if defined BOOST_HAS_PTHREADS - m_network_thread = pthread_self(); -#endif -#endif - TORRENT_ASSERT(is_network_thread()); - eh_initializer(); - - // initialize async operations - start(); - - 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) - (*m_logger) << time_now_string() << " locking mutex\n"; -#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) - (*m_logger) << time_now_string() << " cleaning up torrents\n"; -#endif - m_torrents.clear(); - - TORRENT_ASSERT(m_torrents.empty()); - TORRENT_ASSERT(m_connections.empty()); - } - - - // 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) - { - TORRENT_ASSERT(is_network_thread()); - - std::map >::iterator i - = m_torrents.find(info_hash); -#ifdef TORRENT_DEBUG - for (std::map >::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(); - } - -#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) - { - // current options are file_logger, cout_logger and null_logger - return boost::shared_ptr(new logger(m_logpath, name + ".log", instance, append)); - } -#endif - - std::vector session_impl::get_torrents() - { - std::vector ret; - - for (session_impl::torrent_map::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)); - } - - torrent_handle session_impl::add_torrent(add_torrent_params const& params - , error_code& ec) - { - TORRENT_ASSERT(!params.save_path.empty()); - - if (params.ti && params.ti->num_files() == 0) - { - ec = errors::no_files_in_torrent; - return torrent_handle(); - } - -// 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; - if (params.ti) ih = ¶ms.ti->info_hash(); - else ih = ¶ms.info_hash; - - // is the torrent already active? - boost::shared_ptr torrent_ptr = find_torrent(*ih).lock(); - if (torrent_ptr) - { - if (!params.duplicate_is_error) - 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)); - torrent_ptr->start(); - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - boost::shared_ptr tp((*i)(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 this is an auto managed torrent, force a recalculation - // of which torrents to have active - if (params.auto_managed && m_auto_manage_time_scaler > 2) - m_auto_manage_time_scaler = 2; - - 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) - { - TORRENT_ASSERT(*i == t || (*i)->should_check_files()); - if (*i == t) done = i; - if (next_check == t || next_check->queue_position() > (*i)->queue_position()) - next_check = *i; - } - // 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) - next_check->start_checking(); - - m_queued_for_checking.erase(done); - } - - void session_impl::remove_torrent(const torrent_handle& h, int options) - { - boost::shared_ptr tptr = h.m_torrent.lock(); - if (!tptr) -#ifdef BOOST_NO_EXCEPTIONS - return; -#else - throw_invalid_handle(); -#endif - - INVARIANT_CHECK; - - session_impl::torrent_map::iterator i = - m_torrents.find(tptr->torrent_file().info_hash()); - - if (i != m_torrents.end()) - { - torrent& t = *i->second; - if (options & session::delete_files) - t.delete_files(); - t.abort(); - -#ifdef TORRENT_DEBUG - 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; - - t.set_queue_position(-1); - 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()); - return; - } - } - - bool session_impl::listen_on( - std::pair const& port_range - , const char* net_interface, int flags) - { - INVARIANT_CHECK; - - tcp::endpoint new_interface; - if (net_interface && std::strlen(net_interface) > 0) - { - error_code ec; - new_interface = tcp::endpoint(address::from_string(net_interface, ec), port_range.first); - if (ec) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << "listen_on: " << net_interface - << " failed: " << ec.message() << "\n"; -#endif - return false; - } - } - 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 true; - - m_listen_interface = new_interface; - - open_listen_port(flags & session::listen_reuse_address); - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - m_logger = create_log("main_session", listen_port(), false); - (*m_logger) << time_now_string() << "\n"; -#endif - - return !m_listen_sockets.empty(); - } - - unsigned short 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() - && m_proxy.hostname == m_proxy.hostname) - return m_socks_listen_port; - - // if not, don't tell the tracker anything if we're in anonymous - // mode. We don't want to leak our listen port since it can - // potentially identify us if it is leaked elsewere - if (m_settings.anonymous_mode) return 0; - if (m_listen_sockets.empty()) return 0; - return m_listen_sockets.front().external_port; - } - - void session_impl::announce_lsd(sha1_hash const& ih) - { - // use internal listen port for local peers - if (m_lsd.get()) - m_lsd->announce(ih, m_listen_interface.port()); - } - - void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) - { - 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) - (*m_logger) << time_now_string() - << ": added peer from local discovery: " << peer << "\n"; -#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, 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 (!m_listen_sockets.empty()) - 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; - - 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 = m_upload_rate.queued_bytes(); - s.down_bandwidth_bytes_queue = m_download_rate.queued_bytes(); - - 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); - - // 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); - - // 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 - { - s.dht_nodes = 0; - s.dht_node_cache = 0; - s.dht_torrents = 0; - s.dht_global_nodes = 0; - s.dht_total_allocations = 0; - } -#endif - - 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 session_impl::start_dht(entry const& startup_state) - { - INVARIANT_CHECK; - - if (m_listen_interface.port() != 0) - open_listen_port(false); - - if (m_dht) - { - m_dht->stop(); - m_dht = 0; - } - 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); - - // announce all torrents we have to the DHT - for (torrent_map::const_iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) - { - i->second->dht_announce(); - } - } - -#ifndef TORRENT_DISABLE_DHT - 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; - } - } -#endif - - void session_impl::stop_dht() - { - if (!m_dht) return; - 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 - // TODO: report errors as alerts - if (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; - } - } -#endif - -#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() - { -#if defined BOOST_HAS_PTHREADS - TORRENT_ASSERT(!is_network_thread()); -#endif - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n"; -#endif - 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. -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " waiting for disk io thread\n"; -#endif - m_disk_thread.join(); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " waiting for main thread\n"; -#endif -#if defined TORRENT_ASIO_DEBUGGING - int counter = 0; - while (log_async()) - { - sleep(1000); - ++counter; - printf("\n==== Waiting to shut down: %d ==== \n\n", counter); - } -#endif - m_thread->join(); - - TORRENT_ASSERT(m_torrents.empty()); - TORRENT_ASSERT(m_connections.empty()); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " shutdown complete!\n"; -#endif - TORRENT_ASSERT(m_connections.empty()); - } - -#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() - { - if (m_settings.unchoke_slots_limit < 0) - m_settings.unchoke_slots_limit = (std::numeric_limits::max)(); - - m_allowed_upload_slots = m_settings.unchoke_slots_limit; - 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() - { - INVARIANT_CHECK; - - 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() - { -// too expensive -// INVARIANT_CHECK; - - return m_alerts.get(); - } - - 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(int m) - { - m_alerts.set_alert_mask(m); - } - - size_t session_impl::set_alert_queue_size_limit(size_t queue_size_limit_) - { - return m_alerts.set_alert_queue_size_limit(queue_size_limit_); - } - - 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)); - if (m_settings.broadcast_lsd) - m_lsd->use_broadcast(true); - } - - 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, 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) - { - m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp - , m_listen_interface.port(), m_listen_interface.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, 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) - { - m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp - , m_listen_interface.port(), m_listen_interface.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; - } - - 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; - } - m_upnp = 0; - } - - void session_impl::set_external_address(address const& ip) - { - TORRENT_ASSERT(ip != address()); - - if (is_local(ip)) return; - if (is_loopback(ip)) return; - if (m_external_address == ip) return; - - m_external_address = ip; - if (m_alerts.should_post()) - m_alerts.post_alert(external_ip_alert(ip)); - } - - 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); - } - - std::pair session_impl::allocate_buffer(int size) - { - TORRENT_ASSERT(size > 0); - int num_buffers = (size + send_buffer_size - 1) / send_buffer_size; - TORRENT_ASSERT(num_buffers > 0); - - mutex::scoped_lock l(m_send_buffer_mutex); -#ifdef TORRENT_STATS - TORRENT_ASSERT(m_buffer_allocations >= 0); - m_buffer_allocations += num_buffers; - 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 = num_buffers * send_buffer_size; - return std::make_pair((char*)malloc(num_bytes), num_bytes); -#else - return std::make_pair((char*)m_send_buffers.ordered_malloc(num_buffers) - , num_buffers * send_buffer_size); -#endif - } - -#if defined TORRENT_STATS && defined TORRENT_DISK_STATS - void session_impl::log_buffer_usage() - { - 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 / send_buffer_capacity) << std::endl; - } -#endif - - void session_impl::free_buffer(char* buf, int size) - { - TORRENT_ASSERT(size > 0); - TORRENT_ASSERT(size % send_buffer_size == 0); - int num_buffers = size / send_buffer_size; - TORRENT_ASSERT(num_buffers > 0); - - mutex::scoped_lock l(m_send_buffer_mutex); -#ifdef TORRENT_STATS - m_buffer_allocations -= num_buffers; - 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.ordered_free(buf, num_buffers); -#endif - } - -#ifdef TORRENT_DEBUG - void session_impl::check_invariant() const - { - int num_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; - } - - // the queue is either empty, or it has exactly one checking torrent in it - TORRENT_ASSERT(m_queued_for_checking.empty() || num_checking == 1); - - std::set unique; - int total_downloaders = 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 < 0) - { - TORRENT_ASSERT(pos == -1); - continue; - } - ++total_downloaders; - unique.insert(i->second->queue_position()); - } - TORRENT_ASSERT(int(unique.size()) == total_downloaders); - - std::set unique_peers; - TORRENT_ASSERT(m_settings.connections_limit > 0); - TORRENT_ASSERT(m_settings.unchoke_slots_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; - 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()); - - 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()) - { - TORRENT_ASSERT(t->get_policy().has_connection(p)); - } - } - - 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 (std::map >::const_iterator j - = m_torrents.begin(); j != m_torrents.end(); ++j) - { - TORRENT_ASSERT(boost::get_pointer(j->second)); - } - } -#endif - -}} - diff --git a/libtorrent_utp/src/settings.cpp b/libtorrent_utp/src/settings.cpp deleted file mode 100644 index 8811965ed..000000000 --- a/libtorrent_utp/src/settings.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - -Copyright (c) 2010, 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 boolean: - case 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) = val; break; - case integer: *((int*)dest) = val; break; - case floating_point: *((float*)dest) = float(val) / 1000.f; break; - case boolean: *((bool*)dest) = val; break; - } - } - } - } - } - - void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num, void const* def) - { - 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 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 floating_point: val = size_type(*((float*)src) * 1000.f); break; - case boolean: val = *((bool*)src); break; - default: TORRENT_ASSERT(false); - } - } - } - -} - diff --git a/libtorrent_utp/src/sha1.cpp b/libtorrent_utp/src/sha1.cpp deleted file mode 100644 index 39e41fec5..000000000 --- a/libtorrent_utp/src/sha1.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* -SHA-1 C++ conversion - -original version: - -SHA-1 in C -By Steve Reid -100% Public Domain - -changelog at the end of the file. -*/ - -#include "libtorrent/pch.hpp" - -#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" - -struct TORRENT_EXPORT SHA_CTX -{ - u32 state[5]; - u32 count[2]; - u8 buffer[64]; -}; - -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; - // Wipe variables - a = b = c = d = e = 0; - } - - void SHAPrintContext(SHA_CTX *context, char *msg) - { - using namespace std; - printf("%s (%d,%d) %x %x %x %x %x\n" - , msg, context->count[0], context->count[1] - , context->state[0], context->state[1] - , context->state[2], context->state[3] - , context->state[4]); - } - - 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 - } - - bool is_big_endian() - { - u32 test = 1; - return *reinterpret_cast(&test) == 0; - } -} - -// 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 __BIG_ENDIAN__ - internal_update(context, data, len); -#elif defined __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); - } -} - -/************************************************************ - ------------------ -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 __BIG_ENDIAN__ or 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/libtorrent_utp/src/smart_ban.cpp b/libtorrent_utp/src/smart_ban.cpp deleted file mode 100644 index 2146b6132..000000000 --- a/libtorrent_utp/src/smart_ban.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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" - -namespace libtorrent { - - struct torrent; - -namespace -{ - - struct smart_ban_plugin : torrent_plugin, boost::enable_shared_from_this - { - smart_ban_plugin(torrent& t) - : m_torrent(t) - , m_salt(rand()) - { - } - - 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() || 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() || 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() || 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()}; - - 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(); - - if (b.second.digest == ok_digest) return; - - policy::peer* p = b.second.peer; - - if (p == 0) return; - 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; - }; - -} } - -namespace libtorrent -{ - - boost::shared_ptr create_smart_ban_plugin(torrent* t, void*) - { - return boost::shared_ptr(new smart_ban_plugin(*t)); - } - -} - - diff --git a/libtorrent_utp/src/socket_io.cpp b/libtorrent_utp/src/socket_io.cpp deleted file mode 100644 index 4c61b6a3a..000000000 --- a/libtorrent_utp/src/socket_io.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Copyright (c) 2009, 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" - -namespace libtorrent -{ - - std::string print_address(address const& addr) - { - error_code ec; - return addr.to_string(ec); - } - - std::string print_endpoint(tcp::endpoint const& ep) - { - error_code ec; - std::string ret; - address const& addr = ep.address(); -#if TORRENT_USE_IPV6 - if (addr.is_v6()) - { - ret += '['; - ret += addr.to_string(ec); - ret += ']'; - ret += ':'; - ret += to_string(ep.port()).elems; - } - else -#endif - { - ret += addr.to_string(ec); - ret += ':'; - ret += to_string(ep.port()).elems; - } - return ret; - } - - std::string print_endpoint(udp::endpoint const& ep) - { - return print_endpoint(tcp::endpoint(ep.address(), ep.port())); - } - -} - diff --git a/libtorrent_utp/src/socket_type.cpp b/libtorrent_utp/src/socket_type.cpp deleted file mode 100644 index e93b5e29c..000000000 --- a/libtorrent_utp/src/socket_type.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - -Copyright (c) 2009, 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 -#endif - -namespace libtorrent -{ - - 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; -#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; -#endif - default: TORRENT_ASSERT(false); - } - - m_type = 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) } - -#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/libtorrent_utp/src/socks5_stream.cpp b/libtorrent_utp/src/socks5_stream.cpp deleted file mode 100644 index 7fe732cd0..000000000 --- a/libtorrent_utp/src/socks5_stream.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#include "libtorrent/socks5_stream.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/socket_io.hpp" - -namespace libtorrent -{ - - socks_error_category socks_category; - -#if BOOST_VERSION >= 103500 - const char* socks_error_category::name() const - { - return "socks error"; - } - - std::string socks_error_category::message(int ev) const - { - 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]; - } -#endif - - void socks5_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; - } - - error_code ec; - 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 - 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 (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 - } - 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)(error_code(socks_error::unsupported_version, socks_category)); - error_code ec; - close(ec); - } - } - - void socks5_stream::handshake1(error_code const& e, boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - return; - } - - 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 (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)(error_code(socks_error::unsupported_version, socks_category)); - error_code ec; - close(ec); - return; - } - - if (method == 0) - { - socks_connect(h); - } - else if (method == 2) - { - if (m_user.empty()) - { - (*h)(error_code(socks_error::username_required, socks_category)); - 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); - async_write(m_sock, asio::buffer(m_buffer) - , boost::bind(&socks5_stream::handshake3, this, _1, h)); - } - else - { - (*h)(error_code(socks_error::unsupported_authentication_method, socks_category)); - error_code ec; - close(ec); - return; - } - } - - void socks5_stream::handshake3(error_code const& e - , boost::shared_ptr h) - { - if (e) - { - (*h)(e); - error_code ec; - close(ec); - return; - } - - 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 (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)(error_code(socks_error::unsupported_authentication_version, socks_category)); - error_code ec; - close(ec); - return; - } - - if (status != 0) - { - (*h)(error_code(socks_error::authentication_error, socks_category)); - 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) - { - 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)(error_code(socks_error::unsupported_version, socks_category)); - error_code ec; - close(ec); - return; - } - - 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 (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); - 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 (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)(error_code(socks_error::unsupported_version, socks_category)); - error_code ec; - close(ec); - return; - } - if (response != 0) - { - error_code e(socks_error::general_failure, socks_category); - switch (response) - { - case 2: e = asio::error::no_permission; break; - case 3: e = asio::error::network_unreachable; break; - case 4: e = asio::error::host_unreachable; break; - case 5: e = asio::error::connection_refused; break; - case 6: e = asio::error::timed_out; break; - case 7: e = error_code(socks_error::command_not_supported, socks_category); break; - case 8: e = asio::error::address_family_not_supported; break; - } - (*h)(e); - error_code 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) - { - 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); - - 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)(error_code(socks_error::general_failure, socks_category)); - error_code ec; - close(ec); - return; - } - - // access granted - if (response == 90) - { - if (m_command == 2) - { - if (m_listen == 0) - { - 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 e(code, socks_category); - (*h)(e); - close(e); - } - } - - void socks5_stream::connect3(error_code const& e, boost::shared_ptr h) - { - using namespace libtorrent::detail; - - if (e) - { - (*h)(e); - error_code ec; - close(ec); - return; - } - - if (m_command == 2) - { - if (m_listen == 0) - { - 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/libtorrent_utp/src/stat.cpp b/libtorrent_utp/src/stat.cpp deleted file mode 100644 index fb3c4cf7f..000000000 --- a/libtorrent_utp/src/stat.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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_average = m_average * 3 / 4 + sample / 4; - m_counter = 0; -} - -} - diff --git a/libtorrent_utp/src/storage.cpp b/libtorrent_utp/src/storage.cpp deleted file mode 100644 index 140269dbe..000000000 --- a/libtorrent_utp/src/storage.cpp +++ /dev/null @@ -1,3072 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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 TORRENT_USE_IOSTREAM -#include -#include -#include -#endif - -#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" - -#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG && TORRENT_USE_IOSTREAM -namespace -{ - using namespace libtorrent; - - void print_to_log(std::string const& s) - { - static std::ofstream log("log.txt"); - log << s; - log.flush(); - } -} -#endif - -namespace libtorrent -{ - - 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(); - recursive_copy(f, combine_path(new_path, f), ec); - if (ec) return; - } - } - else if (!ec) - { - copy_file(old_path, new_path, ec); - } - } - - void recursive_remove(std::string const& old_path) - { - error_code ec; - if (is_directory(old_path, ec)) - { - for (directory i(old_path, ec); !i.done(); i.next(ec)) - recursive_remove(combine_path(old_path, i.file())); - remove(old_path, ec); - } - else - { - remove(old_path, ec); - } - } - - std::vector > get_filesizes( - file_storage const& storage, std::string const& p) - { - std::string save_path = complete(p); - std::vector > sizes; - for (file_storage::iterator i = storage.begin() - , end(storage.end()); i != end; ++i) - { - size_type size = 0; - std::time_t time = 0; - - if (!i->pad_file) - { - file_status s; - error_code ec; - stat_file(combine_path(save_path, storage.file_path(*i)), &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 (file_storage::iterator i = fs.begin() - , end(fs.end());i != end; ++i, ++size_iter) - { - size_type size = 0; - std::time_t time = 0; - if (i->pad_file) continue; - - file_status s; - error_code ec; - stat_file(combine_path(p, fs.file_path(*i)), &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; - - // 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; - } - - // 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 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 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); - } - -#ifdef TORRENT_DEBUG - 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 - - class storage : public storage_interface, boost::noncopyable - { - public: - 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.begin() != m_files.end()); - m_save_path = complete(path); - } - - void finalize_file(int file); - 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); - bool move_storage(std::string const& save_path); - 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; - int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs); - int writev(file::iovec_t const* buf, int slot, int offset, int num_bufs); - 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; - - // this identifies a read or write operation - // so that storage::readwrite() 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 (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&); - - ~storage() - { m_pool.release(this); } - - 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); - - file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; } - - 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(file_entry const& fe, 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; - - int m_page_size; - bool m_allocate_files; - }; - - 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; - } - num_read = m_storage->readv(bufs, slot, ph.offset, num_blocks); - - 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 (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); - int ret = m_storage->readv(&buf, slot, ph.offset, 1); - if (ret > 0) num_read += ret; - - 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 (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; - } - - bool storage::initialize(bool allocate_files) - { - m_allocate_files = allocate_files; - error_code ec; - // first, create all missing directories - std::string last_path; - for (file_storage::iterator file_iter = files().begin(), - end_iter = files().end(); file_iter != end_iter; ++file_iter) - { - std::string file_path = combine_path(m_save_path, files().file_path(*file_iter)); - std::string dir = parent_path(file_path); - - if (dir != last_path) - { - last_path = dir; - - if (!is_directory(last_path, ec)) - create_directories(last_path, ec); - } - - int file_index = files().file_index(*file_iter); - - // 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 (file_iter->pad_file) continue; - - // if the file is empty, just create it either way. - // if the file already exists, but is larger than what - // it's supposed to be, also truncate it - if (!allocate_files && file_iter->size > 0) continue; - - file_status s; - stat_file(file_path, &s, ec); - if (ec && ec != boost::system::errc::no_such_file_or_directory) - { - set_error(file_path, ec); - break; - } - - // ec is either ENOENT or the file existed and s is valid - if (ec || s.file_size > file_iter->size || file_iter->size == 0) - { - ec.clear(); - boost::intrusive_ptr f = open_file(*file_iter, file::read_write, ec); - if (ec) set_error(file_path, ec); - else if (f) - { - f->set_size(file_iter->size, ec); - if (ec) set_error(file_path, ec); - } - } - - if (ec) break; - } - - std::vector().swap(m_file_priority); - // close files that were opened in write mode - m_pool.release(this); - return false; - } - - void storage::finalize_file(int index) - { - TORRENT_ASSERT(index >= 0 && index < m_files.num_files()); - if (index < 0 || index >= m_files.num_files()) return; - - error_code ec; - boost::intrusive_ptr f = open_file(files().at(index), file::read_write, ec); - if (ec || !f) return; - - f->finalize(); - } - - bool storage::has_any_file() - { - file_storage::iterator i = files().begin(); - file_storage::iterator end = files().end(); - - for (; i != end; ++i) - { - error_code ec; - file_status s; - stat_file(combine_path(m_save_path, files().file_path(*i)), &s, ec); - if (ec) continue; - if (s.mode & file_status::regular_file && i->size > 0) - return true; - } - return false; - } - - bool storage::rename_file(int index, std::string const& new_filename) - { - if (index < 0 || index >= m_files.num_files()) return true; - std::string old_name = combine_path(m_save_path, files().file_path(files().at(index))); - m_pool.release(this, index); - - error_code ec; - rename(old_name, combine_path(m_save_path, new_filename), ec); - - 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 storage::release_files() - { - m_pool.release(this); - return false; - } - - void 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 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 (file_storage::iterator i = files().begin() - , end(files().end()); i != end; ++i) - { - std::string fp = files().file_path(*i); - std::string p = combine_path(m_save_path, fp); - 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 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 storage::sparse_end(int slot) const - { - TORRENT_ASSERT(slot >= 0); - TORRENT_ASSERT(slot < m_files.num_pieces()); - - size_type file_offset = (size_type)slot * m_files.piece_length(); - std::vector::const_iterator file_iter; - - for (file_iter = files().begin();;) - { - if (file_offset < file_iter->size) - break; - - file_offset -= file_iter->size; - ++file_iter; - TORRENT_ASSERT(file_iter != files().end()); - } - - error_code ec; - boost::intrusive_ptr file_handle = open_file(*file_iter, file::read_only, ec); - if (!file_handle || ec) return slot; - - size_type data_start = file_handle->sparse_end(file_offset); - return (data_start + m_files.piece_length() - 1) / m_files.piece_length(); - } - - bool storage::verify_resume_data(lazy_entry const& rd, error_code& error) - { - 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] = 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 (file_storage::iterator i = files().begin() - , end(files().end()); i != end; ++i, ++fs) - { - if (!i->pad_file && i->size != 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 - bool storage::move_storage(std::string const& sp) - { - std::string save_path = complete(sp); - - error_code ec; - file_status s; - stat_file(save_path, &s, ec); - if (ec == boost::system::errc::no_such_file_or_directory) - create_directories(save_path, ec); - else if (ec) - return false; - - m_pool.release(this); - - bool ret = true; - std::set to_move; - file_storage const& f = files(); - - for (file_storage::iterator i = f.begin() - , end(f.end()); i != end; ++i) - { - std::string split = split_path(f.file_path(*i)); - to_move.insert(to_move.begin(), split); - } - - 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 && ec != boost::system::errc::no_such_file_or_directory) - { - error_code ec; - recursive_copy(old_path, new_path, ec); - if (ec) - { - set_error(old_path, ec); - ret = false; - } - else - { - recursive_remove(old_path); - } - break; - } - } - - if (ret) m_save_path = save_path; - - return ret; - } - -#ifdef TORRENT_DEBUG -/* - void 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 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 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 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 storage::writev(file::iovec_t const* bufs, int slot, int offset - , int num_bufs) - { -#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, &storage::write_unaligned - , m_settings ? settings().disk_io_write_mode : 0, file::read_write }; -#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 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; - file_storage::iterator file_iter = files().file_at_offset(tor_off); - TORRENT_ASSERT(!file_iter->pad_file); - - size_type file_offset = tor_off - file_iter->offset; - 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_iter, file::read_only, 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; - } - - int storage::readv(file::iovec_t const* bufs, int slot, int offset - , int num_bufs) - { -#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, &storage::read_unaligned - , m_settings ? settings().disk_io_read_mode : 0, file::read_only }; -#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 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); - -#ifdef TORRENT_DEBUG - 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 - size_type file_offset = start; - std::vector::const_iterator file_iter; - - for (file_iter = files().begin();;) - { - if (file_offset < file_iter->size) - break; - - file_offset -= file_iter->size; - ++file_iter; - TORRENT_ASSERT(file_iter != files().end()); - } - - 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); - -#ifdef TORRENT_DEBUG - 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_iter, bytes_left -= file_bytes_left - , buf_pos += file_bytes_left) - { - TORRENT_ASSERT(file_iter != files().end()); - TORRENT_ASSERT(buf_pos >= 0); - - file_bytes_left = bytes_left; - if (file_offset + file_bytes_left > file_iter->size) - file_bytes_left = (std::max)(static_cast(file_iter->size - file_offset), 0); - - if (file_bytes_left == 0) continue; - -#ifdef TORRENT_DEBUG - TORRENT_ASSERT(int(slices.size()) > counter); - size_type slice_size = slices[counter].size; - TORRENT_ASSERT(slice_size == file_bytes_left); - TORRENT_ASSERT(&files().at(slices[counter].file_index) - == &*file_iter); - ++counter; -#endif - - if (file_iter->pad_file) - { - if (op.mode == 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); - continue; - } - - error_code ec; - file_handle = open_file(*file_iter, op.mode, ec); - if (!file_handle || ec) - { - std::string path = combine_path(m_save_path, files().file_path(*file_iter)); - TORRENT_ASSERT(ec); - set_error(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_iter) + 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 = (this->*op.unaligned_op)(file_handle, adjusted_offset - , tmp_bufs, num_tmp_bufs, ec); - } - else - { - bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset - , tmp_bufs, num_tmp_bufs, ec); - } - file_offset = 0; - - if (ec) - { - set_error(combine_path(m_save_path, files().file_path(*file_iter)), 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 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 block_size = disk_pool()->block_size(); - - const int size = bufs_size(bufs, num_bufs); - const int start_adjust = file_offset & pos_align; - const int 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; - const int num_blocks = (aligned_size + block_size - 1) / block_size; - TORRENT_ASSERT((aligned_size & size_align) == 0); - - disk_buffer_holder tmp_buf(*disk_pool(), disk_pool()->allocate_buffers(num_blocks, "read scratch"), num_blocks); - file::iovec_t b = {tmp_buf.get(), aligned_size}; - size_type ret = file_handle->readv(aligned_start, &b, 1, ec); - if (ret < 0) return ret; - char* read_buf = tmp_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; - } - if (ret < size + start_adjust) return ret - start_adjust; - return size; - } - - size_type storage::write_unaligned(boost::intrusive_ptr const& file_handle - , size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec) - { - TORRENT_ASSERT(false); // not implemented - return 0; - } - - int storage::write( - const char* buf - , int slot - , int offset - , int size) - { - file::iovec_t b = { (file::iovec_base_t)buf, size }; - return writev(&b, slot, offset, 1); - } - - int storage::read( - char* buf - , int slot - , int offset - , int size) - { - file::iovec_t b = { (file::iovec_base_t)buf, size }; - return readv(&b, slot, offset, 1); - } - - boost::intrusive_ptr storage::open_file(file_entry const& fe, 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 - && ((fe.offset + files().file_base(fe)) & (m_page_size-1)) == 0)) - mode |= file::no_buffer; - if (!m_allocate_files) 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, fe, 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 storage(fs, mapped, path, fp, file_prio); - } - - // 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) {} - bool has_any_file() { return false; } - bool rename_file(int index, std::string const& new_filename) { return false; } - bool release_files() { return false; } - bool delete_files() { return false; } - bool initialize(bool allocate_files) { return false; } - bool move_storage(std::string const& save_path) { return true; } - int read(char* buf, int slot, int offset, int size) { return size; } - int write(char const* buf, int slot, int offset, int size) { return size; } - size_type physical_offset(int slot, int offset) { return 0; } - int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) - { -#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 writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) - { -#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; - } - bool move_slot(int src_slot, int dst_slot) { return false; } - bool swap_slots(int slot1, int slot2) { return false; } - bool swap_slots3(int slot1, int slot2, int slot3) { return false; } - bool verify_resume_data(lazy_entry const& rd, error_code& error) { return false; } - bool write_resume_data(entry& rd) const { return false; } - - int m_piece_size; - }; - - 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_buffer(io, 0) - , m_scratch_buffer2(io, 0) - , 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; - } - - void piece_manager::finalize_file(int index) - { m_storage->finalize_file(index); } - - piece_manager::~piece_manager() - { - } - - void piece_manager::async_finalize_file(int file) - { - disk_io_job j; - j.storage = this; - j.action = disk_io_job::finalize_file; - j.piece = file; - boost::function empty; - m_io_thread.add_job(j, empty); - } - - 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 - , boost::function const& handler) - { - disk_io_job j; - j.storage = this; - j.action = disk_io_job::move_storage; - j.str = p; - 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_DEBUG - 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_DEBUG - 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::queued_bytes() const - { - return m_io_thread.queue_buffer_size(); - } - - void 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(); - m_io_thread.add_job(j, handler); - buffer.release(); - } - - 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) - { - 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); - hash_for_slot(slot, ph, m_files.piece_size(piece)); - if (m_storage->error()) return sha1_hash(0); - return ph.h.final(); - } - - int piece_manager::move_storage_impl(std::string const& save_path) - { - if (m_storage->move_storage(save_path)) - { - m_save_path = complete(save_path); - return 0; - } - return -1; - } - - 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 == storage_mode_compact) - { - 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 != storage_mode_compact) 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); - } - - 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); - 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 defined TORRENT_PARTIAL_HASH_LOG && TORRENT_USE_IOSTREAM - std::ofstream out("partial_hash.log", std::ios::app); -#endif - - 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); - -#if defined TORRENT_PARTIAL_HASH_LOG && TORRENT_USE_IOSTREAM - out << time_now_string() << " NEW [" - " s: " << this - << " p: " << piece_index - << " off: " << offset - << " size: " << size - << " entries: " << m_piece_hasher.size() - << " ]" << std::endl; -#endif - } - else - { - std::map::iterator i = m_piece_hasher.find(piece_index); - if (i != m_piece_hasher.end()) - { -#ifdef TORRENT_DEBUG - 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 == storage_mode_compact) - 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 = 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 == storage_mode_compact) - { - 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 == storage_mode_compact) - { - // 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); - return fatal_disk_error; - } - m_state = state_finished; - m_scratch_buffer.reset(); - m_scratch_buffer2.reset(); - if (m_storage_mode != storage_mode_compact) - { - // 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 = 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 = storage_mode_compact; - 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 == storage_mode_compact || 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 == storage_mode_compact) - { - 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 == storage_mode_compact) - m_free_slots.push_back(i); - } - else - { - TORRENT_ASSERT(index == unallocated); - if (m_storage_mode == storage_mode_compact) - 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 == storage_mode_compact) - { - 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 == storage_mode_compact) - { - // 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); - - TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); - - 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) - { - int blocks_per_piece = (std::max)(m_files.piece_length() - / m_io_thread.block_size(), 1); - m_scratch_buffer2.reset(m_io_thread.allocate_buffers( - blocks_per_piece, "check scratch"), blocks_per_piece); - } - - int piece_size = m_files.piece_size(other_piece); - if (m_storage->read(m_scratch_buffer2.get(), piece, 0, piece_size) - != 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); - if (m_storage->write(m_scratch_buffer.get(), piece, 0, piece_size) != 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) - { - int blocks_per_piece = (std::max)(m_files.piece_length() / m_io_thread.block_size(), 1); - m_scratch_buffer.reset(m_io_thread.allocate_buffers( - blocks_per_piece, "check scratch"), blocks_per_piece); - } - - int piece_size = m_files.piece_size(other_piece); - if (m_storage->read(m_scratch_buffer.get(), piece, 0, piece_size) != 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 == storage_mode_compact) - { - 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 != storage_mode_compact) - { - 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 (file_storage::iterator i = m_files.begin() - , end(m_files.end()); i != end; ++i) - { - file_offset += i->size; - 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 == storage_mode_compact) - { - 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 == storage_mode_compact) - 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 == storage_mode_compact) - { - 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 == storage_mode_compact) - 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 == storage_mode_compact); - 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 != storage_mode_compact) return piece_index; - - INVARIANT_CHECK; - - 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) - { - -#if defined TORRENT_DEBUG && defined TORRENT_STORAGE_DEBUG && TORRENT_USE_IOSTREAM - std::stringstream s; - - s << "there is another piece at our slot, swapping.."; - - s << "\n piece_index: "; - s << piece_index; - s << "\n slot_index: "; - s << slot_index; - s << "\n piece at our slot: "; - s << m_slot_to_piece[piece_index]; - s << "\n"; - - print_to_log(s.str()); - debug_log(); -#endif - - 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 == storage_mode_compact); - - 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 != storage_mode_compact) 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 != storage_mode_compact) return slot; - TORRENT_ASSERT(slot < int(m_slot_to_piece.size())); - TORRENT_ASSERT(slot >= 0); - return m_slot_to_piece[slot]; - } - -#ifdef TORRENT_DEBUG - 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 != storage_mode_compact - || m_files.num_pieces() == 0); - } - - if (m_storage_mode != storage_mode_compact) - { - TORRENT_ASSERT(m_unallocated_slots.empty()); - TORRENT_ASSERT(m_free_slots.empty()); - } - - if (m_storage_mode != storage_mode_compact - && 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"); - } - } - } - } - -#if defined(TORRENT_STORAGE_DEBUG) && TORRENT_USE_IOSTREAM - void piece_manager::debug_log() const - { - std::stringstream s; - - s << "index\tslot\tpiece\n"; - - for (int i = 0; i < m_files.num_pieces(); ++i) - { - s << i << "\t" << m_slot_to_piece[i] << "\t"; - s << m_piece_to_slot[i] << "\n"; - } - - s << "---------------------------------\n"; - - print_to_log(s.str()); - } -#endif -#endif -} // namespace libtorrent - diff --git a/libtorrent_utp/src/thread.cpp b/libtorrent_utp/src/thread.cpp deleted file mode 100644 index 39fda92c1..000000000 --- a/libtorrent_utp/src/thread.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - -Copyright (c) 2010, 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 - -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::condition() - { - pthread_cond_init(&m_cond, 0); - } - - condition::~condition() - { - pthread_cond_destroy(&m_cond); - } - - void condition::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::signal_all(mutex::scoped_lock& l) - { - TORRENT_ASSERT(l.locked()); - pthread_cond_broadcast(&m_cond); - } -#elif defined TORRENT_WINDOWS || defined TORRENT_CYGWIN - condition::condition() - : m_num_waiters(0) - { - m_sem = CreateSemaphore(0, 0, INT_MAX, 0); - } - - condition::~condition() - { - CloseHandle(m_sem); - } - - void condition::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::signal_all(mutex::scoped_lock& l) - { - TORRENT_ASSERT(l.locked()); - ReleaseSemaphore(m_sem, m_num_waiters, 0); - } -#else -#error not implemented -#endif - -} - diff --git a/libtorrent_utp/src/time.cpp b/libtorrent_utp/src/time.cpp deleted file mode 100644 index be1541288..000000000 --- a/libtorrent_utp/src/time.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - -Copyright (c) 2009, 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() - { -// time_t t = std::time(0); -// tm* timeinfo = std::localtime(&t); -// static char str[200]; -// std::strftime(str, 200, "%b %d %X", timeinfo); -// return str; - - 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(int s) { return boost::posix_time::seconds(s); } - time_duration milliseconds(int s) { return boost::posix_time::milliseconds(s); } - time_duration microsec(int s) { return boost::posix_time::microsec(s); } - time_duration minutes(int s) { return boost::posix_time::minutes(s); } - time_duration hours(int s) { return boost::posix_time::hours(s); } - - int total_seconds(time_duration td) - { return td.total_seconds(); } - int 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 >= 0 && 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 -{ - namespace aux - { - 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) * 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); - } -} - -#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/libtorrent_utp/src/timestamp_history.cpp b/libtorrent_utp/src/timestamp_history.cpp deleted file mode 100644 index c679e918c..000000000 --- a/libtorrent_utp/src/timestamp_history.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - -Copyright (c) 2009, 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) -{ - 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/libtorrent_utp/src/torrent.cpp b/libtorrent_utp/src/torrent.cpp deleted file mode 100644 index 0d883770b..000000000 --- a/libtorrent_utp/src/torrent.cpp +++ /dev/null @@ -1,7056 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#include -#include -#include -#include -#include - -#ifdef TORRENT_DEBUG -#include -#endif - -#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" - -#ifdef TORRENT_USE_OPENSSL -#include "libtorrent/ssl_stream.hpp" -#endif - -#if TORRENT_USE_IOSTREAM -#include -#endif - -using namespace libtorrent; -using boost::tuples::tuple; -using boost::tuples::get; -using boost::tuples::make_tuple; -using libtorrent::aux::session_impl; - -namespace -{ - size_type collect_free_download( - torrent::peer_iterator start - , torrent::peer_iterator end) - { - size_type accumulator = 0; - for (torrent::peer_iterator i = start; i != end; ++i) - { - // if the peer is interested in us, it means it may - // want to trade it's surplus uploads for downloads itself - // (and we should not consider it free). If the share diff is - // negative, there's no free download to get from this peer. - size_type diff = (*i)->share_diff(); - TORRENT_ASSERT(diff < (std::numeric_limits::max)()); - if ((*i)->is_peer_interested() || diff <= 0) - continue; - - TORRENT_ASSERT(diff > 0); - (*i)->add_free_upload(-diff); - accumulator += diff; - TORRENT_ASSERT(accumulator > 0); - } - TORRENT_ASSERT(accumulator >= 0); - return accumulator; - } - - // returns the amount of free upload left after - // it has been distributed to the peers - size_type distribute_free_upload( - torrent::peer_iterator start - , torrent::peer_iterator end - , size_type free_upload) - { - if (free_upload <= 0) return free_upload; - int num_peers = 0; - size_type total_diff = 0; - for (torrent::peer_iterator i = start; i != end; ++i) - { - size_type d = (*i)->share_diff(); - TORRENT_ASSERT(d < (std::numeric_limits::max)()); - total_diff += d; - if (!(*i)->is_peer_interested() || (*i)->share_diff() >= 0) continue; - ++num_peers; - } - - if (num_peers == 0) return free_upload; - size_type upload_share; - if (total_diff >= 0) - { - upload_share = (std::min)(free_upload, total_diff) / num_peers; - } - else - { - upload_share = (free_upload + total_diff) / num_peers; - } - if (upload_share < 0) return free_upload; - - for (torrent::peer_iterator i = start; i != end; ++i) - { - peer_connection* p = *i; - if (!p->is_peer_interested() || p->share_diff() >= 0) continue; - p->add_free_upload(upload_share); - free_upload -= upload_share; - } - return free_upload; - } - - 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 -{ - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - -#define PRINT_SIZEOF(x) l << "sizeof(" #x "): " << sizeof(x) << "\n"; -#define PRINT_OFFSETOF(x, y) l << " " << (offsetof(x, y) - temp) << " offsetof(" #x "," #y "): " << offsetof(x, y) << "\n"; temp = offsetof(x, y); - - void torrent::print_size(logger& l) - { - int temp = 0; - PRINT_SIZEOF(torrent) - - PRINT_OFFSETOF(torrent, m_policy) - PRINT_OFFSETOF(torrent, m_total_uploaded) - PRINT_OFFSETOF(torrent, m_total_downloaded) - PRINT_OFFSETOF(torrent, m_started) - PRINT_OFFSETOF(torrent, m_torrent_file) - PRINT_OFFSETOF(torrent, m_owning_storage) - PRINT_OFFSETOF(torrent, m_storage) - PRINT_OFFSETOF(torrent, m_connections) - PRINT_OFFSETOF(torrent, m_web_seeds) - PRINT_OFFSETOF(torrent, m_extensions) - PRINT_OFFSETOF(torrent, m_tracker_timer) - PRINT_OFFSETOF(torrent, m_stat) -// some compilers don't like using offsetof on references it seems -// PRINT_OFFSETOF(torrent, m_ses) - PRINT_OFFSETOF(torrent, m_file_priority) - PRINT_OFFSETOF(torrent, m_file_progress) - PRINT_OFFSETOF(torrent, m_picker) - PRINT_OFFSETOF(torrent, m_trackers) - PRINT_OFFSETOF(torrent, m_time_critical_pieces) - PRINT_OFFSETOF(torrent, m_username) - PRINT_OFFSETOF(torrent, m_password) - PRINT_OFFSETOF(torrent, m_net_interfaces) - PRINT_OFFSETOF(torrent, m_save_path) - PRINT_OFFSETOF(torrent, m_verified) - PRINT_OFFSETOF(torrent, m_error) - PRINT_OFFSETOF(torrent, m_error_file) - PRINT_OFFSETOF(torrent, m_resume_data) - PRINT_OFFSETOF(torrent, m_resume_entry) - PRINT_OFFSETOF(torrent, m_name) - PRINT_OFFSETOF(torrent, m_storage_constructor) - PRINT_OFFSETOF(torrent, m_obfuscated_hash) - PRINT_OFFSETOF(torrent, m_ratio) - PRINT_OFFSETOF(torrent, m_available_free_upload) - PRINT_OFFSETOF(torrent, m_average_piece_time) - PRINT_OFFSETOF(torrent, m_piece_time_deviation) - PRINT_OFFSETOF(torrent, m_total_failed_bytes) - PRINT_OFFSETOF(torrent, m_total_redundant_bytes) - PRINT_OFFSETOF(torrent, m_added_time) - PRINT_OFFSETOF(torrent, m_completed_time) - PRINT_OFFSETOF(torrent, m_last_seen_complete) -// PRINT_OFFSETOF(torrent, m_upload_mode_time:24) -// PRINT_OFFSETOF(torrent, m_state:3) -// PRINT_OFFSETOF(torrent, m_storage_mode:2) -// PRINT_OFFSETOF(torrent, m_announcing:1) -// PRINT_OFFSETOF(torrent, m_waiting_tracker:1) -// PRINT_OFFSETOF(torrent, m_seed_mode:1) -// PRINT_OFFSETOF(torrent, m_active_time:24) - PRINT_OFFSETOF(torrent, m_last_working_tracker) -// PRINT_OFFSETOF(torrent, m_finished_time:24) -// PRINT_OFFSETOF(torrent, m_sequential_download:1) -// PRINT_OFFSETOF(torrent, m_got_tracker_response:1) -// PRINT_OFFSETOF(torrent, m_connections_initialized:1) -// PRINT_OFFSETOF(torrent, m_super_seeding:1) -// PRINT_OFFSETOF(torrent, m_override_resume_data:1) -// PRINT_OFFSETOF(torrent, m_resolving_country:1) -// PRINT_OFFSETOF(torrent, m_resolve_countries:1) -// PRINT_OFFSETOF(torrent, m_need_save_resume_data:1) -// PRINT_OFFSETOF(torrent, m_seeding_time:24) - PRINT_OFFSETOF(torrent, m_time_scaler) -// PRINT_OFFSETOF(torrent, m_max_uploads:24) - PRINT_OFFSETOF(torrent, m_deficit_counter) -// PRINT_OFFSETOF(torrent, m_num_uploads:24) -// PRINT_OFFSETOF(torrent, m_block_size_shift:5) -// PRINT_OFFSETOF(torrent, m_has_incoming:1) -// PRINT_OFFSETOF(torrent, m_files_checked:1) -// PRINT_OFFSETOF(torrent, m_queued_for_checking:1) -// PRINT_OFFSETOF(torrent, m_max_connections:24) -// PRINT_OFFSETOF(torrent, m_padding:24) - PRINT_OFFSETOF(torrent, m_sequence_number) -// PRINT_OFFSETOF(torrent, m_complete:24) - PRINT_OFFSETOF(torrent, m_priority) -// PRINT_OFFSETOF(torrent, m_incomplete:24) -// PRINT_OFFSETOF(torrent, m_progress_ppm:20) -// PRINT_OFFSETOF(torrent, m_abort:1) -// PRINT_OFFSETOF(torrent, m_announce_to_dht:1) -// PRINT_OFFSETOF(torrent, m_announce_to_trackers:1) -// PRINT_OFFSETOF(torrent, m_announce_to_lsd:1) -// PRINT_OFFSETOF(torrent, m_allow_peers:1) -// PRINT_OFFSETOF(torrent, m_upload_mode:1) -// PRINT_OFFSETOF(torrent, m_auto_managed:1) - PRINT_OFFSETOF(torrent, m_num_verified) - PRINT_OFFSETOF(torrent, m_last_scrape) - } -#undef PRINT_SIZEOF -#undef PRINT_OFFSETOF - -#endif - - 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; - } - - // defined in ut_pex.cpp - bool was_introduced_by(peer_plugin const*, tcp::endpoint const&); - - torrent::torrent( - session_impl& ses - , tcp::endpoint const& net_interface - , int block_size - , int seq - , add_torrent_params const& p) - : m_policy(this) - , m_total_uploaded(0) - , m_total_downloaded(0) - , m_started(time_now()) - , m_torrent_file(p.ti ? p.ti : new torrent_info(p.info_hash)) - , m_storage(0) - , m_tracker_timer(ses.m_io_service) - , m_ses(ses) - , m_trackers(m_torrent_file->trackers()) - , m_save_path(complete(p.save_path)) - , m_trackerid(p.trackerid) - , m_storage_constructor(p.storage) - , m_ratio(0.f) - , m_available_free_upload(0) - , m_average_piece_time(0) - , m_piece_time_deviation(0) - , m_total_failed_bytes(0) - , m_total_redundant_bytes(0) - , m_added_time(time(0)) - , m_completed_time(0) - , m_last_seen_complete(0) - , m_last_saved_resume(time(0)) - , 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(p.seed_mode && m_torrent_file->is_valid()) - , 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(p.ti) - , m_super_seeding(false) - , m_override_resume_data(p.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(~0) - , m_deficit_counter(0) - , m_num_uploads(0) - , m_block_size_shift(root2(p.ti ? (std::min)(block_size, m_torrent_file->piece_length()) : block_size)) - , m_has_incoming(false) - , m_files_checked(false) - , m_queued_for_checking(false) - , m_max_connections(~0) - , m_padding(0) - , m_sequence_number(seq) - , m_complete(0xffffff) - , m_priority(0) - , m_incomplete(0xffffff) - , m_progress_ppm(0) - , m_abort(false) - , m_announce_to_dht(!p.paused) - , m_announce_to_trackers(!p.paused) - , m_announce_to_lsd(!p.paused) - , m_allow_peers(!p.paused) - , m_upload_mode(p.upload_mode) - , m_auto_managed(p.auto_managed) - , m_share_mode(p.share_mode) - , m_num_verified(0) - , m_last_scrape(0) - , m_last_download(0) - , m_last_upload(0) - , m_downloaders(0xffffff) - , m_interface_index(0) - , m_graceful_pause_mode(false) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " creating torrent: " - << torrent_file().name() << "\n"; -#endif - m_net_interfaces.push_back(tcp::endpoint(net_interface.address(), 0)); - - if (p.file_priorities) - m_file_priority = *p.file_priorities; - - if (m_seed_mode) - m_verified.resize(m_torrent_file->num_pieces(), false); - - if (p.resume_data) m_resume_data.swap(*p.resume_data); - - if (settings().prefer_udp_trackers) - prioritize_udp_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 - -#ifdef TORRENT_DEBUG - m_files_checked = false; -#endif - INVARIANT_CHECK; - - if (p.name && !p.ti) m_name.reset(new std::string(p.name)); - - 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); - } - } - - void torrent::start() - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " starting torrent: " - << torrent_file().name() << "\n"; -#endif - TORRENT_ASSERT(!m_picker); - - if (!m_seed_mode) - { - m_picker.reset(new piece_picker()); - 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); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << time_now_string() << " fastresume data for " - << torrent_file().name() << " rejected: " << ec.message() - << " pos: " << pos << "\n"; -#endif - if (m_ses.m_alerts.should_post()) - { - m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), ec)); - } - } - } - } - - // we need to start announcing since we don't have any - // metadata. To receive peers to ask for it. - if (m_torrent_file->is_valid()) - { - init(); - } - else - { - set_state(torrent_status::downloading_metadata); - start_announcing(); - } - } - -#ifndef TORRENT_DISABLE_DHT - bool torrent::should_announce_dht() const - { - 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; - - // 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() - { - // 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. - - TORRENT_ASSERT(m_connections.empty()); - - 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); - if (!m_connections.empty()) - disconnect_all(errors::torrent_aborted); - } - - void torrent::read_piece(int piece) - { - 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(); - - 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 - 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_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; - - 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 - { - // 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) - { - if (!j.error) return; - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << "disk error: '" << j.error.message() - << " in file " << j.error_file - << " in torrent " << torrent_file().name() - << "\n"; -#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 == -#if BOOST_VERSION == 103500 - error_code(boost::system::posix_error::not_enough_memory, get_posix_category()) -#elif BOOST_VERSION > 103500 - error_code(boost::system::errc::not_enough_memory, get_posix_category()) -#else - asio::error::no_memory -#endif - ) - { - 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)); - - if (j.action == disk_io_job::write) - { - // if we failed to write, stop downloading and just - // keep seeding. - // TODO: 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; - } - - // put the torrent in an error-state - set_error(j.error, j.error_file); - 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; - 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) - { - rp->piece_data.reset(); - size = 0; - } - - if (m_ses.m_alerts.should_post()) - { - 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(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(); - - // avoid crash trying to access the picker when there is nont - if (is_seed()) 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); - 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); - 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); - } - - 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 (is_seed()) return; - - 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 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 - - // this may not be called from a constructor because of the call to - // shared_from_this() - void torrent::init() - { - TORRENT_ASSERT(m_torrent_file->is_valid()); - TORRENT_ASSERT(m_torrent_file->num_files() > 0); - TORRENT_ASSERT(m_torrent_file->total_size() >= 0); - - m_file_priority.resize(m_torrent_file->num_files(), 1); - 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; - } - - // 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 (has_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()); - } - - 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); - } - - // in case file priorities were passed in via the add_torrent_params - // ans 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()); - - 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); - return; - } - - set_state(torrent_status::checking_resume_data); - - 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 - (*m_ses.m_logger) << time_now_string() << " fastresume data for " - << torrent_file().name() << " rejected: " - << error_code(ev, get_libtorrent_category()).message() << "\n"; -#endif - std::vector().swap(m_resume_data); - lazy_entry().swap(m_resume_entry); - } - else - { - read_resume_data(m_resume_entry); - } - } - - TORRENT_ASSERT(block_size() > 0); - int file = 0; - for (file_storage::iterator i = m_torrent_file->files().begin() - , end(m_torrent_file->files().end()); i != end; ++i, ++file) - { - if (!i->pad_file) continue; - m_padding += i->size; - - peer_request pr = m_torrent_file->map_file(file, 0, m_torrent_file->file_at(file).size); - 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_size()); - for (; pr.length >= block; pr.length -= block, ++pb.block_index) - { - if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; } - m_picker->mark_as_finished(pb, 0); - } - } - - 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); - set_state(torrent_status::queued_for_checking); - std::vector().swap(m_resume_data); - lazy_entry().swap(m_resume_entry); - return; - } - - 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 - (*m_ses.m_logger) << time_now_string() << " fastresume data for " - << torrent_file().name() << " rejected: " - << j.error.message() << " ret:" << ret << "\n"; -#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 j = 0; j < num_bitmask_bytes; ++j) - { - unsigned char bits = bitmask[j]; - int num_bits = (std::min)(num_blocks_per_piece - j*8, 8); - for (int k = 0; k < num_bits; ++k) - { - const int bit = j * 8 + k; - if (bits & (1 << k)) - { - m_picker->mark_as_finished(piece_block(piece, bit), 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() - { - if (m_queued_for_checking) return; - m_queued_for_checking = true; - m_ses.queue_check_torrent(shared_from_this()); - } - - void torrent::dequeue_torrent_check() - { - 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()); - - 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; - - 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 - (*m_ses.m_logger) << time_now_string() << ": fatal disk error [" - " error: " << j.error.message() << - " torrent: " << torrent_file().name() << - " ]\n"; -#endif - pause(); - set_error(j.error, j.error_file); - 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.piece); - - // 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 = &net_interfaces[0]; - - while (str) - { - char* space = strchr(str, ','); - if (space) *space++ = 0; - error_code ec; - address a(address::from_string(str, ec)); - str = space; - if (ec) continue; - m_net_interfaces.push_back(tcp::endpoint(a, 0)); - } - } - - 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 - if (!m_files_checked) 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; - - // announce with the local discovery service - m_ses.announce_lsd(m_torrent_file->info_hash()); - } - -#ifndef TORRENT_DISABLE_DHT - - void torrent::dht_announce() - { - if (!m_ses.m_dht) return; - if (!should_announce_dht()) return; - - TORRENT_ASSERT(m_allow_peers); - - boost::weak_ptr self(shared_from_this()); - m_ses.m_dht->announce(m_torrent_file->info_hash() - , m_ses.listen_port() - , 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) - { - 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)); - } - -#endif - - void torrent::announce_with_tracker(tracker_request::event_t e - , address const& bind_interface) - { - INVARIANT_CHECK; - - if (m_trackers.empty()) 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) 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.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; - - // 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 (!m_ses.m_settings.anonymous_mode) - { - tcp::endpoint ep; - ep = m_ses.get_ipv6_interface(); - if (ep != tcp::endpoint()) req.ipv6 = ep.address().to_string(ec); - ep = m_ses.get_ipv4_interface(); - if (ep != tcp::endpoint()) req.ipv4 = ep.address().to_string(ec); - } - - // if we are aborting. we don't want any new peers - req.num_want = (req.event == tracker_request::stopped) - ?0:settings().num_want; - - req.listen_port = m_ses.listen_port(); - req.key = m_ses.m_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 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 && !settings().announce_to_all_tiers) break; - if (ae.is_working()) { tier = ae.tier; sent_announce = false; } - if (!ae.can_announce(now, is_seed())) - { - if (ae.is_working()) - { - sent_announce = true; // this counts - - if (!settings().announce_to_all_trackers - && !settings().announce_to_all_tiers) - break; - } - 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().anonymous_mode) - { - // in anonymous_mode we don't talk directly to trackers - // only if there is a proxy - std::string protocol = req.url.substr(0, req.url.find(':')); - int proxy_type = m_ses.m_proxy.type; - - 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; - } - - 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 - (*m_ses.m_logger) << time_now_string() << " ==> TACKER REQUEST " << req.url - << " event=" << (req.event==tracker_request::stopped?"stopped" - :req.event==tracker_request::started?"started":"") - << " abort=" << m_abort << "\n"; - 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() - { - m_last_scrape = 0; - - if (m_trackers.empty()) return; - - int i = m_last_working_tracker; - if (i == -1) i = 0; - - tracker_request req; - 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); - - if (complete >= 0) m_complete = complete; - if (incomplete >= 0) m_incomplete = incomplete; - if (downloaders >= 0) m_downloaders = downloaders; - - if (m_ses.m_alerts.should_post()) - { - m_ses.m_alerts.post_alert(scrape_reply_alert( - get_handle(), m_incomplete, m_complete, req.url)); - } - } - - 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 - , 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()) - m_ses.set_external_address(external_ip); - - ptime now = time_now(); - - if (interval < settings().min_announce_interval) - interval = settings().min_announce_interval; - - announce_entry* ae = find_tracker(r); - if (ae) - { - 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_tracker_timer(now); - - if (complete >= 0) m_complete = complete; - if (incomplete >= 0) m_incomplete = incomplete; - if (complete >= 0 && incomplete >= 0) - m_last_scrape = 0; - -#if (defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING) && TORRENT_USE_IOSTREAM - std::stringstream s; - s << "TRACKER RESPONSE:\n" - "interval: " << interval << "\n" - "peers:\n"; - for (std::vector::const_iterator i = peer_list.begin(); - i != peer_list.end(); ++i) - { - s << " " << std::setfill(' ') << std::setw(16) << i->ip - << " " << std::setw(5) << std::dec << i->port << " "; - if (!i->pid.is_all_zeros()) s << " " << i->pid << " " << identify_client(i->pid); - s << "\n"; - } - s << "external ip: " << external_ip << "\n"; - s << "tracker ips: "; - std::copy(tracker_ips.begin(), tracker_ips.end(), std::ostream_iterator
(s, " ")); - s << "\n"; - s << "we connected to: " << tracker_ip << "\n"; - debug_log(s.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_ses.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) - 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) && TORRENT_USE_IOSTREAM - debug_log("announce again using " + print_address(bind_interface) - + " as the bind interface"); -#endif - } - } - } - - ptime torrent::next_announce() const - { - return m_waiting_tracker?m_tracker_timer.expires_at():min_time(); - } - - void torrent::force_tracker_request() - { - force_tracker_request(time_now_hires()); - } - - void torrent::force_tracker_request(ptime t) - { - if (is_paused()) return; - 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); - 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) - *m_ses.m_logger << time_now_string() << " on_i2p_resolve: " << ec.message() << "\n"; -#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) - *m_ses.m_logger << time_now_string() << " on_peer_name_lookup: " << e.message() << "\n"; -#endif - if (e || host == tcp::resolver::iterator() || - m_ses.is_aborted()) return; - - if (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: " + host->endpoint().address().to_string(ec)); -#endif - if (m_ses.m_alerts.should_post()) - m_ses.m_alerts.post_alert(peer_blocked_alert(get_handle(), host->endpoint().address())); - 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(); - - 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) - { - file_entry const& fe = fs.at(i->file_index); - if (fe.pad_file) 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 >= 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 (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); - - // subtract padding files - if (m_padding > 0) - { - file_storage const& files = m_torrent_file->files(); - int fileno = 0; - for (file_storage::iterator i = files.begin() - , end(files.end()); i != end; ++i, ++fileno) - { - if (!i->pad_file) continue; - peer_request p = files.map_file(fileno, 0, i->size); - for (int j = p.piece; p.length > 0; ++j, p.length -= piece_size) - { - int deduction = (std::min)(p.length, piece_size); - 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 <= 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); - - // this is expensive, we might not want to do it all the time - if (!accurate) return; - - 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)); - -#ifdef TORRENT_DEBUG - 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", i->first.piece_index, 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) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " *** PIECE_FINISHED [ p: " - << index << " chk: " << ((passed_hash_check == 0) - ?"passed":passed_hash_check == -1 - ?"disk failed":"failed") << " ]\n"; -#endif - - TORRENT_ASSERT(valid_metadata()); - - TORRENT_ASSERT(!m_picker->have_piece(index)); - - // 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) - { - // 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; - file_storage::iterator f = m_torrent_file->files().file_at_offset(off); - int size = m_torrent_file->piece_size(index); - int file_index = f - m_torrent_file->files().begin(); - for (; size > 0; ++f, ++file_index) - { - size_type file_offset = off - f->offset; - TORRENT_ASSERT(f != m_torrent_file->files().end()); - TORRENT_ASSERT(file_offset <= f->size); - int add = (std::min)(f->size - file_offset, (size_type)size); - m_file_progress[file_index] += add; - - TORRENT_ASSERT(m_file_progress[file_index] - <= m_torrent_file->files().at(file_index).size); - - if (m_file_progress[file_index] >= m_torrent_file->files().at(file_index).size) - { - if (!m_torrent_file->files().at(file_index).pad_file) - { - if (m_owning_storage.get()) - m_storage->async_finalize_file(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); - } - - m_picker->we_have(index); - } - - void torrent::piece_passed(int index) - { -// INVARIANT_CHECK; - - 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; - - 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; - std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); - - we_have(index); - - for (peer_iterator i = m_connections.begin(); i != m_connections.end();) - { - peer_connection* p = *i; - ++i; - p->announce_piece(index); - } - - for (std::set::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - policy::peer* p = static_cast(*i); - if (p == 0) continue; - 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) p->connection->received_valid_data(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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - (*i)->on_piece_pass(index); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#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_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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - (*i)->on_piece_failed(index); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#endif - - for (std::set::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - policy::peer* p = static_cast(*i); - if (p == 0) continue; - if (p->connection) p->connection->received_invalid_data(index); - - // either, we have received too many failed hashes - // or this was the only peer that sent us this piece. - if (p->trust_points <= -7 - || peers.size() == 1) - { - // 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); - - if (p->connection) - { -#ifdef TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " *** BANNING PEER [ " << p->ip() - << " ] 'too many corrupt pieces'\n"; -#endif -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*p->connection->m_logger) << "*** BANNING PEER [ " << p->ip() - << " ] 'too many corrupt pieces'\n"; -#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 (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 (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() - { - INVARIANT_CHECK; - - if (m_abort) return; - - m_abort = true; - // 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_torrent_aborted, shared_from_this(), _1, _2)); - } - - dequeue_torrent_check(); - - if (m_state == torrent_status::checking_files) - set_state(torrent_status::queued_for_checking); - - m_owning_storage = 0; - m_ses.m_host_resolver.cancel(); - } - - void torrent::super_seeding(bool on) - { - if (on == m_super_seeding) return; - - // don't turn on super seeding if we're not a seed - TORRENT_ASSERT(!on || is_seed() || !m_files_checked); - if (on && !is_seed() && m_files_checked) 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); - } - } - - 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)->superseed_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); - } - - if (min_availability > 1) - { - // if the minimum availability is 2 or more, - // we shouldn't be super seeding any more - super_seeding(false); - return -1; - } - - return avail_vec[rand() % 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) - { - if (alerts().should_post()) - alerts().post_alert(torrent_delete_failed_alert(get_handle(), j.error)); - } - else - { - if (alerts().should_post()) - 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_torrent_aborted(int ret, disk_io_job const& j) - { - // the torrent should be completely shut down now, and the - // destructor has to be called from the main thread - } - - 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())); - } - } - - 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; - } - - void torrent::set_piece_deadline(int piece, int t, int flags) - { - ptime deadline = time_now() + milliseconds(t); - - if (is_seed() || m_picker->have_piece(piece)) - { - if (flags & torrent_handle::alert_when_available) - read_piece(piece); - return; - } - - for (std::list::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::next(i)); - --i; - } - 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::list::iterator i = std::upper_bound(m_time_critical_pieces.begin() - , m_time_critical_pieces.end(), p); - m_time_critical_pieces.insert(i, p); - } - - void torrent::remove_time_critical_piece(int piece, bool finished) - { - for (std::list::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); - } - - // 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 * 6 + diff * 4) / 10; - - m_average_piece_time = (m_average_piece_time * 6 + dl_time * 4) / 10; - } - } - m_time_critical_pieces.erase(i); - return; - } - } - - // remove time critical pieces where priority is 0 - void torrent::remove_time_critical_pieces(std::vector const& priority) - { - for (std::list::iterator i = m_time_critical_pieces.begin(); - i != m_time_critical_pieces.end();) - { - if (priority[i->piece] == 0) - { - i = m_time_critical_pieces.erase(i); - continue; - } - ++i; - } - } - - void torrent::piece_availability(std::vector& avail) const - { - INVARIANT_CHECK; - - TORRENT_ASSERT(valid_metadata()); - if (is_seed()) - { - 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()); - - 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()); - - 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) - { - update_peer_interest(was_finished); - remove_time_critical_pieces(pieces); - } - } - - 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 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; - - std::copy(files.begin(), files.end(), m_file_priority.begin()); - 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; - - TORRENT_ASSERT(index < m_torrent_file->num_files()); - TORRENT_ASSERT(index >= 0); - if (m_file_priority[index] == prio) return; - m_file_priority[index] = prio; - update_piece_priorities(); - } - - int torrent::file_priority(int index) const - { - // this call is only valid on torrents with metadata - if (!valid_metadata()) return 1; - - TORRENT_ASSERT(index < m_torrent_file->num_files()); - TORRENT_ASSERT(index >= 0); - return m_file_priority[index]; - } - - void torrent::file_priorities(std::vector& files) const - { - INVARIANT_CHECK; - files.resize(m_file_priority.size()); - 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); - for (int i = 0; i < int(m_file_priority.size()); ++i) - { - size_type start = position; - size_type size = m_torrent_file->files().at(i).size; - 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(); - } - - // 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()); - - 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()); - - 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()); - - 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().at(i).size; - // 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); - } - } - - void torrent::replace_trackers(std::vector const& urls) - { - m_trackers.clear(); - std::remove_copy_if(urls.begin(), urls.end(), back_inserter(m_trackers) - , boost::bind(&std::string::empty, boost::bind(&announce_entry::url, _1))); - - 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; - - if (settings().prefer_udp_trackers) - prioritize_udp_trackers(); - - if (!m_trackers.empty()) announce_with_tracker(); - } - - 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; - } - } - } - - void 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; - } - 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(); - } - - 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; - 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; - 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); - } - } - - void torrent::remove_peer(peer_connection* p) - { -// INVARIANT_CHECK; - - TORRENT_ASSERT(p != 0); - - 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() == this); - - if (p->is_seed()) - { - if (m_picker.get()) - { - m_picker->dec_refcount_all(); - } - } - else - { - if (m_picker.get()) - { - bitfield const& pieces = p->get_bitfield(); - TORRENT_ASSERT(pieces.count() < int(pieces.size())); - m_picker->dec_refcount(pieces); - } - } - } - - 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; - - // if the share ratio is 0 (infinite), the - // m_available_free_upload isn't used, - // because it isn't necessary. - if (ratio() != 0.f) - { - TORRENT_ASSERT(p->associated_torrent().lock().get() == this); - TORRENT_ASSERT(p->share_diff() < (std::numeric_limits::max)()); - m_available_free_upload += p->share_diff(); - } - TORRENT_ASSERT(pp->prev_amount_upload == 0); - TORRENT_ASSERT(pp->prev_amount_download == 0); - pp->prev_amount_download += p->statistics().total_payload_download(); - pp->prev_amount_upload += p->statistics().total_payload_upload(); - } - - 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::connect_to_url_seed(std::list::iterator web) - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!web->resolving); - if (web->resolving) return; - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " resolving web seed: " << web->url << "\n"; -#endif - - 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 (ec) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " failed to parse web seed url: " << ec.message() << "\n"; -#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 - m_web_seeds.erase(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 - m_web_seeds.erase(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 - m_web_seeds.erase(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 - m_web_seeds.erase(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 - m_web_seeds.erase(web); - return; - } - - if (web->endpoint.port() != 0) - { - connect_web_seed(web, web->endpoint); - return; - } - - proxy_settings const& ps = m_ses.proxy(); - if (ps.type == proxy_settings::http - || ps.type == proxy_settings::http_pw) - { - // use proxy - tcp::resolver::query q(ps.hostname, to_string(ps.port).elems); - m_ses.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 - { - web->resolving = true; - tcp::resolver::query q(hostname, to_string(port).elems); - m_ses.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; - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " completed resolve proxy hostname for: " << web->url << "\n"; - if (e) - *m_ses.m_logger << time_now_string() << " on_proxy_name_lookup: " << e.message() << "\n"; -#endif - - 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 - m_web_seeds.erase(web); - return; - } - - if (m_ses.is_aborted()) return; - - tcp::endpoint a(host->endpoint()); - - using boost::tuples::ignore; - std::string hostname; - int port; - error_code ec; - boost::tie(ignore, ignore, hostname, port, 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)); - } - m_web_seeds.erase(web); - return; - } - - if (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())); - return; - } - - web->resolving = true; - tcp::resolver::query q(hostname, to_string(port).elems); - m_ses.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 - (*m_ses.m_logger) << time_now_string() << " completed resolve: " << web->url << "\n"; -#endif - web->resolving = false; - - 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 - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << web->url - << " " << e.message() << "\n"; -#endif - - // unavailable, retry in 30 minutes - web->retry = time_now() + minutes(30); - return; - } - - tcp::endpoint a(host->endpoint()); - connect_web_seed(web, a); - } - - void torrent::connect_web_seed(std::list::iterator web, tcp::endpoint const& a) - { - if (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())); - return; - } - - TORRENT_ASSERT(web->resolving == false); - TORRENT_ASSERT(web->connection == 0); - - web->endpoint = a; - - 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; - - bool ssl = string_begins_no_case("https://", web->url.c_str()); - void* userdata = 0; -#ifdef TORRENT_USE_OPENSSL - if (ssl) userdata = &m_ses.m_ssl_ctx; -#endif - bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata); - (void)ret; - TORRENT_ASSERT(ret); - - proxy_settings const& ps = m_ses.proxy(); - if ((ps.type == proxy_settings::http - || ps.type == proxy_settings::http_pw) - && !ssl) - { - // the web seed connection will talk immediately to - // the proxy, without requiring CONNECT support - s->get()->set_no_connect(true); - } - - 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 -#ifdef TORRENT_USE_OPENSSL - socks5_stream* str = ssl - ? &s->get >()->next_layer().next_layer() - : s->get(); -#else - socks5_stream* str = s->get(); -#endif - TORRENT_ASSERT(str); - - using boost::tuples::ignore; - std::string hostname; - error_code ec; - boost::tie(ignore, ignore, hostname, ignore, ignore) - = parse_url_components(web->url, ec); - str->set_dst_name(hostname); - } - - 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->url, 0, // TODO: pass in web - web->auth, web->extra_headers); - } - else if (web->type == web_seed_entry::http_seed) - { - c = new (std::nothrow) http_seed_connection( - m_ses, shared_from_this(), s, a, web->url, 0, // TODO: pass in web - web->auth, web->extra_headers); - } - if (!c) return; - -#ifdef TORRENT_DEBUG - 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 - -#ifndef BOOST_NO_EXCEPTIONS - try - { -#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); - - TORRENT_ASSERT(!web->connection); - web->connection = c.get(); - -#if defined TORRENT_VERBOSE_LOGGING - (*m_ses.m_logger) << time_now_string() << " web seed connection started " << web->url << "\n"; -#endif - - c->start(); - - 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)); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; -#endif - c->disconnect(errors::no_error, 1); - } -#endif - } - -#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 - { - 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_ses.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* i = - std::lower_bound(country_map, country_map + size, tmp - , boost::bind(&country_entry::code, _1) < boost::bind(&country_entry::code, _2)); - if (i == country_map + size - || i->code != country) - { - // unknown country! - p->set_country("!!"); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << "IP " << p->remote().address() << " was mapped to unknown country: " << country << "\n"; -#endif - return; - } - - p->set_country(i->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_seeds", 0xffffff); - m_incomplete = rd.dict_find_int_value("num_incomplete", 0xffffff); - m_downloaders = rd.dict_find_int_value("num_downloaders", 0xffffff); - set_upload_limit(rd.dict_find_int_value("upload_rate_limit", -1)); - set_download_limit(rd.dict_find_int_value("download_rate_limit", -1)); - set_max_connections(rd.dict_find_int_value("max_connections", -1)); - set_max_uploads(rd.dict_find_int_value("max_uploads", -1)); - m_seed_mode = rd.dict_find_int_value("seed_mode", 0) && m_torrent_file->is_valid(); - if (m_seed_mode) m_verified.resize(m_torrent_file->num_pieces(), false); - super_seeding(rd.dict_find_int_value("super_seeding", 0)); - - 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); - - 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; - - 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* piece_priority = rd.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_override_resume_data) - { - 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_); - - if (!m_override_resume_data) - { - int paused_ = rd.dict_find_int_value("paused", -1); - if (paused_ != -1) - { - m_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_; - } - - lazy_entry const* trackers = rd.dict_find_list("trackers"); - if (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* 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); - } - } - - 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; - 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: 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 - TORRENT_ASSERT(false); - } - } - } - - 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_seeds"] = m_complete; - ret["num_incomplete"] = m_incomplete; - ret["num_downloaders"] = m_downloaders; - - 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; - - const sha1_hash& info_hash = torrent_file().info_hash(); - ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); - - // 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 (!is_seed()) - { - 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 - if (!m_trackers.empty()) - { - 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: save the send_stats state instead - 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 - { - 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 - if (&m_torrent_file->files() != &m_torrent_file->orig_files()) - { - entry::list_type& fl = ret["mapped_files"].list(); - for (torrent_info::file_iterator i = m_torrent_file->begin_files() - , end(m_torrent_file->end_files()); i != end; ++i) - { - fl.push_back(m_torrent_file->files().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); - - 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; - -#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); - } - } - - ret["upload_rate_limit"] = upload_limit(); - ret["download_rate_limit"] = download_limit(); - ret["max_connections"] = max_connections(); - ret["max_uploads"] = max_uploads(); - ret["paused"] = !m_allow_peers; - 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 - { - 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; - - // 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) - { - queue.clear(); - std::vector& blk = m_ses.m_block_info_storage; - blk.clear(); - - if (!valid_metadata() || is_seed()) 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); - - 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; - 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) - { - INVARIANT_CHECK; - - TORRENT_ASSERT(peerinfo); - TORRENT_ASSERT(peerinfo->connection == 0); - - peerinfo->last_connected = m_ses.session_time(); -#ifdef TORRENT_DEBUG - // 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 - - 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_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) - { - 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()); - } - 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; - - bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, 0, sm); - (void)ret; - TORRENT_ASSERT(ret); - } - - m_ses.setup_socket_buffers(*s); - - boost::intrusive_ptr c(new bt_peer_connection( - m_ses, shared_from_this(), s, a, peerinfo)); - -#ifdef TORRENT_DEBUG - c->m_in_constructor = false; -#endif - - if (settings().default_peer_upload_rate) - c->set_upload_limit(settings().default_peer_upload_rate); - if (settings().default_peer_download_rate) - c->set_download_limit(settings().default_peer_download_rate); - - c->add_stat(peerinfo->prev_amount_download, peerinfo->prev_amount_upload); - 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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - boost::shared_ptr pp((*i)->new_connection(c.get())); - if (pp) c->add_extension(pp); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#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; - -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - m_ses.m_half_open.enqueue( - boost::bind(&peer_connection::on_connect, c, _1) - , boost::bind(&peer_connection::on_timeout, c) - , seconds(timeout)); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - 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; - } -#endif - - if (m_share_mode) - recalc_share_mode(); - - return peerinfo->connection; - } - - bool torrent::set_metadata(char const* metadata_buf, int metadata_size) - { - 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())); - } - 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()) - { - // TODO: pass in ec along with the alert - alerts().post_alert(metadata_failed_alert(get_handle())); - } - 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; - - init(); - - return true; - } - - bool torrent::attach_peer(peer_connection* p) - { -// INVARIANT_CHECK; - - TORRENT_ASSERT(p != 0); - TORRENT_ASSERT(!p->is_local()); - - m_has_incoming = true; - - 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; - } - - if (int(m_connections.size()) >= m_max_connections) - { - p->disconnect(errors::too_many_connections); - return false; - } - -#ifndef BOOST_NO_EXCEPTIONS - try - { -#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(p)); - if (pp) p->add_extension(pp); - } -#endif - if (!m_policy.new_connection(*p, m_ses.session_time())) - return false; -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { -#if defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " CLOSING CONNECTION " - << p->remote() << " policy::new_connection threw: " << e.what() << "\n"; -#endif - p->disconnect(errors::no_error); - return false; - } -#endif - TORRENT_ASSERT(m_connections.find(p) == m_connections.end()); - peer_iterator ci = m_connections.insert(p).first; -#ifdef TORRENT_DEBUG - error_code ec; - TORRENT_ASSERT(p->remote() == p->get_socket()->remote_endpoint(ec) || ec); -#endif - -#if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS - m_policy.check_invariant(); -#endif - - if (m_share_mode) - recalc_share_mode(); - - return true; - } - - bool torrent::want_more_peers() const - { - return int(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; - } - - 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->m_logger) << "*** CLOSING CONNECTION: " << ec.message() << "\n"; -#endif -#ifdef TORRENT_DEBUG - 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); -#ifdef TORRENT_DEBUG - int num_conns = m_connections.size(); -#endif - p->disconnect(ec); - TORRENT_ASSERT(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); - - if (alerts().should_post()) - { - alerts().post_alert(torrent_finished_alert( - get_handle())); - } - - 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(); - - m_completed_time = time(0); - - // disconnect all seeds - // TODO: should disconnect all peers that have the pieces we have - // not just seeds - 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->m_logger) << "*** SEED, CLOSING CONNECTION\n"; -#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 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; - - 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; - - 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) 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) - 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())); - } - - 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 - if (m_ses.m_auto_manage_time_scaler > 1) - m_ses.m_auto_manage_time_scaler = 1; - - 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) - finished(); - } - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - (*i)->on_files_checked(); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#endif - - 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(); - } - } - - m_files_checked = true; - - start_announcing(); - } - - alert_manager& torrent::alerts() const - { - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - INVARIANT_CHECK; - - if (m_owning_storage.get()) - { - m_owning_storage->async_move_storage(save_path - , boost::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2)); - } - else - { - m_save_path = save_path; - 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()); - - if (ret == 0) - { - if (alerts().should_post()) - { - alerts().post_alert(storage_moved_alert(get_handle(), j.str)); - } - m_save_path = j.str; - } - 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(); - } - -#ifdef TORRENT_DEBUG - void torrent::check_invariant() const - { - 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_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; - } - // 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); - } - - 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 = begin(); i != 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 == 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 (!m_picker->is_downloaded(b)) - 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; - } - } - } - - if (m_files_checked && valid_metadata()) - { - TORRENT_ASSERT(block_size() > 0); - } -// if (is_seed()) TORRENT_ASSERT(m_picker.get() == 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().at(index).size); - } - } -#endif - - void torrent::set_sequential_download(bool sd) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - m_sequential_download = sd; - } - - 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; - - 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; - } - 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; - } - 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; - } - 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; - - } - m_sequence_number = (std::min)(max_seq, p); - } - - if (m_ses.m_auto_manage_time_scaler > 2) - m_ses.m_auto_manage_time_scaler = 2; - } - - void torrent::set_max_uploads(int limit) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - TORRENT_ASSERT(limit >= -1); - if (limit <= 0) limit = (std::numeric_limits::max)(); - m_max_uploads = limit; - } - - void torrent::set_max_connections(int limit) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - TORRENT_ASSERT(limit >= -1); - if (limit <= 0) limit = (std::numeric_limits::max)(); - m_max_connections = limit; - - if (num_peers() > m_max_connections) - { - disconnect_peers(num_peers() - m_max_connections - , error_code(errors::too_many_connections, get_libtorrent_category())); - } - } - - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - TORRENT_ASSERT(limit >= -1); - if (limit <= 0) limit = 0; - m_bandwidth_channel[peer_connection::upload_channel].throttle(limit); - } - - 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) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - TORRENT_ASSERT(limit >= -1); - if (limit <= 0) limit = 0; - m_bandwidth_channel[peer_connection::download_channel].throttle(limit); - } - - 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; - } - - void 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(); - - 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)); - } - } - - void torrent::clear_error() - { - TORRENT_ASSERT(m_ses.is_network_thread()); - if (!m_error) return; - bool checking_files = should_check_files(); - if (m_ses.m_auto_manage_time_scaler > 2) - m_ses.m_auto_manage_time_scaler = 2; - m_error = error_code(); - m_error_file.clear(); - // 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 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); - } - } - - 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; - // recalculate which torrents should be - // paused - m_ses.m_auto_manage_time_scaler = 0; - - 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); - } - } - - // 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 = 0x400000, - recently_started = 0x200000, - no_seeds = 0x100000, - prio_mask = 0xfffff - }; - - if (!is_finished()) return 0; - - int scale = 100; - if (!is_seed()) scale = 50; - - 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 / download_time < s.seed_time_ratio_limit) - && downloaded > 0 - && m_total_uploaded / 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_downloaders != 0xffffff) downloaders = m_downloaders; - else 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 |= (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 (!m_owning_storage.get()) - { - 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); - - 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; - } - - void torrent::flush_cache() - { - TORRENT_ASSERT(m_ses.is_network_thread()); - 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 (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; - bool checking_files = should_check_files(); - if (!graceful) m_allow_peers = false; - m_announce_to_dht = false; - m_announce_to_trackers = false; - m_announce_to_lsd = false; - - 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 (checking_files && !should_check_files()) - { - // stop checking - m_storage->abort_disk_io(); - dequeue_torrent_check(); - set_state(torrent_status::queued_for_checking); - } - } - - 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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - if ((*i)->on_pause()) return; -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#endif - -#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); - - if (p->outstanding_bytes() > 0) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*p->m_logger) << "*** CHOKING PEER: torrent graceful paused\n"; -#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->m_logger) << "*** CLOSING CONNECTION: torrent_paused\n"; -#endif - p->disconnect(errors::torrent_paused); - } - } - - stop_announcing(); - } - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING - void torrent::log_to_all_peers(char const* message) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - (*(*i)->m_logger) << time_now_string() << " *** " << message << "\n"; - } -#endif - - (*m_ses.m_logger) << time_now_string() << " " << message << "\n"; - } -#endif - - 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; - - bool checking_files = should_check_files(); - - 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(); - } - - if (!checking_files && should_check_files()) - queue_torrent_check(); - } - - 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; - bool checking_files = should_check_files(); - m_allow_peers = true; - m_announce_to_dht = true; - m_announce_to_trackers = true; - m_announce_to_lsd = true; - if (!m_ses.is_paused()) m_graceful_pause_mode = false; - do_resume(); - if (!checking_files && should_check_files()) - queue_torrent_check(); - } - - 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) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - if ((*i)->on_resume()) return; -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#endif - - if (alerts().should_post()) - alerts().post_alert(torrent_resumed_alert(get_handle())); - - m_started = time_now(); - clear_error(); - start_announcing(); - } - - void torrent::update_tracker_timer(ptime now) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - if (!m_announcing) 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 (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; continue; } - ptime next_tracker_announce = (std::max)(i->next_announce, i->min_announce); - if (!i->updating - && next_tracker_announce < next_announce - && (!found_working || i->is_working())) - next_announce = next_tracker_announce; - if (i->is_working()) found_working = true; - if (!settings().announce_to_all_trackers - && !settings().announce_to_all_tiers) break; - } - - if (next_announce <= now) return; - - m_waiting_tracker = true; - error_code ec; - boost::weak_ptr self(shared_from_this()); - - // since we don't know if we have to re-issue the async_wait or not - // always do it -// if (m_tracker_timer.expires_at() <= next_announce) return; - -#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()) 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()) return; - if (m_announcing) return; - - m_announcing = true; - - 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(); - -#ifndef TORRENT_DISABLE_DHT - if (m_ses.m_dht) dht_announce(); -#endif - } - } - - 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; - - ptime now = time_now(); - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - (*i)->tick(); -#ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception&) {} -#endif - } -#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); - } - - // ------------------------ - // upload shift - // ------------------------ - - // this part will shift downloads - // from peers that are seeds and peers - // that don't want to download from us - // to peers that cannot upload anything - // to us. The shifting will make sure - // that the torrent's share ratio - // will be maintained - - // if the share ratio is 0 (infinite) - // m_available_free_upload isn't used - // because it isn't necessary - if (ratio() != 0.f) - { - // accumulate all the free download we get - // and add it to the available free upload - m_available_free_upload += collect_free_download( - this->begin(), this->end()); - - // distribute the free upload among the peers - m_available_free_upload = distribute_free_upload( - this->begin(), this->end(), m_available_free_upload); - } - - m_policy.pulse(); - } - - // 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 && m_upload_mode_time - >= settings().optimistic_disk_retry) - { - set_upload_mode(false); - } - - if (is_paused()) - { - // let the stats fade out to 0 - accumulator += m_stat; - m_stat.second_tick(tick_interval_ms); - 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 (!m_time_critical_pieces.empty()) - { - request_time_critical_pieces(); - } - - // ---- WEB SEEDS ---- - - // 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) - { - // 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->connection) continue; - if (w->retry > time_now()) continue; - if (w->resolving) continue; - - connect_to_url_seed(w); - } - } - - for (peer_iterator i = m_connections.begin(); - i != m_connections.end();) - { - peer_connection* p = *i; - ++i; - - if (!p->ignore_stats()) - m_stat += p->statistics(); - - // updates the peer connection's ul/dl bandwidth - // resource requests -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - p->second_tick(tick_interval_ms); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*p->m_logger) << "**ERROR**: " << e.what() << "\n"; -#endif - p->disconnect(errors::no_error, 1); - } -#endif - } - 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); - } - - 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 (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 (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 (pp.peer_count > rarest_rarity) continue; - if (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 = rand() % 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; - // 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); - ptime now = time_now(); - - // 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 (!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; - } - - void torrent::request_time_critical_pieces() - { - TORRENT_ASSERT(m_ses.is_network_thread()); - // build a list of peers and sort it by download_queue_time - std::vector peers; - peers.reserve(m_connections.size()); - std::remove_copy_if(m_connections.begin(), m_connections.end() - , std::back_inserter(peers), !boost::bind(&peer_connection::can_request_time_critical, _1)); - 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)); - - std::set peers_with_requests; - - std::vector interesting_blocks; - std::vector backup1; - std::vector backup2; - std::vector ignore; - - ptime now = time_now(); - - for (std::list::iterator i = m_time_critical_pieces.begin() - , end(m_time_critical_pieces.end()); i != end; ++i) - { - if (i != m_time_critical_pieces.begin() && i->deadline > now - + milliseconds(m_average_piece_time + m_piece_time_deviation * 4)) - { - // don't request pieces whose deadline is too far in the future - break; - } - - // loop until every block has been requested from - do - { - // 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)); - - if (p == peers.end()) break; - peer_connection& c = **p; - - interesting_blocks.clear(); - backup1.clear(); - backup2.clear(); - m_picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks - , backup1, backup2, 1, 0, c.peer_info_struct() - , ignore, piece_picker::fast, 0); - - std::vector const& rq = c.request_queue(); - - bool added_request = false; - - if (!interesting_blocks.empty() && std::find_if(rq.begin(), rq.end() - , has_block(interesting_blocks.front())) != rq.end()) - { - c.make_time_critical(interesting_blocks.front()); - added_request = true; - } - else if (!interesting_blocks.empty()) - { - c.add_request(interesting_blocks.front(), peer_connection::req_time_critical); - added_request = true; - } - - // TODO: if there's been long enough since we requested something - // from this piece, request one of the backup blocks (the one with - // the least number of requests to it) and update the last request - // timestamp - - if (added_request) - { - peers_with_requests.insert(peers_with_requests.begin(), &c); - if (i->first_requested == min_time()) i->first_requested = now; - - if (!c.can_request_time_critical()) - { - peers.erase(p); - } - else - { - // 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()); - } - - // 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->type != type) continue; - ret.insert(i->url); - } - return ret; - } - - 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(&web_seed_entry::connection, _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()); - if (m_deficit_counter < 100) return false; - m_deficit_counter -= 100; - bool ret = m_policy.connect_one_peer(m_ses.session_time()); - return ret; - } - - void torrent::give_connect_points(int points) - { - TORRENT_ASSERT(m_ses.is_network_thread()); - TORRENT_ASSERT(points <= 100); - TORRENT_ASSERT(points > 0); - TORRENT_ASSERT(want_more_peers()); - m_deficit_counter += points; - } - - 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); - } - - 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 defined TORRENT_DEBUG && !defined TORRENT_DISABLE_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 - - 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(); - 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 - { - TORRENT_ASSERT(valid_metadata()); - - fp.resize(m_torrent_file->num_files(), 0); - - if (flags & torrent_handle::piece_granularity) - { - std::copy(m_file_progress.begin(), m_file_progress.end(), fp.begin()); - return; - } - - if (is_seed()) - { - for (int i = 0; i < m_torrent_file->num_files(); ++i) - fp[i] = m_torrent_file->files().at(i).size; - 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().at(i).size; - -// zero sized files are considered -// 100% done all the time - if (size == 0) - { - fp[i] = 0; - continue; - } - - size_type done = 0; - while (size > 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(); - - 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(); - torrent_info::file_iterator file = m_torrent_file->file_at_offset(offset); - int file_index = file - m_torrent_file->begin_files(); - 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 != m_torrent_file->end_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 >= file->offset + file->size) - { - ++file; - ++file_index; - } - TORRENT_ASSERT(file != m_torrent_file->end_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 > file->offset + file->size) - { - int left_over = block_size() - block; - // split the block on multiple files - while (block > 0) - { - TORRENT_ASSERT(offset <= file->offset + file->size); - size_type slice = (std::min)(file->offset + file->size - offset - , block); - fp[file_index] += slice; - offset += slice; - block -= slice; - TORRENT_ASSERT(offset <= file->offset + file->size); - if (offset == file->offset + file->size) - { - ++file; - ++file_index; - if (file == m_torrent_file->end_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_index] += block; - offset += block_size(); - } - TORRENT_ASSERT(file_index <= m_torrent_file->num_files()); - } - } - } - - void torrent::set_state(torrent_status::state_t s) - { -#ifdef TORRENT_DEBUG - 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::finished) - TORRENT_ASSERT(is_finished()); - if (s == torrent_status::downloading && m_state == torrent_status::finished) - TORRENT_ASSERT(!is_finished()); -#endif - - if (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)); - m_state = s; - } - - torrent_status torrent::status(boost::uint32_t flags) const - { - INVARIANT_CHECK; - - ptime now = time_now(); - - torrent_status st; - - st.has_incoming = m_has_incoming; - if (m_error) st.error = m_error.message() + ": " + m_error_file; - st.seed_mode = m_seed_mode; - - 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)std::count_if(m_connections.begin(), m_connections.end() - , !boost::bind(&peer_connection::is_connecting, _1)); - - 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.active_time = m_active_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.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; - } - } - - st.num_uploads = m_num_uploads; - st.uploads_limit = m_max_uploads; - st.num_connections = int(m_connections.size()); - st.connections_limit = 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.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; - } - - 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()) - { - 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); - } - 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; - } - - if (flags & torrent_handle::query_last_seen_complete) - { - time_t last = last_seen_complete(); - for (std::set::const_iterator i = m_connections.begin() - , end(m_connections.end()); i != i; ++i) - { - last = (std::max)(last, (*i)->last_seen_complete()); - } - st.last_seen_complete = last; - } - else - { - st.last_seen_complete = 0; - } - return st; - } - - void torrent::add_redundant_bytes(int b) - { - TORRENT_ASSERT(b > 0); - m_total_redundant_bytes += b; - m_ses.add_redundant_bytes(b); -// TORRENT_ASSERT(m_total_redundant_bytes + m_total_failed_bytes -// <= m_stat.total_payload_download()); - } - - void torrent::add_failed_bytes(int b) - { - 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 - { - 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; - } - - // TODO: with some response codes, we should just consider - // the tracker as a failure and not retry - // it anymore - void torrent::tracker_request_error(tracker_request const& r - , int response_code, error_code const& ec, const std::string& 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: " + ec.message() + " " + msg); -#endif - if (r.kind == tracker_request::announce_request) - { - announce_entry* ae = find_tracker(r); - if (ae) - { - ae->failed(retry_interval); - ae->last_error = ec; - ae->message = msg; - int tracker_index = ae - &m_trackers[0]; - 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 (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) announce_with_tracker(); - update_tracker_timer(time_now()); - } - - -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - void torrent::debug_log(const std::string& line) - { - (*m_ses.m_logger) << time_now_string() << " " << line << "\n"; - } -#endif - -} - diff --git a/libtorrent_utp/src/torrent_handle.cpp b/libtorrent_utp/src/torrent_handle.cpp deleted file mode 100644 index f25192f52..000000000 --- a/libtorrent_utp/src/torrent_handle.cpp +++ /dev/null @@ -1,864 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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 -{ - - template - void fun_ret(R* ret, bool* done, condition* e, mutex* m, boost::function f) - { - *ret = f(); - mutex::scoped_lock l(*m); - *done = true; - e->signal_all(l); - } - - // defined in session.cpp - void fun_wrap(bool* done, condition* 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.post(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.post(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.post(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.post(boost::bind(&torrent:: x, t, a1, a2, a3)) - -#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.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ - do { ses.cond.wait(l); } while(!done) - -#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.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done); } - -#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.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done); } - -#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.post(boost::bind(&fun_wrap, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2, a3)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done); } - -#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.post(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done) - -#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.post(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done) - -#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.post(boost::bind(&fun_ret, &r, &done, &ses.cond, &ses.mut, boost::function(boost::bind(&torrent:: x, t, a1, a2)))); \ - t.reset(); \ - do { ses.cond.wait(l); } while(!done) - -#ifndef BOOST_NO_EXCEPTIONS - void throw_invalid_handle() - { - throw libtorrent_exception(errors::invalid_torrent_handle); - } -#endif - -#ifdef TORRENT_DEBUG - - void torrent_handle::check_invariant() const - {} - -#endif - - sha1_hash torrent_handle::info_hash() const - { - INVARIANT_CHECK; - const static sha1_hash empty; - TORRENT_SYNC_CALL_RET(sha1_hash, empty, info_hash); - return r; - } - - int torrent_handle::max_uploads() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(int, 0, max_uploads); - return r; - } - - void torrent_handle::set_max_uploads(int max_uploads) const - { - INVARIANT_CHECK; - TORRENT_ASSERT(max_uploads >= 2 || max_uploads == -1); - TORRENT_ASYNC_CALL1(set_max_uploads, max_uploads); - } - - void torrent_handle::use_interface(const char* net_interface) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(use_interface, std::string(net_interface)); - } - - int torrent_handle::max_connections() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(int, 0, max_connections); - return r; - } - - void torrent_handle::set_max_connections(int max_connections) const - { - INVARIANT_CHECK; - TORRENT_ASSERT(max_connections >= 2 || max_connections == -1); - TORRENT_ASYNC_CALL1(set_max_connections, max_connections); - } - - int torrent_handle::get_peer_upload_limit(tcp::endpoint ip) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(int, -1, get_peer_upload_limit, ip); - return r; - } - - int torrent_handle::get_peer_download_limit(tcp::endpoint ip) const - { - INVARIANT_CHECK; - 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 - { - INVARIANT_CHECK; - TORRENT_ASSERT(limit >= -1); - TORRENT_ASYNC_CALL2(set_peer_upload_limit, ip, limit); - } - - void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const - { - INVARIANT_CHECK; - TORRENT_ASSERT(limit >= -1); - TORRENT_ASYNC_CALL2(set_peer_download_limit, ip, limit); - } - - void torrent_handle::set_upload_limit(int limit) const - { - INVARIANT_CHECK; - TORRENT_ASSERT(limit >= -1); - TORRENT_ASYNC_CALL1(set_upload_limit, limit); - } - - int torrent_handle::upload_limit() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(int, 0, upload_limit); - return r; - } - - void torrent_handle::set_download_limit(int limit) const - { - INVARIANT_CHECK; - TORRENT_ASSERT(limit >= -1); - TORRENT_ASYNC_CALL1(set_download_limit, limit); - } - - int torrent_handle::download_limit() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(int, 0, download_limit); - return r; - } - - void torrent_handle::move_storage( - std::string const& save_path) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(move_storage, save_path); - } - -#if TORRENT_USE_WSTRING - void torrent_handle::move_storage( - std::wstring const& save_path) const - { - INVARIANT_CHECK; - std::string utf8; - wchar_utf8(save_path, utf8); - TORRENT_ASYNC_CALL1(move_storage, utf8); - } - - void torrent_handle::rename_file(int index, std::wstring const& new_name) const - { - INVARIANT_CHECK; - std::string utf8; - wchar_utf8(new_name, utf8); - TORRENT_ASYNC_CALL2(rename_file, index, utf8); - } -#endif // TORRENT_USE_WSTRING - - void torrent_handle::rename_file(int index, std::string const& new_name) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(rename_file, index, new_name); - } - - void torrent_handle::add_extension( - boost::function(torrent*, void*)> const& ext - , void* userdata) - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(add_extension, ext, userdata); - } - - bool torrent_handle::set_metadata(char const* metadata, int size) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET2(bool, false, set_metadata, metadata, size); - return r; - } - - void torrent_handle::pause(int flags) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause)); - } - - void torrent_handle::set_share_mode(bool b) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_share_mode, b); - } - - void torrent_handle::set_upload_mode(bool b) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_upload_mode, b); - } - - void torrent_handle::flush_cache() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(flush_cache); - } - - void torrent_handle::save_resume_data(int f) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(save_resume_data, f); - } - - bool torrent_handle::need_save_resume_data() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, need_save_resume_data); - return r; - } - - void torrent_handle::force_recheck() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(force_recheck); - } - - void torrent_handle::resume() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(resume); - } - - void torrent_handle::auto_managed(bool m) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(auto_managed, m); - } - - void torrent_handle::set_priority(int p) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_priority, p); - } - - int torrent_handle::queue_position() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(int, -1, queue_position); - return r; - } - - void torrent_handle::queue_position_up() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(queue_down); - } - - void torrent_handle::queue_position_down() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(queue_up); - } - - void torrent_handle::queue_position_top() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_queue_position, 0); - } - - void torrent_handle::queue_position_bottom() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_queue_position, INT_MAX); - } - - void torrent_handle::clear_error() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(clear_error); - } - - void torrent_handle::set_tracker_login(std::string const& name - , std::string const& password) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(set_tracker_login, name, password); - } - -#ifndef TORRENT_NO_DEPRECATE -#if !TORRENT_NO_FPU - void torrent_handle::file_progress(std::vector& progress) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL1(file_progress, boost::ref(progress)); - } -#endif -#endif - - void torrent_handle::file_progress(std::vector& progress, int flags) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL2(file_progress, boost::ref(progress), flags); - } - - torrent_status torrent_handle::status(boost::uint32_t flags) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(torrent_status, torrent_status(), status, flags); - return r; - } - - void torrent_handle::set_sequential_download(bool sd) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(set_sequential_download, sd); - } - - std::string torrent_handle::name() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(std::string, "", name); - return r; - } - - void torrent_handle::piece_availability(std::vector& avail) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL1(piece_availability, boost::ref(avail)); - } - - void torrent_handle::piece_priority(int index, int priority) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(set_piece_priority, index, priority); - } - - int torrent_handle::piece_priority(int index) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(int, 0, piece_priority, index); - return r; - } - - void torrent_handle::prioritize_pieces(std::vector const& pieces) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(prioritize_pieces, pieces); - } - - std::vector torrent_handle::piece_priorities() const - { - INVARIANT_CHECK; - std::vector ret; - TORRENT_SYNC_CALL1(piece_priorities, boost::ref(ret)); - return ret; - } - - void torrent_handle::file_priority(int index, int priority) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(set_file_priority, index, priority); - } - - int torrent_handle::file_priority(int index) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(int, 0, file_priority, index); - return r; - } - - void torrent_handle::prioritize_files(std::vector const& files) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(prioritize_files, files); - } - - std::vector torrent_handle::file_priorities() const - { - INVARIANT_CHECK; - std::vector ret; - TORRENT_SYNC_CALL1(file_priorities, ret); - return ret; - } - -#ifndef TORRENT_NO_DEPRECATE -// ============ start deprecation =============== - - bool torrent_handle::is_seed() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, is_seed); - return r; - } - - bool torrent_handle::is_finished() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, is_finished); - return r; - } - - bool torrent_handle::is_paused() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, is_torrent_paused); - return r; - } - - bool torrent_handle::is_sequential_download() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, is_sequential_download); - return r; - } - - bool torrent_handle::is_auto_managed() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, is_auto_managed); - return r; - } - - bool torrent_handle::has_metadata() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, valid_metadata); - return r; - } - - void torrent_handle::filter_piece(int index, bool filter) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(filter_piece, index, filter); - } - - void torrent_handle::filter_pieces(std::vector const& pieces) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(filter_pieces, pieces); - } - - bool torrent_handle::is_piece_filtered(int index) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET1(bool, false, is_piece_filtered, index); - return r; - } - - std::vector torrent_handle::filtered_pieces() const - { - INVARIANT_CHECK; - std::vector ret; - TORRENT_SYNC_CALL1(filtered_pieces, ret); - return ret; - } - - void torrent_handle::filter_files(std::vector const& files) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(filter_files, files); - } - -// ============ end deprecation =============== -#endif - - std::vector torrent_handle::trackers() const - { - INVARIANT_CHECK; - 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 - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::url_seed); - } - - void torrent_handle::remove_url_seed(std::string const& url) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::url_seed); - } - - std::set torrent_handle::url_seeds() const - { - INVARIANT_CHECK; - 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 - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(add_web_seed, url, web_seed_entry::http_seed); - } - - void torrent_handle::remove_http_seed(std::string const& url) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(remove_web_seed, url, web_seed_entry::http_seed); - } - - std::set torrent_handle::http_seeds() const - { - INVARIANT_CHECK; - 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 - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(replace_trackers, urls); - } - - void torrent_handle::add_tracker(announce_entry const& url) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(add_tracker, url); - } - - void torrent_handle::add_piece(int piece, char const* data, int flags) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL3(add_piece, piece, data, flags); - } - - void torrent_handle::read_piece(int piece) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(read_piece, piece); - } - - storage_interface* torrent_handle::get_storage_impl() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(storage_interface*, 0, get_storage); - return r; - } - - torrent_info const& torrent_handle::get_torrent_info() const - { - INVARIANT_CHECK; -#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 -// mutex::scoped_lock l(t->session().m_mutex); - if (!t->valid_metadata()) -#ifdef BOOST_NO_EXCEPTIONS - return empty; -#else - throw_invalid_handle(); -#endif - return t->torrent_file(); - } - - bool torrent_handle::is_valid() const - { - INVARIANT_CHECK; - return !m_torrent.expired(); - } - -#ifndef TORRENT_NO_DEPRECATE - entry torrent_handle::write_resume_data() const - { - INVARIANT_CHECK; - - 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.post(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(); - do { ses.cond.wait(l); } while(!done); - } - - return ret; - } -#endif - - std::string torrent_handle::save_path() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(std::string, "", save_path); - return r; - } - - void torrent_handle::connect_peer(tcp::endpoint const& adr, int source) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL2(add_peer, adr, source); - } - - void torrent_handle::force_reannounce( - boost::posix_time::time_duration duration) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(force_tracker_request, time_now() + seconds(duration.total_seconds())); - } - -#ifndef TORRENT_DISABLE_DHT - void torrent_handle::force_dht_announce() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(dht_announce); - } -#endif - - void torrent_handle::force_reannounce() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(force_tracker_request, time_now()); - } - - void torrent_handle::scrape_tracker() const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL(scrape_tracker); - } - - bool torrent_handle::super_seeding() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, super_seeding); - return r; - } - - void torrent_handle::super_seeding(bool on) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(super_seeding, on); - } - - void torrent_handle::set_ratio(float ratio) const - { - INVARIANT_CHECK; - - TORRENT_ASSERT(ratio >= 0.f); - if (ratio < 1.f && ratio > 0.f) - ratio = 1.f; - TORRENT_ASYNC_CALL1(set_ratio, ratio); - } - -#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES - void torrent_handle::resolve_countries(bool r) - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL1(resolve_countries, r); - } - - bool torrent_handle::resolve_countries() const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL_RET(bool, false, resolving_countries); - return r; - } -#endif - - void torrent_handle::get_full_peer_list(std::vector& v) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL1(get_full_peer_list, boost::ref(v)); - } - - void torrent_handle::get_peer_info(std::vector& v) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL1(get_peer_info, boost::ref(v)); - } - - void torrent_handle::get_download_queue(std::vector& queue) const - { - INVARIANT_CHECK; - TORRENT_SYNC_CALL1(get_download_queue, boost::ref(queue)); - } - - void torrent_handle::set_piece_deadline(int index, int deadline, int flags) const - { - INVARIANT_CHECK; - TORRENT_ASYNC_CALL3(set_piece_deadline, index, deadline, flags); - } - -} - diff --git a/libtorrent_utp/src/torrent_info.cpp b/libtorrent_utp/src/torrent_info.cpp deleted file mode 100644 index a23f7e180..000000000 --- a/libtorrent_utp/src/torrent_info.cpp +++ /dev/null @@ -1,1231 +0,0 @@ -/* - -Copyright (c) 2003-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. - -*/ - -#include "libtorrent/pch.hpp" - -#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/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" - -#if TORRENT_USE_I2P -#include "libtorrent/parse_url.hpp" -#endif - -namespace libtorrent -{ - - void convert_to_utf8(std::string& str, unsigned char chr) - { - str += 0xc0 | ((chr & 0xff) >> 6); - str += 0x80 | (chr & 0x3f); - } - - 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_EXPORT bool verify_encoding(std::string& target, bool fix_paths = false) - { - std::string tmp_path; - bool valid_encoding = true; - for (std::string::iterator i = target.begin() - , end(target.end()); i != end; ++i) - { - // valid ascii-character - if ((*i & 0x80) == 0) - { - // replace invalid characters with '.' - if (!fix_paths || valid_path_character(*i)) - { - tmp_path += *i; - } - else - { - tmp_path += '_'; - valid_encoding = false; - } - continue; - } - - if (end - i < 2) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 2-byte utf-8 character - if ((i[0] & 0xe0) == 0xc0 - && (i[1] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - i += 1; - continue; - } - - if (end - i < 3) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 3-byte utf-8 character - if ((i[0] & 0xf0) == 0xe0 - && (i[1] & 0xc0) == 0x80 - && (i[2] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - tmp_path += i[2]; - i += 2; - continue; - } - - if (end - i < 4) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 4-byte utf-8 character - if ((i[0] & 0xf0) == 0xe0 - && (i[1] & 0xc0) == 0x80 - && (i[2] & 0xc0) == 0x80 - && (i[3] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - tmp_path += i[2]; - tmp_path += i[3]; - i += 3; - continue; - } - - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - } - // 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; - } - - void verify_encoding(file_entry& target) - { - std::string p = target.filename(); - if (!verify_encoding(p, true)) target.set_name(p.c_str()); - } - - // TODO: should this take a char const*? - 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; - } - - void trim_path_element(std::string& path_element) - { - const int max_path_len = TORRENT_MAX_PATH; - 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; - } - } - } - - TORRENT_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 (!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, std::string* symlink - , 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(); - - 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 (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(target); - - // bitcomet pad file - if (path.find("_____padding_file_") != std::string::npos) - target.pad_file = true; - - target.set_name(path.c_str()); - - 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 && symlink) - { - 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); - *symlink = combine_path(*symlink, path_element); - } - // indeicate that we have a symlink - target.symlink_index = 0; - } - - 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()); - for (int i = 0, end(list.list_size()); i < end; ++i) - { - lazy_entry const* file_hash = 0; - time_t mtime = 0; - std::string symlink; - file_entry e; - lazy_entry const* fee = 0; - if (!extract_single_file(*list.list_at(i), e, root_dir - , &file_hash, &symlink, &fee, &mtime)) - return false; - - // TODO: this logic should be a separate step - // done once the torrent is loaded, and the original - // filenames should be preserved! - int cnt = 0; - std::set files; - - // as long as this file already exists - // increase the counter - std::string path = e.filename(); - while (!files.insert(path).second) - { - ++cnt; - char suffix[50]; - snprintf(suffix, sizeof(suffix), ".%d%s", cnt, extension(path).c_str()); - replace_extension(path, suffix); - } - e.set_name(path.c_str()); - target.add_file(e, file_hash ? file_hash->string_ptr() + info_ptr_diff : 0 - , e.symlink_index != -1 ? &symlink : 0, mtime); - - // 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 - file_entry const& fe = target.at(target.num_files() - 1); - // TODO: once the filename renaming is removed from here - // this check can be removed as well - if (fee && fe.filename() == 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; - const_cast(fe).set_name(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) - { - // round up to nearest 2 exponent - int i; - for (i = 0; pieces > 0; pieces >>= 1, ++i); - return 1 << i; - } - - int load_file(std::string const& filename, std::vector& v) - { - file f; - error_code ec; - if (!f.open(filename, file::read_only, ec)) return -1; - size_type s = f.get_size(ec); - if (ec) return -1; - if (s > 8000000) return -2; - v.resize(s); - if (s == 0) return 0; - file::iovec_t b = {&v[0], s}; - size_type read = f.readv(0, &b, 1, ec); - if (read != s) return -3; - if (ec) return -3; - return 0; - } - - int announce_entry::next_announce_in() const - { return total_seconds(time_now() - next_announce); } - - int announce_entry::min_announce_in() const - { return total_seconds(time_now() - min_announce); } - - void announce_entry::failed(int retry_interval) - { - ++fails; - int delay = (std::min)(tracker_retry_delay_min + int(fails) * int(fails) - * tracker_retry_delay_min, 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()); - } - - torrent_info::torrent_info(torrent_info const& t, int flags) - : 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_merkle_first_leaf(t.m_merkle_first_leaf) - , 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 (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); - int ret = lazy_bdecode(m_info_section.get(), m_info_section.get() - + m_info_section_size, m_info_dict, ec); - - lazy_entry const* pieces = m_info_dict.dict_find_string("pieces"); - if (pieces && pieces->string_length() == m_files.num_pieces() * 20) - { - m_piece_hashes = m_info_section.get() + (pieces->string_ptr() - m_info_section.get()); - TORRENT_ASSERT(m_piece_hashes >= m_info_section.get()); - TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size); - } - } - } - - void torrent_info::remap_files(file_storage const& f) - { - // 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; - } - -#ifndef TORRENT_NO_DEPRECATE - // standard constructor that parses a torrent file - torrent_info::torrent_info(entry const& torrent_file) - : m_piece_hashes(0) - , m_creation_date(0) - , m_merkle_first_leaf(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(errors::invalid_bencoding); -#endif - return; - } -#ifndef BOOST_NO_EXCEPTIONS - if (!parse_torrent_file(e, ec, 0)) - throw invalid_torrent_file(ec); -#else - parse_torrent_file(e, ec); -#endif - } -#endif - -#ifndef BOOST_NO_EXCEPTIONS - torrent_info::torrent_info(lazy_entry const& torrent_file, int flags) - : m_piece_hashes(0) - , m_creation_date(0) - , m_merkle_first_leaf(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); - } - - torrent_info::torrent_info(char const* buffer, int size, int flags) - : m_piece_hashes(0) - , m_creation_date(0) - , m_merkle_first_leaf(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); - } - - torrent_info::torrent_info(std::string const& filename, int flags) - : 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); - if (ret < 0) return; - - lazy_entry e; - error_code ec; - 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); - } - -#if TORRENT_USE_WSTRING - torrent_info::torrent_info(std::wstring const& filename, int flags) - : m_piece_hashes(0) - , m_creation_date(0) - , m_merkle_first_leaf(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); - if (ret < 0) return; - - lazy_entry e; - error_code ec; - 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); - } -#endif -#endif - - torrent_info::torrent_info(lazy_entry const& torrent_file, error_code& ec, int flags) - : 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); - } - - torrent_info::torrent_info(char const* buffer, int size, error_code& ec, int flags) - : m_piece_hashes(0) - , m_creation_date(0) - , m_merkle_first_leaf(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); - } - - torrent_info::torrent_info(std::string const& filename, error_code& ec, int flags) - : 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); - 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); - } - -#if TORRENT_USE_WSTRING - torrent_info::torrent_info(std::wstring const& filename, error_code& ec, int flags) - : 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); - 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); - } -#endif - - // 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_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() - { - 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) - { - 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 - - 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) - std::string name = info.dict_find_string_value("name.utf-8"); - if (name.empty()) name = info.dict_find_string_value("name"); - if (name.empty()) - { - ec = errors::torrent_missing_name; - return false; - } - - 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.set_name(name.c_str()); - e.offset = 0; - e.size = info.dict_find_int_value("length", -1); - size_type ts = info.dict_find_int_value("mtime", -1); - time_t mtime = 0; - if (ts > 0) mtime = std::time_t(ts); - 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"); - std::string symlink; - 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); - symlink = combine_path(symlink, path_element); - } - e.symlink_index = 0; - } - lazy_entry const* fh = info.dict_find_string("sha1"); - if (fh && fh->string_length() != 20) fh = 0; - - // bitcomet pad file - if (e.filename().find("_____padding_file_") != std::string::npos) - e.pad_file = true; - if (e.size < 0) - { - ec = errors::torrent_invalid_length; - return false; - } - m_files.add_file(e, fh ? fh->string_ptr() + info_ptr_diff : 0 - , e.symlink_index != -1 ? &symlink : 0, mtime); - 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; - } - m_files.set_name(name); - - // 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("pieces"); - lazy_entry const* root_hash = info.dict_find("root hash"); - if ((pieces == 0 || pieces->type() != lazy_entry::string_t) - && (root_hash == 0 || root_hash->type() != lazy_entry::string_t)) - { - 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) - { - 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 - { - 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; - } - - // 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) - { - m_web_seeds.push_back(web_seed_entry(maybe_url_encode(url_seeds->string_value()) - , web_seed_entry::url_seed)); - } - else if (url_seeds && url_seeds->type() == lazy_entry::list_t) - { - 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; - m_web_seeds.push_back(web_seed_entry(maybe_url_encode(url->string_value()) - , web_seed_entry::url_seed)); - } - } - - // 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) - { - 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) - { - 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) continue; - m_web_seeds.push_back(web_seed_entry(maybe_url_encode(url->string_value()) - , 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); - - lazy_entry const* info = torrent_file.dict_find_dict("info"); - if (info == 0) - { - ec = errors::torrent_missing_info; - return false; - } - return parse_info_section(*info, ec, flags); - } - - 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 - { - 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 (file_storage::iterator i = m_files.begin(); i != m_files.end(); ++i) - os << " " << std::setw(11) << i->size << " " << m_files.file_path(*i) << "\n"; - } - -// ------- end deprecation ------- -#endif - -} - diff --git a/libtorrent_utp/src/tracker_manager.cpp b/libtorrent_utp/src/tracker_manager.cpp deleted file mode 100644 index 57ec24d21..000000000 --- a/libtorrent_utp/src/tracker_manager.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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 - ? m_completion_timeout - total_seconds(m_read_time - m_start_time) - : (std::min)(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() - { - return m_requester.lock(); - } - - void tracker_connection::fail(error_code const& ec, int code - , char const* msg, int interval, int min_interval) - { - boost::shared_ptr cb = requester(); - if (cb) cb->tracker_request_error(m_req, code, ec, msg - , 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); - if (m_abort) 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_udp(error_code const& e - , udp::endpoint const& ep, char const* buf, int size) - { - 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_udp(error_code const& e - , char const* hostname, char const* buf, int size) - { - 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: " + req.url); -#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/libtorrent_utp/src/udp_socket.cpp b/libtorrent_utp/src/udp_socket.cpp deleted file mode 100644 index b58380c40..000000000 --- a/libtorrent_utp/src/udp_socket.cpp +++ /dev/null @@ -1,1025 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/config.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/udp_socket.hpp" -#include "libtorrent/connection_queue.hpp" -#include "libtorrent/escape_string.hpp" -#include "libtorrent/socket_io.hpp" -#include "libtorrent/error.hpp" -#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 - , udp_socket::callback_t const& c - , udp_socket::callback2_t const& c2 - , connection_queue& cc) - : m_callback(c) - , m_callback2(c2) - , m_ipv4_sock(ios) - , m_v4_buf_size(0) - , m_v4_buf(0) -#if TORRENT_USE_IPV6 - , m_ipv6_sock(ios) - , m_v6_buf_size(0) - , m_v6_buf(0) -#endif - , m_bind_port(0) - , m_outstanding(0) - , m_socks5_sock(ios) - , m_connection_ticket(-1) - , m_cc(cc) - , m_resolver(ios) - , m_queue_packets(false) - , m_tunnel_packets(false) - , m_abort(false) - , m_reallocate_buffers(false) -{ -#ifdef TORRENT_DEBUG - m_magic = 0x1337; - m_started = false; - m_outstanding_when_aborted = -1; -#if defined BOOST_HAS_PTHREADS - m_thread = 0; -#endif -#endif - - m_v4_buf_size = 1600; - m_v4_buf = (char*)malloc(m_v4_buf_size); -#if TORRENT_USE_IPV6 - m_v6_buf_size = 1600; - m_v6_buf = (char*)malloc(m_v6_buf_size); -#endif -} - -udp_socket::~udp_socket() -{ - free(m_v4_buf); -#if TORRENT_USE_IPV6 - free(m_v6_buf); -#endif -#ifdef TORRENT_DEBUG - TORRENT_ASSERT(m_magic == 0x1337); - TORRENT_ASSERT(!m_callback || !m_started); - TORRENT_ASSERT_VAL(m_outstanding == 0, m_outstanding); - m_magic = 0; -#endif -} - -#ifdef TORRENT_DEBUG - #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) -{ - CHECK_MAGIC; - - TORRENT_ASSERT(is_open()); - - // if the sockets are closed, the udp_socket is closing too - if (!is_open()) 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 - TORRENT_ASSERT(m_queue_packets); - if (!m_queue_packets) return; - - m_queue.push_back(queued_packet()); - queued_packet& qp = m_queue.back(); - qp.ep.port(port); - qp.hostname = strdup(hostname); - qp.buf.insert(qp.buf.begin(), p, p + len); -} - -void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_code& ec) -{ - CHECK_MAGIC; - - TORRENT_ASSERT(is_open()); - - // if the sockets are closed, the udp_socket is closing too - if (!is_open()) return; - - if (m_tunnel_packets) - { - // send udp packets through SOCKS5 server - wrap(ep, p, len, ec); - return; - } - - if (m_queue_packets) - { - m_queue.push_back(queued_packet()); - queued_packet& qp = m_queue.back(); - qp.ep = ep; - qp.hostname = 0; - qp.buf.insert(qp.buf.begin(), p, p + len); - return; - } - -#if TORRENT_USE_IPV6 - if (ep.address().is_v4() && m_ipv4_sock.is_open()) -#endif - m_ipv4_sock.send_to(asio::buffer(p, len), ep, 0, ec); -#if TORRENT_USE_IPV6 - else - m_ipv6_sock.send_to(asio::buffer(p, len), ep, 0, ec); -#endif -} - -void udp_socket::maybe_realloc_buffers() -{ - if (m_reallocate_buffers) - { - m_v4_buf = (char*)realloc(m_v4_buf, m_v4_buf_size); -#if TORRENT_USE_IPV6 - m_v6_buf = (char*)realloc(m_v6_buf, m_v6_buf_size); -#endif - m_reallocate_buffers = false; - } -} - -void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_transferred) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("udp_socket::on_read"); -#endif - TORRENT_ASSERT(m_magic == 0x1337); - TORRENT_ASSERT(is_single_thread()); - - TORRENT_ASSERT(m_outstanding > 0); - --m_outstanding; - - if (e == asio::error::operation_aborted || m_abort) - { - if (m_outstanding == 0) - { - // "this" may be destructed in the callback - callback_t tmp = m_callback; - m_callback.clear(); - } - return; - } - - CHECK_MAGIC; - if (!m_callback) return; - - if (e) - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - -#if TORRENT_USE_IPV6 - if (s == &m_ipv4_sock) -#endif - m_callback(e, m_v4_ep, 0, 0); -#if TORRENT_USE_IPV6 - else - m_callback(e, m_v6_ep, 0, 0); -#endif - -#ifndef BOOST_NO_EXCEPTIONS - } catch(std::exception&) {} -#endif - - // 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::message_size) - { - if (m_outstanding == 0) - { - // "this" may be destructed in the callback - callback_t tmp = m_callback; - m_callback.clear(); - } - return; - } - - if (m_abort) return; - - maybe_realloc_buffers(); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif -#if TORRENT_USE_IPV6 - if (s == &m_ipv4_sock) -#endif - s->async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size) - , m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2)); -#if TORRENT_USE_IPV6 - else - s->async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size) - , m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2)); -#endif - - ++m_outstanding; -#ifdef TORRENT_DEBUG - m_started = true; -#endif - return; - } - -#if TORRENT_USE_IPV6 - if (s == &m_ipv4_sock) -#endif - { - -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - - if (m_tunnel_packets) - { - // if the source IP doesn't match the proxy's, ignore the packet - if (m_v4_ep == m_proxy_addr) - unwrap(e, m_v4_buf, bytes_transferred); - } - else - { - m_callback(e, m_v4_ep, m_v4_buf, bytes_transferred); - } - -#ifndef BOOST_NO_EXCEPTIONS - } catch(std::exception&) {} -#endif - - if (m_abort) return; - - maybe_realloc_buffers(); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - s->async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size) - , m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2)); - } -#if TORRENT_USE_IPV6 - else - { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - - if (m_tunnel_packets) - { - // if the source IP doesn't match the proxy's, ignore the packet - if (m_v6_ep == m_proxy_addr) - unwrap(e, m_v6_buf, bytes_transferred); - } - else - { - m_callback(e, m_v6_ep, m_v6_buf, bytes_transferred); - } - -#ifndef BOOST_NO_EXCEPTIONS - } catch(std::exception&) {} -#endif - - if (m_abort) return; - - maybe_realloc_buffers(); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - s->async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size) - , m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2)); - } -#endif - ++m_outstanding; -#ifdef TORRENT_DEBUG - m_started = true; -#endif -} - -void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec) -{ - CHECK_MAGIC; - using namespace libtorrent::detail; - - char header[20]; - 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_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) -#endif - m_ipv4_sock.send_to(iovec, m_proxy_addr, 0, ec); -#if TORRENT_USE_IPV6 - else - m_ipv6_sock.send_to(iovec, m_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_proxy_addr.address().is_v4() && m_ipv4_sock.is_open()) -#endif - m_ipv4_sock.send_to(iovec, m_proxy_addr, 0, ec); -#if TORRENT_USE_IPV6 - else - m_ipv6_sock.send_to(iovec, m_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; - m_callback2(e, hostname.c_str(), p, size - (p - buf)); - return; - } - - m_callback(e, sender, p, size - (p - buf)); -} - -#ifndef BOOST_ASIO_ENABLE_CANCELIO -#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 (m_connection_ticket >= 0) - { - m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - } - - if (m_outstanding == 0) - { - // "this" may be destructed in the callback - callback_t tmp = m_callback; - m_callback.clear(); - } -} - -void udp_socket::set_buf_size(int s) -{ - if (s > m_v4_buf_size) - { - m_v4_buf_size = s; -#if TORRENT_USE_IPV6 - m_v6_buf_size = s; -#endif - m_reallocate_buffers = true; - } -} - -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) 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 - - maybe_realloc_buffers(); - - if (ep.address().is_v4()) - { - m_ipv4_sock.open(udp::v4(), ec); - if (ec) return; - m_ipv4_sock.bind(ep, ec); - if (ec) return; -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size) - , m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2)); - ++m_outstanding; - } -#if TORRENT_USE_IPV6 - else - { - m_ipv6_sock.set_option(v6only(true), ec); - if (ec) return; - m_ipv6_sock.bind(ep, ec); - if (ec) return; -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size) - , m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2)); - ++m_outstanding; - } -#endif -#ifdef TORRENT_DEBUG - m_started = true; -#endif - m_bind_port = ep.port(); -} - -void udp_socket::bind(int port) -{ - CHECK_MAGIC; - TORRENT_ASSERT(is_single_thread()); - - TORRENT_ASSERT(m_abort == false); - if (m_abort) return; - - error_code ec; - - 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 - - maybe_realloc_buffers(); - - m_ipv4_sock.open(udp::v4(), ec); - if (!ec) - { -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - m_ipv4_sock.bind(udp::endpoint(address_v4::any(), port), ec); - m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size) - , m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2)); - ++m_outstanding; -#ifdef TORRENT_DEBUG - m_started = true; -#endif - } -#if TORRENT_USE_IPV6 - m_ipv6_sock.open(udp::v6(), ec); - if (!ec) - { -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_read"); -#endif - m_ipv6_sock.set_option(v6only(true), ec); - m_ipv6_sock.bind(udp::endpoint(address_v6::any(), port), ec); - m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size) - , m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2)); - ++m_outstanding; -#ifdef TORRENT_DEBUG - m_started = true; -#endif - } -#endif - m_bind_port = 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_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 (e) return; - CHECK_MAGIC; - - TORRENT_ASSERT(is_single_thread()); - - m_proxy_addr.address(i->endpoint().address()); - m_proxy_addr.port(i->endpoint().port()); - // on_connect may be called from within this thread - 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() -{ - CHECK_MAGIC; - TORRENT_ASSERT(is_single_thread()); - - error_code ec; - m_socks5_sock.close(ec); - m_connection_ticket = -1; -} - -void udp_socket::on_connect(int ticket) -{ - CHECK_MAGIC; - TORRENT_ASSERT(is_single_thread()); - - if (m_abort) return; - -#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); - m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port()) - , boost::bind(&udp_socket::on_connected, this, _1)); -} - -void udp_socket::on_connected(error_code const& e) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("udp_socket::on_connected"); -#endif - CHECK_MAGIC; - - TORRENT_ASSERT(is_single_thread()); - m_cc.done(m_connection_ticket); - m_connection_ticket = -1; - if (e) 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 < sizeof(m_tmp_buf), (p - m_tmp_buf)); -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_handshake1"); -#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 - CHECK_MAGIC; - if (e) return; - - TORRENT_ASSERT(is_single_thread()); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_handshake2"); -#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 - CHECK_MAGIC; - if (e) 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) 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); - 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 < sizeof(m_tmp_buf), (p - m_tmp_buf)); -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_handshake3"); -#endif - asio::async_write(m_socks5_sock, asio::buffer(m_tmp_buf, p - m_tmp_buf) - , boost::bind(&udp_socket::handshake3, this, _1)); - } - else - { - 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 - CHECK_MAGIC; - if (e) return; - - TORRENT_ASSERT(is_single_thread()); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::on_handshake4"); -#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 - CHECK_MAGIC; - if (e) 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) return; - if (status != 0) 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; - tcp::endpoint local = m_socks5_sock.local_endpoint(ec); - write_uint8(local.address().is_v4() ? 1 : 4, p); // ATYP IPv4 - detail::write_address(local.address(), p); - int port = 0; -#if TORRENT_USE_IPV6 - if (local.address().is_v4()) -#endif - port = m_ipv4_sock.local_endpoint(ec).port(); -#if TORRENT_USE_IPV6 - else - port = m_ipv6_sock.local_endpoint(ec).port(); -#endif - detail::write_uint16(port , p); - TORRENT_ASSERT_VAL(p - m_tmp_buf < sizeof(m_tmp_buf), (p - m_tmp_buf)); -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::connect1"); -#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 - CHECK_MAGIC; - if (e) return; - - TORRENT_ASSERT(is_single_thread()); - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::connect2"); -#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 - CHECK_MAGIC; - if (e) 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 - read_uint8(p); // RESERVED - int atyp = read_uint8(p); // address type - - if (version != 5) return; - if (status != 0) return; - - if (atyp == 1) - { - m_proxy_addr.address(address_v4(read_uint32(p))); - m_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!"); - } - - m_tunnel_packets = true; - 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); - free(p.hostname); - } - else - { - udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec); - } - m_queue.pop_front(); - } - -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("udp_socket::hung_up"); -#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 - 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); -} - -rate_limited_udp_socket::rate_limited_udp_socket(io_service& ios - , callback_t const& c - , callback2_t const& c2 - , connection_queue& cc) - : udp_socket(ios, c, c2, cc) - , m_timer(ios) - , m_queue_size_limit(200) - , m_rate_limit(4000) - , m_quota(4000) - , m_last_tick(time_now()) -{ -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("rate_limited_udp_socket::on_tick"); -#endif - error_code ec; - m_timer.expires_from_now(seconds(1), ec); - m_timer.async_wait(boost::bind(&rate_limited_udp_socket::on_tick, this, _1)); - TORRENT_ASSERT_VAL(!ec, ec); -} - -bool rate_limited_udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_code& ec, int flags) -{ - if (m_quota < len) - { - // bit 1 of flags means "don't drop" - if (int(m_queue.size()) >= m_queue_size_limit && (flags & 1) == 0) - return false; - m_queue.push_back(queued_packet()); - queued_packet& qp = m_queue.back(); - qp.ep = ep; - qp.buf.insert(qp.buf.begin(), p, p + len); - return true; - } - - m_quota -= len; - udp_socket::send(ep, p, len, ec); - return true; -} - -void rate_limited_udp_socket::on_tick(error_code const& e) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("rate_limited_udp_socket::on_tick"); -#endif - if (e) return; - if (is_closed()) return; - error_code ec; - ptime now = time_now_hires(); -#if defined TORRENT_ASIO_DEBUGGING - add_outstanding_async("rate_limited_udp_socket::on_tick"); -#endif - m_timer.expires_at(now + seconds(1), ec); - m_timer.async_wait(boost::bind(&rate_limited_udp_socket::on_tick, this, _1)); - - time_duration delta = now - m_last_tick; - m_last_tick = now; - if (m_quota < m_rate_limit) m_quota += m_rate_limit * total_milliseconds(delta) / 1000; - - if (m_queue.empty()) return; - - while (!m_queue.empty() && int(m_queue.front().buf.size()) <= m_quota) - { - queued_packet const& p = m_queue.front(); - TORRENT_ASSERT(m_quota >= p.buf.size()); - m_quota -= p.buf.size(); - error_code ec; - udp_socket::send(p.ep, &p.buf[0], p.buf.size(), ec); - m_queue.pop_front(); - } -} - -void rate_limited_udp_socket::close() -{ - error_code ec; - m_timer.cancel(ec); - udp_socket::close(); -} - diff --git a/libtorrent_utp/src/udp_tracker_connection.cpp b/libtorrent_utp/src/udp_tracker_connection.cpp deleted file mode 100644 index fafc0f7e7..000000000 --- a/libtorrent_utp/src/udp_tracker_connection.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#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 - -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_man(man) - , 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; - int port; - error_code ec; - - using boost::tuples::ignore; - boost::tie(ignore, ignore, hostname, port, ignore) - = parse_url_components(tracker_req().url, ec); - - if (ec) - { - // never call fail() when the session mutex is locked! - m_ses.m_io_service.post(boost::bind( - &tracker_connection::fail_disp, self(), 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: " + 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::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)); - - // remove endpoints that are filtered by the IP filter - for (std::list::iterator i = m_endpoints.begin(); - i != m_endpoints.end();) - { - if (m_ses.m_ip_filter.access(i->address()) == ip_filter::blocked) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - if (cb) cb->debug_log("*** UDP_TRACKER [ IP blocked by filter: " + print_address(i->address()) + " ]"); -#endif - i = m_endpoints.erase(i); - } - else - ++i; - } - - if (m_endpoints.empty()) - { - fail(error_code(errors::banned_by_ip_filter)); - return; - } - - std::list::iterator iter = m_endpoints.begin(); - m_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(m_target.address().is_v4() != bind_interface().is_v4()); - if (cb) - { - char const* tracker_address_type = m_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 - { - m_target = udp::endpoint(iter->address(), iter->port()); - } - } - - if (cb) cb->m_tracker_address = tcp::endpoint(m_target.address(), m_target.port()); - - start_announce(); - } - - 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(); - char msg[200]; - snprintf(msg, 200, "*** UDP_TRACKER [ timed out url: %s ]", tracker_req().url.c_str()); - if (cb) cb->debug_log(msg); -#endif - m_abort = true; - 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) - { - char msg[200]; - snprintf(msg, 200, "<== UDP_TRACKER_PACKET [ size: %d ]", size); - cb->debug_log(msg); - } -#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) - { - char msg[200]; - snprintf(msg, 200, "*** UDP_TRACKER_PACKET [ action: %d ]", action); - cb->debug_log(msg); - } -#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) - { - char msg[200]; - snprintf(msg, 200, "*** UDP_TRACKER_RESPONSE [ tid: %x ]" - , int(transaction)); - cb->debug_log(msg); - } -#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); - char msg[200]; - snprintf(msg, 200, "==> UDP_TRACKER_CONNECT [%s]", hex_ih); - cb->debug_log(msg); - } -#endif - if (m_abort) return; - - char buf[16]; - char* ptr = buf; - - if (m_transaction_id == 0) - m_transaction_id = std::rand() ^ (std::rand() << 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 = std::rand() ^ (std::rand() << 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); - out += 20; - TORRENT_ASSERT(out - 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, 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(); - char msg[200]; - snprintf(msg, 200, "<== UDP_TRACKER_RESPONSE [ url: %s ]", tracker_req().url.c_str()); - cb->debug_log(msg); - } -#endif - - if (!cb) - { - close(); - return true; - } - - std::vector peer_list; - for (int i = 0; i < num_peers; ++i) - { - // TODO: don't use a string here. The problem is that - // some trackers will respond with actual strings. - // Especially 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, 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 = std::rand() ^ (std::rand() << 16); - - if (m_abort) return; - - char buf[8 + 4 + 4 + 20 + 20 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 2 + 2]; - 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 (!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 - detail::write_uint16(0, out); // extensions - - TORRENT_ASSERT(out - buf == 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); - char msg[200]; - snprintf(msg, 200, "==> UDP_TRACKER_ANNOUNCE [%s]", hex_ih); - cb->debug_log(msg); - } -#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_announce; - sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header - ++m_attempts; - if (ec) - { - fail(ec); - return; - } - } - -} - diff --git a/libtorrent_utp/src/upnp.cpp b/libtorrent_utp/src/upnp.cpp deleted file mode 100644 index ab94e330d..000000000 --- a/libtorrent_utp/src/upnp.cpp +++ /dev/null @@ -1,1357 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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" - -#if defined TORRENT_ASIO_DEBUGGING -#include "libtorrent/debug.hpp" -#endif - -#include -#include -#if BOOST_VERSION < 103500 -#include -#include -#else -#include -#include -#endif -#include - -using namespace libtorrent; - -static error_code ec; - -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(ios, 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_disabled(false) - , m_closing(false) - , m_ignore_non_routers(ignore_nonrouters) - , m_cc(cc) -{ - TORRENT_ASSERT(cb); - - if (state) - { - upnp_state_t* s = (upnp_state_t*)state; - m_devices.swap(s->devices); - m_mappings.swap(s->mappings); - delete s; - } -} - -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[200]; - snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting.", 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) -{ - mutex::scoped_lock l(m_mutex); - - char msg[200]; - 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[200]; - 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& e) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("upnp::resend_request"); -#endif - if (e) 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); -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - char msg[200]; - 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); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - (void)e; - char msg[200]; - snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), e.what()); - log(msg, l); - d.disabled = true; - } -#endif - } - } -} - -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[200]; - snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" - , print_endpoint(from).c_str(), ec.message().c_str()); - log(msg, l); - } - else - { - char msg[200]; - int num_chars = snprintf(msg, sizeof(msg) - , "ignoring response from: %s. IP is not on local network. " - , print_endpoint(from).c_str()); - log(msg, l); - - std::vector net = enum_net_interfaces(m_io_service, ec); - for (std::vector::const_iterator i = net.begin() - , end(net.end()); i != end; ++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; - } - - 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[200]; - snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" - , print_endpoint(from).c_str(), 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 a router. " - , print_endpoint(from).c_str()); - for (std::vector::const_iterator i = routes.begin() - , end(routes.end()); i != end; ++i) - { - if (num_chars >= sizeof(msg)-1) break; - 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); - } - return; - } - } - - http_parser p; - bool error = false; - p.incoming(buffer::const_interval(buffer - , buffer + bytes_transferred), error); - if (error) - { - char msg[200]; - 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[200]; - snprintf(msg, sizeof(msg), "HTTP status %u from %s" - , p.status_code(), print_endpoint(from).c_str()); - log(msg, l); - } - else - { - char msg[200]; - 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[200]; - 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[200]; - 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 (ec) - { - char msg[200]; - snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s" - , d.url.c_str(), print_endpoint(from).c_str(), 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[200]; - 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[200]; - snprintf(msg, sizeof(msg), "URL with port 0 from %s" - , print_endpoint(from).c_str()); - log(msg, l); - return; - } - - char msg[200]; - 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[200]; - snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s" - , int(m_devices.size()), d.url.c_str()); - log(msg, l); - return; - } - - 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); - } - - - if (!m_devices.empty()) - { - 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); -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - char msg[200]; - 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); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - (void)e; - - char msg[200]; - snprintf(msg, sizeof(msg), "connection failed to: %s %s" - , d.url.c_str(), e.what()); - log(msg, l); - d.disabled = true; - } -#endif - } - } - } -} - -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.0\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[200]; - 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 i - = std::find_if(d.mapping.begin(), d.mapping.end() - , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); - if (i == d.mapping.end()) return; - - update_map(d, i - 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[200]; - 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[200]; - 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 - , 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 - , 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[200]; - 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) {} - 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_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.in_service && state.top_tags("service", "controlurl")) - { - 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[200]; - snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" - , d.url.c_str(), e.message().c_str()); - log(msg, l); - d.disabled = true; - return; - } - - if (!p.header_finished()) - { - char msg[200]; - 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[200]; - snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" - , d.url.c_str(), 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[200]; - 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); - d.control_url = protocol + "://" + d.hostname + ":" - + to_string(d.port).elems + s.control_url; - } - - char msg[200]; - 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 (ec) - { - char msg[200]; - snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s" - , d.control_url.c_str(), ec.message().c_str()); - log(msg, l); - d.disabled = true; - return; - } - - if (num_mappings() > 0) update_map(d, 0, 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(), 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_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 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 - - -const char* upnp_error_category::name() const -{ - return "UPnP error"; -} - -std::string upnp_error_category::message(int ev) const -{ - 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; - } - return "unknown UPnP error"; -} - -namespace libtorrent -{ - TORRENT_EXPORT upnp_error_category upnp_category; -} - -#else - -namespace libtorrent -{ - TORRENT_EXPORT ::asio::error::error_category upnp_category(21); -} - -#endif - -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[200]; - snprintf(msg, sizeof(msg), "error while adding port map: %s" - , 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; - } - - // 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[200]; - 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 == 718 || s.error_code == 727) - { - if (m.external_port != 0) - { - // conflict in mapping, set port to wildcard - // and let the router decide - m.external_port = 0; - m.action = mapping_t::action_add; - ++m.failcount; - update_map(d, mapping, l); - return; - } - return_error(mapping, s.error_code, l); - } - else if (s.error_code == 716) - { - // The external port cannot be wildcarder - // pick a random port - m.external_port = 40000 + (std::rand() % 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, 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, 0, error_code(code, 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[200]; - snprintf(msg, sizeof(msg), "error while deleting portmap: %s", 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[200]; - snprintf(msg, sizeof(msg), "error while deleting portmap: %s", 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); - } - - d.mapping[mapping].protocol = none; - - next(d, mapping, l); -} - -void upnp::on_expire(error_code const& e) -{ -#if defined TORRENT_ASIO_DEBUGGING - complete_async("upnp::on_expire"); -#endif - if (e) 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 ec; - m_refresh_timer.expires_at(next_expire, ec); - 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_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/libtorrent_utp/src/ut_metadata.cpp b/libtorrent_utp/src/ut_metadata.cpp deleted file mode 100644 index 93ca997f0..000000000 --- a/libtorrent_utp/src/ut_metadata.cpp +++ /dev/null @@ -1,447 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/pch.hpp" - -#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" -#ifdef TORRENT_STATS -#include "libtorrent/aux_/session_impl.hpp" -#endif - -namespace libtorrent { namespace -{ - int div_round_up(int numerator, int denominator) - { - return (numerator + denominator - 1) / denominator; - } - - 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(char const* buf, int size, int piece, int total_size) - { - if (m_torrent.valid_metadata()) return false; - - if (!m_metadata) - { - // verify the total_size - if (total_size <= 0 || total_size > 500 * 1024) return false; - - m_metadata.reset(new char[total_size]); - m_requested_metadata.resize(div_round_up(total_size, 16 * 1024), 0); - m_metadata_size = total_size; - } - - if (piece < 0 || piece >= int(m_requested_metadata.size())) - return false; - - if (total_size != m_metadata_size) - { - // 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] = (std::numeric_limits::max)(); - - bool have_all = std::count(m_requested_metadata.begin() - , m_requested_metadata.end(), (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()) - std::fill(m_requested_metadata.begin(), m_requested_metadata.end(), 0); - return false; - } - - // clear the storage for the bitfield - std::vector().swap(m_requested_metadata); - - return true; - } - - // returns a piece of the metadata that - // we should request. - int metadata_request(); - - // 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; - } - - 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 > 500 * 1024) return; - m_metadata_size = size; - m_metadata.reset(new char[size]); - m_requested_metadata.resize(div_round_up(size, 16 * 1024), 0); - } - - 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 vector keeps track of how many times each meatdata - // block has been requested - // std::numeric_limits::max() means we have the piece - std::vector m_requested_metadata; - }; - - - struct ut_metadata_peer_plugin : peer_plugin - { - ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc - , ut_metadata_plugin& tp) - : m_message_index(0) - , m_no_metadata(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"] = 15; - 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("m"); - if (!messages || messages->type() != lazy_entry::dict_t) 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); - return true; - } - - void write_metadata_packet(int type, int piece) - { - TORRENT_ASSERT(type >= 0 && type <= 2); - TORRENT_ASSERT(piece >= 0); - TORRENT_ASSERT(!m_pc.associated_torrent().expired()); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_pc.m_logger) << time_now_string() << " ==> UT_METADATA [ " - "type: " << type << " | piece: " << piece << " ]\n"; -#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) - { - 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 != 15) return false; - if (m_message_index == 0) return false; - - if (length > 17 * 1024) - { - 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::undefined_t) - { - m_pc.disconnect(errors::invalid_metadata_message, 2); - return true; - } - - int type = msg["msg_type"].integer(); - int piece = msg["piece"].integer(); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_pc.m_logger) << time_now_string() << " <== UT_METADATA [ " - "type: " << type << " | piece: " << piece << " ]\n"; -#endif - - switch (type) - { - case 0: // request - { - if (!m_torrent.valid_metadata()) - { - write_metadata_packet(2, piece); - return true; - } - // TODO: put the request on the queue in some cases - write_metadata_packet(1, 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()) return true; - - m_sent_requests.erase(i); - entry const* total_size = msg.find_key("total_size"); - m_tp.received_metadata(body.begin + len, body.left() - len, piece - , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0); - } - break; - case 2: // have no data - { - m_no_metadata = time_now(); - 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() - { - // 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_sent_requests.push_back(piece); - write_metadata_packet(0, piece); - } - } - - bool has_metadata() const - { - return time_now() - m_no_metadata > minutes(1); - } - - private: - - // this is the message index the remote peer uses - // for metadata extension messages. - int m_message_index; - - // this is set to the current time each time we get a - // "I don't have metadata" message. - ptime m_no_metadata; - - // 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)); - } - - int ut_metadata_plugin::metadata_request() - { - 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, 1); - return 0; - } - - int piece = i - m_requested_metadata.begin(); - m_requested_metadata[piece] = piece; - return piece; - } - -} } - -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)); - } - -} - - diff --git a/libtorrent_utp/src/ut_pex.cpp b/libtorrent_utp/src/ut_pex.cpp deleted file mode 100644 index 2dff2e94f..000000000 --- a/libtorrent_utp/src/ut_pex.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - -Copyright (c) 2006, 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. - -*/ - -#include "libtorrent/pch.hpp" - -#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/extensions/ut_pex.hpp" - -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 peers that we haven't connected to - // (that have connected to us) - if (!p.is_local()) return false; - // don't send out peers that we haven't successfully connected to - if (p.is_connecting()) return false; - return true; - } - - struct ut_pex_plugin: torrent_plugin - { - ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55), 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() - { - if (++m_1_minute < 60) return; - - m_1_minute = 0; - - 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 const& 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); - - // 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 |= p->get_socket()->get() ? 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; - int m_1_minute; - 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_pex(min_time()) - , m_1_minute(55) - , m_message_index(0) - , m_first_time(true) - {} - - 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 = 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; - } - - ptime now = time_now(); - if (now - m_last_pex < seconds(10)) - { - // 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; - } - - if (body.left() < length) return true; - - m_last_pex = 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 - (*m_pc.m_logger) << time_now_string() << " <== PEX [" - " dropped:" << (p?p->string_length():0); -#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 - (*m_pc.m_logger) << " added:" << (p?p->string_length():0) << " ]\n"; -#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 (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"); - 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"); - 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 (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 - return true; - } - - // the peers second tick - // every minute we send a pex message - virtual void tick() - { - if (!m_message_index) return; // no handshake yet - if (++m_1_minute <= 60) return; - - if (m_first_time) - { - send_ut_peer_list(); - m_first_time = false; - } - else - { - send_ut_peer_diff(); - } - m_1_minute = 0; - } - - 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(); - - buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); - - detail::write_uint32(1 + 1 + pex_msg.size(), i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - std::copy(pex_msg.begin(), pex_msg.end(), i.begin); - i.begin += pex_msg.size(); - - TORRENT_ASSERT(i.begin == i.end); - m_pc.setup_send(); - } - - 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 - int flags = p->is_seed() ? 2 : 0; -#ifndef TORRENT_DISABLE_ENCRYPTION - flags |= p->supports_encryption() ? 1 : 0; -#endif - tcp::endpoint const& remote = peer->remote(); - // 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); - - buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); - - detail::write_uint32(1 + 1 + pex_msg.size(), i.begin); - detail::write_uint8(bt_peer_connection::msg_extended, i.begin); - detail::write_uint8(m_message_index, i.begin); - std::copy(pex_msg.begin(), pex_msg.end(), i.begin); - i.begin += pex_msg.size(); - - TORRENT_ASSERT(i.begin == i.end); - m_pc.setup_send(); - } - - 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 message we received - ptime m_last_pex; - - int m_1_minute; - 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 full - // 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(); - - bt_peer_connection* c = static_cast(pc); - 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 - } -} - - diff --git a/libtorrent_utp/src/utp_socket_manager.cpp b/libtorrent_utp/src/utp_socket_manager.cpp deleted file mode 100644 index 2f512f66b..000000000 --- a/libtorrent_utp/src/utp_socket_manager.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - -Copyright (c) 2009, 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 - -// #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_sock_buf_size(0) - {} - - 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; - - 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; - } - - // 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 - } - - tcp::endpoint utp_socket_manager::local_endpoint(error_code& ec) const - { - return m_sock.local_endpoint(ec); - } - - bool utp_socket_manager::incoming_packet(char const* p, int size, udp::endpoint const& ep) - { -// UTP_LOGV("incoming packet size:%d\n", size); - - if (size < 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); - } - - socket_map_t::iterator i = m_utp_sockets.find(id); - - 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) - { - // create the new socket with this ID - m_new_connection = id; - -// 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; - 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; - } - - // #error send reset - - return false; - } - - 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; - } - - 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 = rand(); - 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/libtorrent_utp/src/utp_stream.cpp b/libtorrent_utp/src/utp_stream.cpp deleted file mode 100644 index 9fbd63bc7..000000000 --- a/libtorrent_utp/src/utp_stream.cpp +++ /dev/null @@ -1,2900 +0,0 @@ -/* - -Copyright (c) 2009, 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/sliding_average.hpp" -#include "libtorrent/utp_socket_manager.hpp" -#include "libtorrent/alloca.hpp" -#include "libtorrent/timestamp_history.hpp" -#include "libtorrent/error.hpp" -#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 - sack_resend_limit = 3, -}; - -// 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_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 size of the buffer 'buf' pointst 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:7; - - // 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; - - // the actual packet buffer - char buf[]; -}; - -// 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_read_handler(0) - , m_write_handler(0) - , m_connect_handler(0) - , m_remote_address() - , m_read_timeout() - , m_write_timeout() - , m_timeout(time_now_hires() + milliseconds(m_sm->connect_timeout())) - , m_last_cwnd_hit(min_time()) - , m_ack_timer(time_now() + minutes(10)) - , m_last_history_step(time_now_hires()) - , m_cwnd(TORRENT_ETHERNET_MTU << 16) - , 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(100 * 1024 * 1024) - , m_in_packets(0) - , m_out_packets(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_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) - { - for (int i = 0; i != num_delay_hist; ++i) - m_delay_sample_hist[i] = UINT_MAX; - } - - ~utp_socket_impl(); - - void init(udp::endpoint const& ep, boost::uint16_t id, void* userdata - , utp_socket_manager* sm) - { - m_remote_address = ep.address(); - m_port = ep.port(); - m_send_id = id + 1; - m_recv_id = id; - m_userdata = userdata; - m_sm = sm; - } - - void tick(ptime const& now); - void init_mtu(int link_mtu, int utp_mtu); - bool incoming_packet(char const* buf, int size - , udp::endpoint const& ep, ptime receive_time); - 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; - void destroy(); - void detach(); - void send_syn(); - void send_fin(); - - bool send_pkt(bool ack); - bool resend_packet(packet* p, bool fast_resend = false); - void send_reset(utp_header* ph); - void parse_sack(boost::uint16_t packet_ack, char const* ptr, int size, int* acked_bytes - , ptime const now, boost::uint32_t& min_rtt); - void write_payload(char* ptr, int size); - void ack_packet(packet* p, ptime const& receive_time - , boost::uint32_t& min_rtt, boost::uint16_t seq_nr); - void write_sack(char* buf, int size) const; - void incoming(char 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(ptime now); - void maybe_trigger_send_callback(ptime now); - bool cancel_handlers(error_code const& ec, bool kill); - bool consume_incoming_data( - utp_header const* ph, char const* ptr, int payload_size, ptime now); - void update_mtu_limits(); - void experienced_loss(int seq_nr); - - void check_receive_buffers() const; - - 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; - - // 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 send and receive buffers - // maps packet sequence numbers - packet_buffer m_inbuf; - packet_buffer m_outbuf; - - // timers when we should trigger the read and - // write callbacks (unless the buffers fill up - // before) - ptime m_read_timeout; - ptime m_write_timeout; - - // 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 wanted to send more data, but couldn't because - // it would bring the number of outstanding bytes above the cwnd. - // this is used to restrict increasing the cwnd size when we're - // not sending fast enough to need it bigger - ptime m_last_cwnd_hit; - - // the next time we need to send an ACK the latest - // updated every time we send an ACK and every time we - // put off sending an ACK for a received packet - ptime m_ack_timer; - - // 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() is signed. - boost::int64_t m_cwnd; - - timestamp_history m_delay_hist; - timestamp_history m_their_delay_hist; - - // 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; - - // 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) - // TODO: support the option to turn it off - bool m_nagle: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(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_socket_impl::update_mtu_limits() -{ - TORRENT_ASSERT(m_mtu_floor <= m_mtu_ceiling); - m_mtu = (m_mtu_floor + m_mtu_ceiling) / 2; - - // clear the mtu probe sequence number since - // it was either dropped or acked - m_mtu_seq = 0; - - if (m_mtu_ceiling - m_mtu_floor < 10) - { - // we have narrowed down the mtu within 10 - // bytes. That's good enough, start using - // floor as the packet size from now on. - // set the ceiling to the floor as well to - // disable more probes to be sent - // we'll never re-probe this connection - m_mtu = m_mtu_ceiling = m_mtu_floor; - } -} - -int utp_socket_state(utp_socket_impl const* s) -{ - return s->m_state; -} - -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; - m_impl->destroy(); -} - -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 m_impl->m_sm->local_endpoint(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; - - 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) -{ - 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(time_now_hires()); -} - -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(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\n" - , m_impl, pop_packets); - - 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); - - 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(false)); - - // 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(time_now_hires()); -} - -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; - - if (m_impl->test_socket_state()) return; - m_impl->send_syn(); -} - -// =========== utp_socket_impl ============ - -utp_socket_impl::~utp_socket_impl() -{ - TORRENT_ASSERT(!m_attached); - - 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); - } -} - -bool utp_socket_impl::should_delete() const -{ - // 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 - bool ret = (m_state >= UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE) - && !m_attached; - - if (ret) - { - UTP_LOGV("%8p: should_delete() = true\n", this); - } - - return ret; -} - -void utp_socket_impl::maybe_trigger_receive_callback(ptime now) -{ - // nothing has been read or there's no outstanding read operation - if (m_read == 0 || m_read_handler == 0) return; - - if (m_read > m_read_buffer_size / 2 || now >= m_read_timeout) - { - 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(ptime now) -{ - // nothing has been written or there's no outstanding write operation - if (m_written == 0 || m_write_handler == 0) return; - - if (m_written > m_write_buffer_size / 2 || now >= m_write_timeout) - { - 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(); - } -} - -void utp_socket_impl::destroy() -{ -#if TORRENT_UTP_LOG - UTP_LOGV("%8p: destroy state:%s\n", this, socket_state_names[m_state]); -#endif - - if (m_userdata == 0) return; - - if (m_state == UTP_STATE_CONNECTED) - { - send_fin(); - if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; - } - - 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; - } - - // #error our end is closing. Wait for everything to be acked -} - -void utp_socket_impl::detach() -{ - UTP_LOGV("%8p: detach()\n", this); - m_attached = false; -} - -void utp_socket_impl::send_syn() -{ - m_seq_nr = rand(); - 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 = 1; - 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())); - -#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) - { - free(p); - m_error = ec; - m_state = UTP_STATE_ERROR_WAIT; - test_socket_state(); - return; - } - - TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); - m_outbuf.insert(m_seq_nr, p); - - 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 -} - -void utp_socket_impl::send_fin() -{ - TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT); - - // we need a heap allocated packet in order to stick it - // in the send buffer, so that we can resend it - packet* p = (packet*)malloc(sizeof(packet) + sizeof(utp_header)); - - p->size = sizeof(utp_header); - p->header_size = sizeof(utp_header); - p->num_transmissions = 1; - p->need_resend = false; - utp_header* h = (utp_header*)p->buf; - - h->type_ver = (ST_FIN << 4) | 1; - h->extension = 0; - h->connection_id = m_send_id; - h->timestamp_difference_microseconds = m_reply_micro; - h->wnd_size = m_in_buf_size - m_buffered_incoming_bytes - m_receive_buffer_size; - h->seq_nr = m_seq_nr; - h->ack_nr = m_ack_nr; - - ptime now = time_now_hires(); - p->send_time = now; - h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); - - error_code ec; - m_sm->send_packet(udp::endpoint(m_remote_address, m_port) - , (char const*)h, sizeof(utp_header), ec); - -#if TORRENT_UTP_LOG - UTP_LOGV("%8p: sending FIN seq_nr:%d ack_nr:%d type:%s " - "id:%d target:%s size:%d error:%s send_buffer_size:%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() - , int(sizeof(utp_header)), ec.message().c_str(), m_write_buffer_size); -#endif - - if (ec) - { - m_error = ec; - m_state = UTP_STATE_ERROR_WAIT; - test_socket_state(); - free(p); - return; - } - -#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 - - packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); - if (old) - { - if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; - free(old); - } - m_seq_nr = (m_seq_nr + 1) & ACK_MASK; - m_fast_resend_seq_nr = m_seq_nr; - - TORRENT_ASSERT(!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) -{ - 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 = rand(); - 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, char const* ptr - , int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt) -{ - if (size == 0) return; - - // this is the sequence number the current bit represents - int ack_nr = (packet_ack + 2) & ACK_MASK; - -#if TORRENT_UTP_LOG - std::string bitmask; - for (char 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 (char 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, 0xffff)) ++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 if ((m_acked_seq_nr + 1) == ack_nr) - { - // this packet must have been acked by a previous - // selective ack - m_acked_seq_nr = ack_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; - } - - // 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, 0xffff)) - { - experienced_loss(m_fast_resend_seq_nr); - int num_resent = 0; - for (; m_fast_resend_seq_nr != last_ack; m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK) - { - packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); - 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(char* ptr, int size) -{ -#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; - - ptime now = time_now_hires(); - - 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)); - memcpy(ptr, static_cast(i->buf), to_copy); - size -= to_copy; - if (m_written == 0) - { - m_write_timeout = now + milliseconds(100); - UTP_LOGV("%8p: setting write timeout to 100 ms from now\n", this); - } - TORRENT_ASSERT(to_copy >= 0); - TORRENT_ASSERT(to_copy < INT_MAX / 2 && m_written < INT_MAX / 2); - 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 -} - -// 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) -bool utp_socket_impl::send_pkt(bool ack) -{ - // This assert is bad because we call this function to ack - // received FIN when we're in UTP_STATE_FIN_SENT. - // - // TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT); - - // first see if we need to resend any packets - - 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 ack is set, we need to continue - // to send our ack anyway, if we don't have to send an - // ack, we might as well return - if (!ack) 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; - } - - bool ret = false; - - 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; - ret = true; // there's more data to send - } - - // 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 - payload_size = 0; - - // we're restrained by the window size - m_last_cwnd_hit = time_now_hires(); - - // there's no more space in the cwnd, no need to - // try to send more right now - ret = false; - - UTP_LOGV("%8p: no space in window send_buffer_size:%d cwnd:%d " - "ret:%d adv_wnd:%d in-flight:%d mtu:%d\n" - , this, m_write_buffer_size, int(m_cwnd >> 16) - , ret, m_adv_wnd, m_bytes_in_flight, m_mtu); - } - - // if we don't have any data to send, or can't send any data - // and we don't have any data to ack, don't send a packet - if (payload_size == 0 && !ack) - { -#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 " - "ret:%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, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) - , int(ret), m_adv_wnd, m_bytes_in_flight, m_mtu); -#endif - return false; - } - - if (((m_seq_nr - m_acked_seq_nr) & ACK_MASK) > 1 - && payload_size < m_mtu - header_size - && !ack - && 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 " - "ret:%d adv_wnd:%d in-flight:%d mtu:%d\n" - , this, m_write_buffer_size, int(m_cwnd >> 16) - , ret, m_adv_wnd, m_bytes_in_flight, m_mtu); - return false; - } - - int packet_size = header_size + payload_size; - - // MTU DISCOVERY - bool use_as_probe = false; - if (m_mtu_seq == 0 - && packet_size > m_mtu_floor - && m_seq_nr != 0) - { - use_as_probe = true; - m_mtu_seq = m_seq_nr; - } - - packet* p; - // 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) + packet_size); - else p = (packet*)TORRENT_ALLOCA(char, sizeof(packet) + packet_size); - - p->size = packet_size; - p->header_size = packet_size - payload_size; - p->num_transmissions = 1; - p->need_resend = false; - p->mtu_probe = use_as_probe; - char* ptr = p->buf; - utp_header* h = (utp_header*)ptr; - ptr += sizeof(utp_header); - - h->type_ver = ((payload_size ? ST_DATA : ST_STATE) << 4) | 1; - h->extension = sack ? 1 : 0; - h->connection_id = m_send_id; - h->timestamp_difference_microseconds = m_reply_micro; - h->wnd_size = m_in_buf_size - m_buffered_incoming_bytes - m_receive_buffer_size; - // 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->ack_nr = m_ack_nr; - - if (sack) - { - *ptr++ = 0; // end of extension chain - *ptr++ = sack; // bytes for SACK bitfield - write_sack(ptr, sack); - ptr += sack; - } - - write_payload(ptr, payload_size); - - // 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())); - -#if TORRENT_UTP_LOG - UTP_LOGV("%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 " - "ret:%d adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u " - "mtu_probe:%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() - , packet_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) - , ret, 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)); -#endif - - TORRENT_ASSERT(!m_error); - - error_code ec; - m_sm->send_packet(udp::endpoint(m_remote_address, m_port) - , (char const*)h, packet_size, ec - , use_as_probe ? utp_socket_manager::dont_fragment : 0); - - ++m_out_packets; - - if (ec == error::message_size && use_as_probe) - { - m_mtu_ceiling = m_mtu - 1; - update_mtu_limits(); - // TODO: we might want to do something else here - // as well, to resend the packet immediately without - // it being an MTU probe - } - else if (ec) - { - m_error = ec; - m_state = UTP_STATE_ERROR_WAIT; - test_socket_state(); - if (payload_size) free(p); - return false; - } - - // we just sent a packet. this means we just ACKed the last received - // packet as well. So, we can now reset the delayed ack timer to - // not trigger for a long time - m_ack_timer = now + minutes(10); - - // if we have payload, we need to save the packet until it's acked - // and progress m_seq_nr - if (payload_size) - { -#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 - packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); - if (old) - { - if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; - free(old); - } - m_seq_nr = (m_seq_nr + 1) & ACK_MASK; - TORRENT_ASSERT(payload_size >= 0); - m_bytes_in_flight += payload_size; - } - - return ret; -} - -// size is in bytes -void utp_socket_impl::write_sack(char* buf, int size) const -{ - TORRENT_ASSERT(m_inbuf.size()); - int ack_nr = (m_ack_nr + 2) & ACK_MASK; - char* 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) -{ - // for fast re-sends the packet hasn't been marked as needing resending - TORRENT_ASSERT(p->need_resend || fast_resend); - - TORRENT_ASSERT(!m_error); - - if (fast_resend - && ((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq - && m_mtu_seq != 0) - { - // we got multiple acks for the packet before our probe, assume - // it was dropped because it was too big - m_mtu_ceiling = m_mtu - 1; - update_mtu_limits(); - } - - // we can only resend the packet if there's - // enough space in our congestion window - 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_last_cwnd_hit = time_now_hires(); - 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; - - ++p->num_transmissions; - 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())); - - error_code ec; - m_sm->send_packet(udp::endpoint(m_remote_address, m_port) - , (char const*)p->buf, p->size, ec); - ++m_out_packets; - -#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) - { - m_error = ec; - m_state = UTP_STATE_ERROR_WAIT; - test_socket_state(); - return false; - } - - return true; -} - -void utp_socket_impl::experienced_loss(int seq_nr) -{ - // 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 - if (compare_less_wrap(seq_nr, m_loss_seq_nr, ACK_MASK)) return; - - // cut window size in 2 - m_cwnd = (std::max)(m_cwnd / 2, 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 -} - -void utp_socket_impl::ack_packet(packet* p, ptime const& receive_time - , boost::uint32_t& min_rtt, boost::uint16_t seq_nr) -{ - TORRENT_ASSERT(p); - 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 = m_mtu; - update_mtu_limits(); - } - - // increment the acked sequence number counter - if (((m_acked_seq_nr + 1) & ACK_MASK) == seq_nr) - { - m_acked_seq_nr = seq_nr; - // 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; - } - // increment the fast resend sequence number - if (m_fast_resend_seq_nr == seq_nr) - m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; - - 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(char const* buf, int size, packet* p, ptime now) -{ - 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); - if (m_read == 0) - { - m_read_timeout = now + milliseconds(100); - UTP_LOGV("%8p: setting read timeout to 100 ms from now\n", this); - } - m_read += to_copy; - target->buf = ((char*)target->buf) + to_copy; - target->len -= to_copy; - buf += 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); - maybe_trigger_receive_callback(now); - 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); - } - if (m_receive_buffer_size == 0) m_read_timeout = now + milliseconds(100); - // 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) -{ - TORRENT_ASSERT(ec); - bool ret = m_read_handler || m_write_handler || m_connect_handler; - 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, char const* ptr, int payload_size - , ptime now) -{ - 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 - return true; - } - - if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK)) - { - TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); - - // 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())); - } - - // should we trigger the read handler? - maybe_trigger_receive_callback(now); - } - 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; - } - - // 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; - 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() -{ - // 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) -{ - // 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; - } - - m_mtu = utp_mtu; - m_mtu_ceiling = utp_mtu; - 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 = m_mtu << 16; - - UTP_LOGV("%8p: intializing 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(char const* buf, int size - , udp::endpoint const& ep, ptime receive_time) -{ - utp_header* ph = (utp_header*)buf; - - if (ph->get_version() != 1) - { - UTP_LOGV("%8p: incoming packet version:%d (ignored)\n" - , this, int(ph->get_version())); - return false; - } - - // SYN packets have special (reverse) connection ids - if (ph->get_type() != ST_SYN && ph->connection_id != m_recv_id) - { - UTP_LOGV("%8p: incoming packet id:%d expected:%d (ignored)\n" - , this, int(ph->connection_id), int(m_recv_id)); - return false; - } - - if (ph->get_type() >= NUM_TYPES) - { - UTP_LOGV("%8p: incoming packet type:%d (ignored)\n" - , this, int(ph->get_type())); - 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_LOGV("%8p: incoming packet type:ST_SYN (ignored)\n", this); - 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) - { - m_reply_micro = boost::uint32_t(total_microseconds(receive_time - min_time())) - - 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) - { - // 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); - } - - if (ph->get_type() == ST_RESET) - { - 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; - } - - // 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_LOGV("%8p: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n" - , this, int(ph->ack_nr), m_seq_nr); - 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); - 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)) - { - UTP_LOGV("%8p: incoming packet seq_nr:%d eof_seq_nr:%d (ignored)\n" - , this, int(ph->seq_nr), m_eof_seq_nr); - - } - - 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_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" - , this, int(ph->seq_nr), m_ack_nr); - 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 = UINT_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) - { - if (((m_acked_seq_nr + 1) & ACK_MASK) == ack_nr) - m_acked_seq_nr = ack_nr; - continue; - } - acked_bytes += p->size - p->header_size; - ack_packet(p, receive_time, min_rtt, ack_nr); - } - - // 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; - if (compare_less_wrap(m_fast_resend_seq_nr, (m_acked_seq_nr + 1) & ACK_MASK, ACK_MASK)) - m_fast_resend_seq_nr = (m_acked_seq_nr + 1) & ACK_MASK; - } - - // look for extended headers - char 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_LOGV("%8p: invalid extension header\n", this); - return true; - } - int next_extension = unsigned(*ptr++); - unsigned int len = unsigned(*ptr++); - if (ptr - buf + len > size) - { - UTP_LOGV("%8p: invalid extension header size:%d packet:%d\n" - , this, len, int(ptr - buf)); - 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.\n", this, m_fast_resend_seq_nr); - - // resend the lost packet - packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); - TORRENT_ASSERT(p); - 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; - } - // don't fast-resend this again - m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; - } - - // 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(true); - 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(); - -#if TORRENT_UTP_LOG - UTP_LOGV("%8p: state:%s\n" - , this, socket_state_names[m_state]); -#endif - m_ack_nr = ph->seq_nr; - m_seq_nr = rand(); - m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; - m_loss_seq_nr = m_acked_seq_nr; - - TORRENT_ASSERT(m_send_id == ph->connection_id); - TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff)); - - send_pkt(true); - - return true; - } - else - { -#if TORRENT_UTP_LOG - UTP_LOGV("%8p: 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. - - // 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); - - if (sample && acked_bytes && prev_bytes_in_flight) - do_ledbat(acked_bytes, delay, prev_bytes_in_flight, receive_time); - - 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; - int delayed_ack = m_sm->delayed_ack(); - if (has_ack && delayed_ack && m_ack_timer > receive_time) - { - // we have data to ACK, and delayed ACKs are enabled. - // update the ACK timer and clear the flag, to pretend - // like we don't have anything to ACK - m_ack_timer = (std::min)(m_ack_timer, receive_time + milliseconds(delayed_ack)); - has_ack = false; - UTP_LOGV("%8p: delaying ack. timer triggers in %d milliseconds\n" - , this, int(total_milliseconds(m_ack_timer - time_now_hires()))); - } - - if (send_pkt(has_ack)) - { - // try to send more data as long as we can - while (send_pkt(false)); - } - maybe_trigger_send_callback(receive_time); - 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 " - "\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); - } -#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) -{ - // 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(); - - // 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 = (window_factor * delay_factor) >> 16; - scaled_gain *= boost::int64_t(m_sm->gain_factor()); - - if (scaled_gain > 0 && m_last_cwnd_hit + milliseconds((std::max)(m_rtt.mean(), 10)) < now) - { - // we haven't bumped into the cwnd limit size in the last second - // this probably means we have a send rate limit, so we shouldn't make - // the cwnd size any larger - scaled_gain = 0; - } - - UTP_LOGV("%8p: do_ledbat delay:%d off_target: %d window_factor:%f target_factor:%f " - "scaled_gain:%f cwnd:%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)); - - // 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); - } -} - -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 -{ - // 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; - - 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) -{ -#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 - bool window_opened = false; - - TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); - - // don't hang on to received data for too long, and don't - // wait too long telling the client we've sent some data. - // these functions will trigger time callback if we have - // a reason to and it's been long enough since we sent or - // received the data - maybe_trigger_receive_callback(now); - maybe_trigger_send_callback(now); - - // 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 - - // the window went from less than one MSS to one MSS - // we can now sent messages again, the send window was opened - if ((m_cwnd >> 16) < m_mtu) window_opened = true; - - m_cwnd = m_mtu << 16; - if (m_outbuf.size()) ++m_num_timeouts; - m_timeout = now + milliseconds(packet_timeout()); - - UTP_LOGV("%8p: timeout resetting cwnd:%d\n" - , this, int(m_cwnd >> 16)); - - 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; - update_mtu_limits(); - } - - // 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; - - // 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.\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(false); - 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; - } - } - - if (now > m_ack_timer) - { - UTP_LOGV("%8p: ack timer expired, sending ACK\n", this); - // we need to send an ACK now! - send_pkt(true); - if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) 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 -{ - 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(size == m_receive_buffer_size); -} - -} - diff --git a/libtorrent_utp/src/web_connection_base.cpp b/libtorrent_utp/src/web_connection_base.cpp deleted file mode 100644 index 416fb33c0..000000000 --- a/libtorrent_utp/src/web_connection_base.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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 - , std::string const& url - , policy::peer* peerinfo - , std::string const& auth - , web_seed_entry::headers_t const& extra_headers) - : peer_connection(ses, t, s, remote, peerinfo) - , m_external_auth(auth) - , m_extra_headers(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(url, ec); - TORRENT_ASSERT(!ec); - -#ifdef TORRENT_USE_OPENSSL - if (protocol == "https") m_ssl = true; -#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) { - 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); - } - - -#ifdef TORRENT_DEBUG - 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/libtorrent_utp/src/web_peer_connection.cpp b/libtorrent_utp/src/web_peer_connection.cpp deleted file mode 100644 index fe856a906..000000000 --- a/libtorrent_utp/src/web_peer_connection.cpp +++ /dev/null @@ -1,800 +0,0 @@ -/* - -Copyright (c) 2003, 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/pch.hpp" - -#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 -{ - web_peer_connection::web_peer_connection( - session_impl& ses - , boost::weak_ptr t - , boost::shared_ptr s - , tcp::endpoint const& remote - , std::string const& url - , policy::peer* peerinfo - , std::string const& auth - , web_seed_entry::headers_t const& extra_headers) - : web_connection_base(ses, t, s, remote, url, peerinfo, auth, extra_headers) - , m_url(url) - , m_range_pos(0) - , m_block_pos(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(); - - // we always prefer downloading 1 MB chunks - // from web seeds - prefer_whole_pieces((1024 * 1024) / tor->torrent_file().piece_length()); - - // 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", url.c_str()); -#endif - } - - void web_peer_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 - 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 < piece_block::invalid.block_index); - TORRENT_ASSERT(ret.piece_index < 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] == '/') m_path += t->torrent_file().name(); - if (!m_url.empty() && m_url[m_url.size() - 1] == '/') m_url += t->torrent_file().name(); - } - - torrent_info const& info = t->torrent_file(); - - 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; - - 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(r.piece) * info.piece_length() + r.start).elems; - request += "-"; - request += to_string(size_type(r.piece) * info.piece_length() + r.start + r.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(r.piece, r.start - , r.length); - - for (std::vector::iterator i = files.begin(); - i != files.end(); ++i) - { - file_slice const& f = *i; - - request += "GET "; - if (using_proxy) - { - request += m_url; - std::string path = info.orig_files().file_path(info.orig_files().at(f.file_index)); -#ifdef TORRENT_WINDOWS - convert_path_to_posix(path); -#endif - request += escape_path(path.c_str(), path.length()); - } - else - { - std::string path = m_path; - path += info.orig_files().file_path(info.orig_files().at(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; - 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 - - 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; - } - } - - void web_peer_connection::on_receive(error_code const& error - , std::size_t bytes_transferred) - { - INVARIANT_CHECK; - -#ifdef TORRENT_DEBUG - size_type 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() + 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 error = false; - boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, error); - m_statistics.received_bytes(0, protocol); - TORRENT_ASSERT(bytes_transferred >= protocol); - bytes_transferred -= protocol; - - if (error) - { - 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() + 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() + 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) - { -#ifdef TORRENT_VERBOSE_LOGGING - peer_log("*** STATUS: %d %s", m_parser.status_code(), m_parser.message().c_str()); - std::map const& headers = m_parser.headers(); - for (std::map::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())) - { - 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(errors::http_error, 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); - 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) - { - m_statistics.received_bytes(0, bytes_transferred); - TORRENT_ASSERT(!m_file_requests.empty()); - int file_index = m_file_requests.front(); - - torrent_info const& info = t->torrent_file(); - std::string path = info.orig_files().file_path(info.orig_files().at(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); - 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); - } - t->add_web_seed(location, web_seed_entry::url_seed, m_external_auth, m_extra_headers); - t->remove_web_seed(this); - 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); - 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); - 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; - } - } - - 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; - } - - // ========================= - // === 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 >= 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 >= 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 - cut_receive_buffer(header_size, t->block_size() + 1024, 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; - } - } - } - - int left_in_response = range_end - range_start - m_range_pos; - int payload_transferred = (std::min)(left_in_response, int(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(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; - -#if 0 - std::cerr << "REQUESTS: m_requests: " << m_requests.size() - << " file_requests: " << m_file_requests.size() << std::endl; -#endif - - 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)); - - size_type rs = size_type(in_range.piece) * info.piece_length() + in_range.start; - size_type re = rs + in_range.length; - size_type fs = size_type(front_request.piece) * info.piece_length() + front_request.start; -#if 0 - size_type fe = fs + front_request.length; - - std::cerr << "RANGE: r = (" << rs << ", " << re << " ) " - "f = (" << fs << ", " << fe << ") " - "file_index = " << file_index << " received_body = " << m_received_body << std::endl; -#endif - - // 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 > 0) - { - 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); - } - - if (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. - - incoming_piece(front_request, &m_piece[0]); - m_requests.pop_front(); - if (associated_torrent().expired()) return; - m_block_pos -= front_request.length; - cut_receive_buffer(m_body_start, t->block_size() + 1024); - 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()); - } - } - - // report all received blocks to the bittorrent engine - while (!m_requests.empty() - && range_contains(in_range, m_requests.front(), info.piece_length()) - && recv_buffer.left() >= 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); - m_requests.pop_front(); - if (associated_torrent().expired()) return; - 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() + 1024); - 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()) - { - range_overlaps_request = in_range.start + in_range.length - > m_requests.front().start + int(m_piece.size()); - - 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) - { - 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; - } - 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() + 1024); - 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; - continue; - } - if (bytes_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; - } - -} - diff --git a/libtorrent_utp/test/Jamfile b/libtorrent_utp/test/Jamfile deleted file mode 100644 index 813fced4e..000000000 --- a/libtorrent_utp/test/Jamfile +++ /dev/null @@ -1,49 +0,0 @@ -use-project /torrent : .. ; - -exe test_natpmp : test_natpmp.cpp /torrent//torrent - : multi on full ; - -explicit test_natpmp ; - -project - : requirements - /torrent//torrent - main.cpp - setup_transfer.cpp - : default-build - multi - full - on - ; - -test-suite libtorrent : - [ run test_threads.cpp ] - [ run test_bandwidth_limiter.cpp ] - [ run test_buffer.cpp ] - [ run test_piece_picker.cpp ] - [ run test_bencoding.cpp ] - [ run test_fast_extension.cpp ] - [ run test_primitives.cpp ] - [ run test_ip_filter.cpp ] - [ run test_hasher.cpp ] - [ run test_dht.cpp ] - [ run test_storage.cpp ] - [ run test_upnp.cpp ] - - [ run test_web_seed.cpp ] - [ run test_bdecode_performance.cpp ] - [ run test_pe_crypto.cpp ] - - [ run test_utp.cpp ] - [ run test_auto_unchoke.cpp ] - [ run test_http_connection.cpp ] - [ run test_torrent.cpp ] - [ run test_transfer.cpp ] -# [ run test_entry.cpp ] - [ run test_metadata_extension.cpp ] - [ run test_trackers_extension.cpp ] - [ run test_swarm.cpp ] - [ run test_lsd.cpp ] - [ run test_pex.cpp ] - ; - diff --git a/libtorrent_utp/test/Makefile.am b/libtorrent_utp/test/Makefile.am deleted file mode 100644 index 8e8aed2ba..000000000 --- a/libtorrent_utp/test/Makefile.am +++ /dev/null @@ -1,71 +0,0 @@ -test_programs = \ - test_auto_unchoke \ - test_bandwidth_limiter \ - test_bdecode_performance \ - test_bencoding \ - test_buffer \ - test_fast_extension \ - test_hasher \ - test_http_connection \ - test_ip_filter \ - test_dht \ - test_lsd \ - test_metadata_extension \ - test_natpmp \ - test_pe_crypto \ - test_pex \ - test_piece_picker \ - test_primitives \ - test_storage \ - test_swarm \ - test_torrent \ - test_trackers_extension \ - test_transfer \ - test_upnp \ - test_web_seed - -if ENABLE_TESTS -check_PROGRAMS = $(test_programs) -noinst_LTLIBRARIES = libtest.la -endif - -TESTS = $(check_PROGRAMS) - -EXTRA_DIST = Jamfile -EXTRA_PROGRAMS = $(test_programs) - -noinst_HEADERS = test.hpp setup_transfer.hpp - -libtest_la_SOURCES = main.cpp setup_transfer.cpp - -test_auto_unchoke_SOURCES = test_auto_unchoke.cpp -test_bandwidth_limiter_SOURCES = test_bandwidth_limiter.cpp -test_bdecode_performance_SOURCES = test_bdecode_performance.cpp -test_dht_SOURCES = test_dht.cpp -test_bencoding_SOURCES = test_bencoding.cpp -test_buffer_SOURCES = test_buffer.cpp -test_fast_extension_SOURCES = test_fast_extension.cpp -test_hasher_SOURCES = test_hasher.cpp -test_http_connection_SOURCES = test_http_connection.cpp -test_ip_filter_SOURCES = test_ip_filter.cpp -test_lsd_SOURCES = test_lsd.cpp -test_metadata_extension_SOURCES = test_metadata_extension.cpp -test_natpmp_SOURCES = test_natpmp.cpp -test_pe_crypto_SOURCES = test_pe_crypto.cpp -test_pex_SOURCES = test_pex.cpp -test_piece_picker_SOURCES = test_piece_picker.cpp -test_primitives_SOURCES = test_primitives.cpp -test_storage_SOURCES = test_storage.cpp -test_swarm_SOURCES = test_swarm.cpp -test_torrent_SOURCES = test_torrent.cpp -test_trackers_extension_SOURCES = test_trackers_extension.cpp -test_transfer_SOURCES = test_transfer.cpp -test_upnp_SOURCES = test_upnp.cpp -test_web_seed_SOURCES = test_web_seed.cpp - -LDADD = $(top_builddir)/src/libtorrent-rasterbar.la libtest.la - -#AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @DEBUGFLAGS@ @PTHREAD_CFLAGS@ -AM_CPPFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ - -#AM_LDFLAGS= @BOOST_SYSTEM_LIB@ @BOOST_FILESYSTEM_LIB@ @BOOST_THREAD_LIB@ @PTHREAD_LIBS@ @SSL_LDFLAGS@ @SSL_LIBS@ diff --git a/libtorrent_utp/test/main.cpp b/libtorrent_utp/test/main.cpp deleted file mode 100644 index 839cf7287..000000000 --- a/libtorrent_utp/test/main.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - -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. - -*/ - -#include -#include - -int test_main(); - -extern bool tests_failure; - -int main() -{ -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - test_main(); - return tests_failure ? 1 : 0; -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception const& e) - { - std::cerr << "Terminated with exception: \"" << e.what() << "\"\n"; - return 1; - } - catch (...) - { - std::cerr << "Terminated with unknown exception\n"; - return 1; - } -#endif -} - diff --git a/libtorrent_utp/test/setup_transfer.cpp b/libtorrent_utp/test/setup_transfer.cpp deleted file mode 100644 index 9a16dce12..000000000 --- a/libtorrent_utp/test/setup_transfer.cpp +++ /dev/null @@ -1,961 +0,0 @@ -/* - -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. - -*/ - -#include - -#include "libtorrent/session.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/http_parser.hpp" -#include "libtorrent/thread.hpp" - -#include "libtorrent/thread.hpp" -#include -#include - -#include "test.hpp" -#include "libtorrent/assert.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/create_torrent.hpp" -#include "libtorrent/socket_io.hpp" // print_endpoint -#include "libtorrent/socket_type.hpp" -#include "libtorrent/instantiate_connection.hpp" - -#ifdef TORRENT_USE_OPENSSL -#include -#include -#endif - -using namespace libtorrent; - -bool tests_failure = false; - -void report_failure(char const* err, char const* file, int line) -{ -#ifdef TORRENT_WINDOWS - HANDLE console = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, 0, CONSOLE_TEXTMODE_BUFFER, 0); - SetConsoleTextAttribute(console, FOREGROUND_RED); - fprintf(stderr, "\n**** %s:%d \"%s\" ****\n\n", file, line, err); - CloseHandle(console); -#else - fprintf(stderr, "\033[31m %s:%d \"%s\"\033[0m\n", file, line, err); -#endif - tests_failure = true; -} - -bool print_alerts(libtorrent::session& ses, char const* name - , bool allow_disconnects, bool allow_no_torrents, bool allow_failed_fastresume - , bool (*predicate)(libtorrent::alert*)) -{ - bool ret = false; - std::vector handles = ses.get_torrents(); - TEST_CHECK(!handles.empty() || allow_no_torrents); - torrent_handle h; - if (!handles.empty()) h = handles[0]; - std::auto_ptr a; - a = ses.pop_alert(); - while (a.get()) - { - if (predicate && predicate(a.get())) ret = true; - if (peer_disconnected_alert* p = alert_cast(a.get())) - { - fprintf(stderr, "%s(%s): %s\n", name, print_endpoint(p->ip).c_str(), p->message().c_str()); - } - else if (a->message() != "block downloading" - && a->message() != "block finished" - && a->message() != "piece finished") - { - fprintf(stderr, "%s: %s\n", name, a->message().c_str()); - } - TEST_CHECK(alert_cast(a.get()) == 0 || allow_failed_fastresume); - - peer_error_alert* pea = alert_cast(a.get()); - TEST_CHECK(pea == 0 - || (!handles.empty() && h.status().is_seeding) - || pea->error.message() == "connecting to peer" - || pea->error.message() == "closing connection to ourself" - || pea->error.message() == "duplicate connection" - || pea->error.message() == "duplicate peer-id" - || pea->error.message() == "upload to upload connection" - || pea->error.message() == "stopping torrent" - || (allow_disconnects && pea->error.message() == "Broken pipe") - || (allow_disconnects && pea->error.message() == "Connection reset by peer") - || (allow_disconnects && pea->error.message() == "End of file.")); - a = ses.pop_alert(); - } - return ret; -} - -void test_sleep(int millisec) -{ - libtorrent::sleep(millisec); -} - -void stop_proxy(int port) -{ - char buf[100]; - snprintf(buf, sizeof(buf), "delegated -P%d -Fkill", port); - system(buf); -} - -void start_proxy(int port, int proxy_type) -{ - using namespace libtorrent; - - stop_proxy(port); - - char const* type = ""; - char const* auth = ""; - - switch (proxy_type) - { - case proxy_settings::socks4: - type = "socks4"; - break; - case proxy_settings::socks5: - type = "socks5"; - break; - case proxy_settings::socks5_pw: - type = "socks5"; - auth = "AUTHORIZER=-list{testuser:testpass}"; - break; - case proxy_settings::http: - type = "http"; - break; - case proxy_settings::http_pw: - type = "http"; - auth = "AUTHORIZER=-list{testuser:testpass}"; - break; - } - - char buf[512]; - // we need to echo n since dg will ask us to configure it - snprintf(buf, sizeof(buf), "echo n | delegated -P%d ADMIN=test@test.com " - "PERMIT=\"*:*:localhost\" REMITTABLE=+,https RELAY=proxy,delegate " - "SERVER=%s %s" - , port, type, auth); - - fprintf(stderr, "starting delegated proxy...\n"); - system(buf); - fprintf(stderr, "launched\n"); - // apparently delegate takes a while to open its listen port - test_sleep(1000); -} - -using namespace libtorrent; - -template -boost::intrusive_ptr clone_ptr(boost::intrusive_ptr const& ptr) -{ - return boost::intrusive_ptr(new T(*ptr)); -} - -boost::intrusive_ptr create_torrent(std::ostream* file, int piece_size - , int num_pieces, bool add_tracker) -{ - char const* tracker_url = "http://non-existent-name.com/announce"; - // excercise the path when encountering invalid urls - char const* invalid_tracker_url = "http:"; - char const* invalid_tracker_protocol = "foo://non/existent-name.com/announce"; - - file_storage fs; - int total_size = piece_size * num_pieces; - fs.add_file("temporary", total_size); - libtorrent::create_torrent t(fs, piece_size); - if (add_tracker) - { - t.add_tracker(tracker_url); - t.add_tracker(invalid_tracker_url); - t.add_tracker(invalid_tracker_protocol); - } - - std::vector piece(piece_size); - for (int i = 0; i < int(piece.size()); ++i) - piece[i] = (i % 26) + 'A'; - - // calculate the hash for all pieces - int num = t.num_pieces(); - sha1_hash ph = hasher(&piece[0], piece.size()).final(); - for (int i = 0; i < num; ++i) - t.set_hash(i, ph); - - if (file) - { - while (total_size > 0) - { - file->write(&piece[0], (std::min)(int(piece.size()), total_size)); - total_size -= piece.size(); - } - } - - std::vector tmp; - std::back_insert_iterator > out(tmp); - - bencode(out, t.generate()); - error_code ec; - return boost::intrusive_ptr(new torrent_info( - &tmp[0], tmp.size(), ec)); -} - -boost::tuple -setup_transfer(session* ses1, session* ses2, session* ses3 - , bool clear_files, bool use_metadata_transfer, bool connect_peers - , std::string suffix, int piece_size - , boost::intrusive_ptr* torrent, bool super_seeding - , add_torrent_params const* p) -{ - assert(ses1); - assert(ses2); - - session_settings sess_set = ses1->settings(); - sess_set.allow_multiple_connections_per_ip = true; - sess_set.ignore_limits_on_local_network = false; - ses1->set_settings(sess_set); - ses2->set_settings(sess_set); - if (ses3) ses3->set_settings(sess_set); - ses1->set_alert_mask(~(alert::progress_notification | alert::stats_notification)); - ses2->set_alert_mask(~(alert::progress_notification | alert::stats_notification)); - if (ses3) ses3->set_alert_mask(~(alert::progress_notification | alert::stats_notification)); - - std::srand(time(0)); - peer_id pid; - std::generate(&pid[0], &pid[0] + 20, std::rand); - ses1->set_peer_id(pid); - std::generate(&pid[0], &pid[0] + 20, std::rand); - ses2->set_peer_id(pid); - assert(ses1->id() != ses2->id()); - if (ses3) - { - std::generate(&pid[0], &pid[0] + 20, std::rand); - ses3->set_peer_id(pid); - assert(ses3->id() != ses2->id()); - } - - boost::intrusive_ptr t; - if (torrent == 0) - { - error_code ec; - create_directory("./tmp1" + suffix, ec); - std::ofstream file(("./tmp1" + suffix + "/temporary").c_str()); - t = ::create_torrent(&file, piece_size, 19, true); - file.close(); - if (clear_files) - { - remove_all("./tmp2" + suffix + "/temporary", ec); - remove_all("./tmp3" + suffix + "/temporary", ec); - } - char ih_hex[41]; - to_hex((char const*)&t->info_hash()[0], 20, ih_hex); - fprintf(stderr, "generated torrent: %s ./tmp1%s/temporary\n", ih_hex, suffix.c_str()); - } - else - { - t = *torrent; - } - - // they should not use the same save dir, because the - // file pool will complain if two torrents are trying to - // use the same files - sha1_hash info_hash = t->info_hash(); - add_torrent_params param; - if (p) param = *p; - param.ti = clone_ptr(t); - param.save_path = "./tmp1" + suffix; - param.seed_mode = true; - error_code ec; - torrent_handle tor1 = ses1->add_torrent(param, ec); - tor1.super_seeding(super_seeding); - param.seed_mode = false; - TEST_CHECK(!ses1->get_torrents().empty()); - torrent_handle tor2; - torrent_handle tor3; - - // the downloader cannot use seed_mode - param.seed_mode = false; - - if (ses3) - { - param.ti = clone_ptr(t); - param.save_path = "./tmp3" + suffix; - tor3 = ses3->add_torrent(param, ec); - TEST_CHECK(!ses3->get_torrents().empty()); - } - - if (use_metadata_transfer) - { - param.ti = 0; - param.info_hash = t->info_hash(); - } - else - { - param.ti = clone_ptr(t); - } - param.save_path = "./tmp2" + suffix; - - tor2 = ses2->add_torrent(param, ec); - TEST_CHECK(!ses2->get_torrents().empty()); - - assert(ses1->get_torrents().size() == 1); - assert(ses2->get_torrents().size() == 1); - - test_sleep(100); - - if (connect_peers) - { - fprintf(stderr, "connecting peer\n"); - error_code ec; - tor1.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec) - , ses2->listen_port())); - - if (ses3) - { - // give the other peers some time to get an initial - // set of pieces before they start sharing with each-other - tor3.connect_peer(tcp::endpoint( - address::from_string("127.0.0.1", ec) - , ses2->listen_port())); - tor3.connect_peer(tcp::endpoint( - address::from_string("127.0.0.1", ec) - , ses1->listen_port())); - } - } - - return boost::make_tuple(tor1, tor2, tor3); -} - -boost::asio::io_service* tracker_ios = 0; -boost::shared_ptr tracker_server; -libtorrent::mutex tracker_lock; -libtorrent::event tracker_initialized; - -bool udp_failed = false; - -void stop_tracker() -{ - if (tracker_server && tracker_ios) - { - tracker_ios->stop(); - tracker_server->join(); - tracker_server.reset(); - tracker_ios = 0; - } -} - -void udp_tracker_thread(int* port); - -int start_tracker() -{ - stop_tracker(); - - { - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.clear(l); - } - - int port = 0; - - tracker_server.reset(new libtorrent::thread(boost::bind(&udp_tracker_thread, &port))); - - { - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.wait(l); - } - test_sleep(100); - return port; -} - -void on_udp_receive(error_code const& ec, size_t bytes_transferred, udp::endpoint const* from, char* buffer, udp::socket* sock) -{ - if (ec) - { - fprintf(stderr, "UDP tracker, read failed: %s\n", ec.message().c_str()); - return; - } - - udp_failed = false; - - if (bytes_transferred < 16) - { - fprintf(stderr, "UDP message too short\n"); - return; - } - char* ptr = buffer; - detail::read_uint64(ptr); - boost::uint32_t action = detail::read_uint32(ptr); - boost::uint32_t transaction_id = detail::read_uint32(ptr); - - error_code e; - - switch (action) - { - case 0: // connect - - ptr = buffer; - detail::write_uint32(0, ptr); // action = connect - detail::write_uint32(transaction_id, ptr); // transaction_id - detail::write_uint64(10, ptr); // connection_id - sock->send_to(asio::buffer(buffer, 16), *from, 0, e); - break; - - case 1: // announce - - ptr = buffer; - detail::write_uint32(1, ptr); // action = announce - detail::write_uint32(transaction_id, ptr); // transaction_id - detail::write_uint32(1800, ptr); // interval - detail::write_uint32(1, ptr); // incomplete - detail::write_uint32(1, ptr); // complete - // 0 peers - sock->send_to(asio::buffer(buffer, 20), *from, 0, e); - break; - default: // ignore scrapes - break; - } -} - -void udp_tracker_thread(int* port) -{ - io_service ios; - udp::socket acceptor(ios); - error_code ec; - acceptor.open(udp::v4(), ec); - if (ec) - { - fprintf(stderr, "Error opening listen UDP socket: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.signal(l); - return; - } - acceptor.bind(udp::endpoint(address_v4::any(), 0), ec); - if (ec) - { - fprintf(stderr, "Error binding UDP socket to port 0: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.signal(l); - return; - } - *port = acceptor.local_endpoint().port(); - - tracker_ios = &ios; - - fprintf(stderr, "UDP tracker initialized on port %d\n", *port); - - { - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.signal(l); - } - - char buffer[2000]; - - for (;;) - { - error_code ec; - udp::endpoint from; - udp_failed = true; - acceptor.async_receive_from( - asio::buffer(buffer, sizeof(buffer)), from, boost::bind( - &on_udp_receive, _1, _2, &from, &buffer[0], &acceptor)); - ios.run_one(ec); - if (udp_failed) return; - - if (ec) - { - fprintf(stderr, "Error receiving on UDP socket: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(tracker_lock); - tracker_initialized.signal(l); - return; - } - ios.reset(); - } -} - -boost::asio::io_service* web_ios = 0; -boost::shared_ptr web_server; -libtorrent::mutex web_lock; -libtorrent::event web_initialized; - -void stop_web_server() -{ - if (web_server && web_ios) - { - web_ios->post(boost::bind(&io_service::stop, web_ios)); - web_server->join(); - web_server.reset(); - web_ios = 0; - } -} - -void web_server_thread(int* port, bool ssl, bool chunked); - -int start_web_server(bool ssl, bool chunked_encoding) -{ - stop_web_server(); - - { - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.clear(l); - } - - if (ssl) - { - system("echo . > tmp"); - system("echo test province >>tmp"); - system("echo test city >> tmp"); - system("echo test company >> tmp"); - system("echo test department >> tmp"); - system("echo tester >> tmp"); - system("echo test@test.com >> tmp"); - system("openssl req -new -x509 -keyout server.pem -out server.pem " - "-days 365 -nodes > %s\n", msg); - write(s, boost::asio::buffer(msg, pkt_len), boost::asio::transfer_all(), ec); -} - -bool accept_done = false; - -void on_accept(error_code const& ec) -{ - if (ec) - { - fprintf(stderr, "Error accepting socket: %s\n", ec.message().c_str()); - accept_done = false; - } - else - { -// fprintf(stderr, "accepting connection\n"); - accept_done = true; - } -} - -void send_content(socket_type& s, char const* file, int size, bool chunked) -{ - error_code ec; - if (chunked) - { - int chunk_size = 13; - char head[20]; - std::vector bufs(3); - bufs[2] = asio::const_buffer("\r\n", 2); - while (chunk_size > 0) - { - chunk_size = std::min(chunk_size, size); - int len = snprintf(head, sizeof(head), "%x\r\n", chunk_size); - bufs[0] = asio::const_buffer(head, len); - if (chunk_size == 0) - { - // terminate - bufs.erase(bufs.begin()+1); - } - else - { - bufs[1] = asio::const_buffer(file, chunk_size); - } - write(s, bufs, boost::asio::transfer_all(), ec); - size -= chunk_size; - file += chunk_size; - chunk_size *= 2; - } - } - else - { - write(s, boost::asio::buffer(file, size), boost::asio::transfer_all(), ec); - } -} - -void web_server_thread(int* port, bool ssl, bool chunked) -{ - io_service ios; - socket_acceptor acceptor(ios); - error_code ec; - acceptor.open(tcp::v4(), ec); - if (ec) - { - fprintf(stderr, "Error opening listen socket: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.signal(l); - return; - } - acceptor.set_option(socket_acceptor::reuse_address(true), ec); - if (ec) - { - fprintf(stderr, "Error setting listen socket to reuse addr: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.signal(l); - return; - } - acceptor.bind(tcp::endpoint(address_v4::any(), 0), ec); - if (ec) - { - fprintf(stderr, "Error binding listen socket to port 0: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.signal(l); - return; - } - *port = acceptor.local_endpoint().port(); - acceptor.listen(10, ec); - if (ec) - { - fprintf(stderr, "Error listening on socket: %s\n", ec.message().c_str()); - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.signal(l); - return; - } - - web_ios = &ios; - - fprintf(stderr, "web server initialized on port %d\n", *port); - - { - libtorrent::mutex::scoped_lock l(web_lock); - web_initialized.signal(l); - } - - char buf[10000]; - int len = 0; - int offset = 0; - bool connection_close = false; - socket_type s(ios); - void* ctx = 0; -#ifdef TORRENT_USE_OPENSSL - boost::asio::ssl::context ssl_ctx(ios, boost::asio::ssl::context::sslv2_server); - ssl_ctx.use_certificate_chain_file("server.pem"); - ssl_ctx.use_private_key_file("server.pem", asio::ssl::context::pem); - ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none); - - if (ssl) ctx = &ssl_ctx; -#endif - - proxy_settings p; - instantiate_connection(ios, p, s, ctx); - for (;;) - { - if (connection_close) - { -// fprintf(stderr, "closing connection\n"); - s.close(ec); - connection_close = false; - } - - if (!s.is_open()) - { - len = 0; - offset = 0; - - error_code ec; - stream_socket* sock; -#ifdef TORRENT_USE_OPENSSL - if (ssl) sock = &s.get >()->next_layer().next_layer(); - else -#endif - sock = s.get(); - - accept_done = false; - acceptor.async_accept(*sock, &on_accept); - ios.reset(); - ios.run_one(); - if (!accept_done) - { - fprintf(stderr, "accept failed: %s\n", ec.message().c_str()); - return; - } -// fprintf(stderr, "accepting incoming connection\n"); - if (!s.is_open()) continue; - -#ifdef TORRENT_USE_OPENSSL - if (ssl) - s.get >()->next_layer().handshake(asio::ssl::stream_base::server); -#endif - - } - - http_parser p; - bool failed = false; - - do - { - p.reset(); - bool error = false; - - p.incoming(buffer::const_interval(buf + offset, buf + len), error); - - char const* extra_header[4] = {"","","",""}; - - TEST_CHECK(error == false); - if (error) - { - fprintf(stderr, "parse failed\n"); - failed = true; - break; - } - - while (!p.finished()) - { - TORRENT_ASSERT(len < int(sizeof(buf))); - size_t received = s.read_some(boost::asio::buffer(&buf[len] - , sizeof(buf) - len), ec); -// fprintf(stderr, "read: %d\n", int(received)); - - if (ec || received <= 0) - { - fprintf(stderr, "read failed: %s received: %d\n", ec.message().c_str(), int(received)); - failed = true; - break; - } - len += received; - - p.incoming(buffer::const_interval(buf + offset, buf + len), error); - - TEST_CHECK(error == false); - if (error) - { - fprintf(stderr, "parse failed\n"); - failed = true; - break; - } - } - - std::string connection = p.header("connection"); - std::string via = p.header("via"); - - // The delegate proxy doesn't say connection close, but it expects it to be closed - // the Via: header is an indicator of delegate making the request - if (connection == "close" || !via.empty()) - { -// fprintf(stderr, "got connection close\n"); - connection_close = true; - } - -// fprintf(stderr, "%s", std::string(buf + offset, p.body_start()).c_str()); - - if (failed) - { - fprintf(stderr, "connection failed\n"); - connection_close = true; - break; - } - - offset += p.body_start() + p.content_length(); -// fprintf(stderr, "offset: %d len: %d\n", offset, len); - - if (p.method() != "get" && p.method() != "post") - { - fprintf(stderr, "incorrect method: %s\n", p.method().c_str()); - break; - } - - std::string path = p.path(); - - if (path == "/redirect") - { - extra_header[0] = "Location: /test_file\r\n"; - send_response(s, ec, 301, "Moved Permanently", extra_header, 0); - break; - } - - if (path == "/infinite_redirect") - { - extra_header[0] = "Location: /infinite_redirect\r\n"; - send_response(s, ec, 301, "Moved Permanently", extra_header, 0); - break; - } - - if (path == "/relative/redirect") - { - extra_header[0] = "Location: ../test_file\r\n"; - send_response(s, ec, 301, "Moved Permanently", extra_header, 0); - break; - } - - if (path.substr(0, 9) == "/announce") - { - entry announce; - announce["interval"] = 1800; - announce["complete"] = 1; - announce["incomplete"] = 1; - announce["peers"].string(); - std::vector buf; - bencode(std::back_inserter(buf), announce); - - send_response(s, ec, 200, "OK", extra_header, buf.size()); - write(s, boost::asio::buffer(&buf[0], buf.size()), boost::asio::transfer_all(), ec); - } - - if (path.substr(0, 6) == "/seed?") - { - char const* piece = strstr(path.c_str(), "&piece="); - if (piece == 0) - { - fprintf(stderr, "invalid web seed request: %s\n", path.c_str()); - break; - } - boost::uint64_t idx = atoi(piece + 7); - char const* range = strstr(path.c_str(), "&ranges="); - int range_end = 0; - int range_start = 0; - if (range) - { - range_start = atoi(range + 8); - range = strchr(range, '-'); - if (range == 0) - { - fprintf(stderr, "invalid web seed request: %s\n", path.c_str()); - break; - } - range_end = atoi(range + 1); - } - else - { - range_start = 0; - // assume piece size of 64kiB - range_end = 64*1024-1; - } - - int size = range_end - range_start + 1; - boost::uint64_t off = idx * 64 * 1024 + range_start; - std::vector file_buf; - int res = load_file("./tmp1_web_seed/seed", file_buf); - - error_code ec; - if (res == -1 || file_buf.empty()) - { - send_response(s, ec, 404, "Not Found", extra_header, 0); - continue; - } - send_response(s, ec, 200, "OK", extra_header, size); -// fprintf(stderr, "sending %d bytes of payload [%d, %d)\n" -// , size, int(off), int(off + size)); - write(s, boost::asio::buffer(&file_buf[0] + off, size) - , boost::asio::transfer_all(), ec); - - memmove(buf, buf + offset, len - offset); - len -= offset; - offset = 0; - continue; - } - -// fprintf(stderr, ">> serving file %s\n", path.c_str()); - std::vector file_buf; - // remove the / from the path - path = path.substr(1); - int res = load_file(path, file_buf); - if (res == -1) - { - send_response(s, ec, 404, "Not Found", extra_header, 0); - continue; - } - - if (res != 0) - { - // this means the file was either too big or couldn't be read - send_response(s, ec, 503, "Internal Error", extra_header, 0); - continue; - } - - // serve file - - if (extension(path) == ".gz") - { - extra_header[0] = "Content-Encoding: gzip\r\n"; - } - - if (chunked) - { - extra_header[2] = "Transfer-Encoding: chunked\r\n"; - } - - if (!p.header("range").empty()) - { - std::string range = p.header("range"); - int start, end; - sscanf(range.c_str(), "bytes=%d-%d", &start, &end); - char eh[400]; - snprintf(eh, sizeof(eh), "Content-Range: bytes %d-%d\r\n", start, end); - extra_header[1] = eh; - send_response(s, ec, 206, "Partial", extra_header, end - start + 1); - if (!file_buf.empty()) - { - send_content(s, &file_buf[0] + start, end - start + 1, chunked); - } -// fprintf(stderr, "send %d bytes of payload\n", end - start + 1); - } - else - { - send_response(s, ec, 200, "OK", extra_header, file_buf.size()); - if (!file_buf.empty()) - send_content(s, &file_buf[0], file_buf.size(), chunked); - } -// fprintf(stderr, "%d bytes left in receive buffer. offset: %d\n", len - offset, offset); - memmove(buf, buf + offset, len - offset); - len -= offset; - offset = 0; - } while (offset < len); - } - fprintf(stderr, "exiting web server thread\n"); -} - - diff --git a/libtorrent_utp/test/setup_transfer.hpp b/libtorrent_utp/test/setup_transfer.hpp deleted file mode 100644 index 17153710b..000000000 --- a/libtorrent_utp/test/setup_transfer.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - -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 SETUP_TRANSFER_HPP -#define SETUP_TRANSFER_HPP - -#include "libtorrent/session.hpp" -#include - -namespace libtorrent -{ - class alert; - struct add_torrent_params; -} - -bool print_alerts(libtorrent::session& ses, char const* name - , bool allow_disconnects = false - , bool allow_no_torrents = false - , bool allow_failed_fastresume = false - , bool (*)(libtorrent::alert*) = 0); - -void test_sleep(int millisec); - -boost::intrusive_ptr create_torrent(std::ostream* file = 0 - , int piece_size = 16 * 1024, int num_pieces = 13, bool add_tracker = true); - -boost::tuple -setup_transfer(libtorrent::session* ses1, libtorrent::session* ses2 - , libtorrent::session* ses3, bool clear_files, bool use_metadata_transfer = true - , bool connect = true, std::string suffix = "", int piece_size = 16 * 1024 - , boost::intrusive_ptr* torrent = 0, bool super_seeding = false - , libtorrent::add_torrent_params const* p = 0); - -int start_web_server(bool ssl = false, bool chunked = false); -void stop_web_server(); -void start_proxy(int port, int type); -void stop_proxy(int port); - -void stop_tracker(); -int start_tracker(); - -#endif - diff --git a/libtorrent_utp/test/test.hpp b/libtorrent_utp/test/test.hpp deleted file mode 100644 index bd57438b0..000000000 --- a/libtorrent_utp/test/test.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - -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 TEST_HPP -#define TEST_HPP - -void report_failure(char const* str, char const* file, int line); - -#include -#include -#include - -#if defined(_MSC_VER) -#define COUNTER_GUARD(x) -#else -#define COUNTER_GUARD(type) \ - struct BOOST_PP_CAT(type, _counter_guard) \ - { \ - ~BOOST_PP_CAT(type, _counter_guard()) \ - { \ - TEST_CHECK(counted_type::count == 0); \ - } \ - } BOOST_PP_CAT(type, _guard) -#endif - -#define TEST_REPORT_AUX(x, line, file) \ - report_failure(x, line, file) - -#ifdef BOOST_NO_EXCEPTIONS -#define TEST_CHECK(x) \ - if (!(x)) \ - TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); -#define TEST_EQUAL(x, y) \ - if (x != y) { \ - std::stringstream s__; \ - s__ << "TEST_EQUAL_ERROR: " << #x << ": " << x << " expected: " << y << std::endl; \ - TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ - } -#else -#define TEST_CHECK(x) \ - try \ - { \ - if (!(x)) \ - TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); \ - } \ - catch (std::exception& e) \ - { \ - TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ - } \ - catch (...) \ - { \ - TEST_ERROR("Exception thrown: " #x); \ - } - -#define TEST_EQUAL(x, y) \ - try { \ - if (x != y) { \ - std::stringstream s__; \ - s__ << "TEST_EQUAL_ERROR: " << #x << ": " << x << " expected: " << y << std::endl; \ - TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ - } \ - } \ - catch (std::exception& e) \ - { \ - TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ - } \ - catch (...) \ - { \ - TEST_ERROR("Exception thrown: " #x); \ - } -#endif - -#define TEST_ERROR(x) \ - TEST_REPORT_AUX((std::string("ERROR: \"") + x + "\"").c_str(), __FILE__, __LINE__) - -#define TEST_NOTHROW(x) \ - try \ - { \ - x; \ - } \ - catch (...) \ - { \ - TEST_ERROR("Exception thrown: " #x); \ - } - -#endif // TEST_HPP - diff --git a/libtorrent_utp/test/test_auto_unchoke.cpp b/libtorrent_utp/test/test_auto_unchoke.cpp deleted file mode 100644 index 1b4cf3845..000000000 --- a/libtorrent_utp/test/test_auto_unchoke.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/thread.hpp" -#include -#include - -#include "test.hpp" -#include "setup_transfer.hpp" - -void test_swarm() -{ - using namespace libtorrent; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48010, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49010, 50000), "0.0.0.0", 0); - session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50010, 51000), "0.0.0.0", 0); - - ses1.set_alert_mask(alert::all_categories); - ses2.set_alert_mask(alert::all_categories); - ses3.set_alert_mask(alert::all_categories); - - // this is to avoid everything finish from a single peer - // immediately. To make the swarm actually connect all - // three peers before finishing. - float rate_limit = 100000; - - session_settings settings; - settings.allow_multiple_connections_per_ip = true; - settings.ignore_limits_on_local_network = false; - settings.choking_algorithm = session_settings::auto_expand_choker; - settings.upload_rate_limit = rate_limit; - settings.unchoke_slots_limit = 1; - ses1.set_settings(settings); - - settings.upload_rate_limit = rate_limit / 10; - settings.download_rate_limit = rate_limit / 5; - settings.unchoke_slots_limit = 0; - ses2.set_settings(settings); - ses3.set_settings(settings); - -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::forced; - pes.in_enc_policy = pe_settings::forced; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); - ses3.set_pe_settings(pes); -#endif - - torrent_handle tor1; - torrent_handle tor2; - torrent_handle tor3; - - boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_unchoke"); - - session_status st = ses1.status(); - TEST_CHECK(st.allowed_upload_slots == 1); - for (int i = 0; i < 50; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - print_alerts(ses3, "ses3"); - - st = ses1.status(); - std::cerr << st.allowed_upload_slots << " "; - if (st.allowed_upload_slots >= 2) break; - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - torrent_status st3 = tor3.status(); - - std::cerr - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << st1.num_peers << " " << st.allowed_upload_slots << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers << " - " - << "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st3.progress * 100) << "% " - << st3.num_peers - << std::endl; - - test_sleep(1000); - } - - TEST_CHECK(st.allowed_upload_slots >= 2); - - // make sure the files are deleted - ses1.remove_torrent(tor1, session::delete_files); - ses2.remove_torrent(tor2, session::delete_files); - ses3.remove_torrent(tor3, session::delete_files); -} - -int test_main() -{ - using namespace libtorrent; - - // in case the previous run was t r catch (std::exception&) {}erminated - error_code ec; - remove_all("./tmp1_unchoke", ec); - remove_all("./tmp2_unchoke", ec); - remove_all("./tmp3_unchoke", ec); - - test_swarm(); - - test_sleep(2000); - TEST_CHECK(!exists("./tmp1_unchoke/temporary")); - TEST_CHECK(!exists("./tmp2_unchoke/temporary")); - TEST_CHECK(!exists("./tmp3_unchoke/temporary")); - - remove_all("./tmp1_unchoke", ec); - remove_all("./tmp2_unchoke", ec); - remove_all("./tmp3_unchoke", ec); - - return 0; -} - diff --git a/libtorrent_utp/test/test_bandwidth_limiter.cpp b/libtorrent_utp/test/test_bandwidth_limiter.cpp deleted file mode 100644 index bc9846d42..000000000 --- a/libtorrent_utp/test/test_bandwidth_limiter.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - -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. - -*/ - -#include "test.hpp" - -#include "libtorrent/bandwidth_manager.hpp" -#include "libtorrent/bandwidth_queue_entry.hpp" -#include "libtorrent/bandwidth_limit.hpp" -#include "libtorrent/bandwidth_socket.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/stat.hpp" -#include "libtorrent/time.hpp" - -#include -#include -#include -#include - -struct torrent; -struct peer_connection; - -using namespace libtorrent; - -const float sample_time = 20.f; // seconds - -//#define VERBOSE_LOGGING - -bandwidth_channel global_bwc; - -struct peer_connection: bandwidth_socket -{ - peer_connection(bandwidth_manager& bwm - , bandwidth_channel& torrent_bwc, int prio, bool ignore_limits, std::string name) - : m_bwm(bwm) - , m_torrent_bandwidth_channel(torrent_bwc) - , m_priority(prio) - , m_ignore_limits(ignore_limits) - , m_name(name) - , m_quota(0) - {} - - bool is_disconnecting() const { return false; } - bool ignore_bandwidth_limits() { return m_ignore_limits; } - void assign_bandwidth(int channel, int amount); - - void throttle(int limit) { m_bandwidth_channel.throttle(limit); } - - void start(); - - bandwidth_manager& m_bwm; - - bandwidth_channel m_bandwidth_channel; - bandwidth_channel& m_torrent_bandwidth_channel; - - int m_priority; - bool m_ignore_limits; - std::string m_name; - int m_quota; -}; - -void peer_connection::assign_bandwidth(int channel, int amount) -{ - m_quota += amount; -#ifdef VERBOSE_LOGGING - std::cerr << " [" << m_name - << "] assign bandwidth, " << amount << std::endl; -#endif - TEST_CHECK(amount > 0); - start(); -} - -void peer_connection::start() -{ - m_bwm.request_bandwidth(self(), 150000, m_priority - , &m_bandwidth_channel - , &m_torrent_bandwidth_channel - , &global_bwc); -} - - -typedef std::vector > connections_t; - -void do_change_rate(bandwidth_channel& t1, bandwidth_channel& t2, int limit) -{ - static int counter = 10; - --counter; - if (counter == 0) - { - t1.throttle(limit); - t2.throttle(limit); - return; - } - - t1.throttle(limit + limit / 2 * ((counter & 1)?-1:1)); - t2.throttle(limit + limit / 2 * ((counter & 1)?1:-1)); -} - -void do_change_peer_rate(connections_t& v, int limit) -{ - static int count = 10; - --count; - if (count == 0) - { - std::for_each(v.begin(), v.end() - , boost::bind(&peer_connection::throttle, _1, limit)); - return; - } - - int c = count; - for (connections_t::iterator i = v.begin(); i != v.end(); ++i, ++c) - i->get()->throttle(limit + limit / 2 * ((c & 1)?-1:1)); -} - -void nop() {} - -void run_test(connections_t& v - , bandwidth_manager& manager - , boost::function f = &nop) -{ - std::cerr << "-------------" << std::endl; - - std::for_each(v.begin(), v.end() - , boost::bind(&peer_connection::start, _1)); - - for (int i = 0; i < int(sample_time * 10); ++i) - { - manager.update_quotas(milliseconds(100)); - if ((i % 15) == 0) f(); - } -} - -bool close_to(float val, float comp, float err) -{ - return fabs(val - comp) <= err; -} - -void spawn_connections(connections_t& v, bandwidth_manager& bwm - , bandwidth_channel& bwc, int num, char const* prefix) -{ - for (int i = 0; i < num; ++i) - { - char name[200]; - snprintf(name, sizeof(name), "%s%d", prefix, i); - v.push_back(new peer_connection(bwm, bwc, 200, false, name)); - } -} - -void test_equal_connections(int num, int limit) -{ - std::cerr << "\ntest equal connections " << num << " " << limit << std::endl; - bandwidth_manager manager(0); - global_bwc.throttle(limit); - - bandwidth_channel t1; - - connections_t v; - spawn_connections(v, manager, t1, num, "p"); - run_test(v, manager); - - float sum = 0.f; - float err = (std::max)(limit / num * 0.3f, 1000.f); - for (connections_t::iterator i = v.begin() - , end(v.end()); i != end; ++i) - { - sum += (*i)->m_quota; - - std::cerr << (*i)->m_quota / sample_time - << " target: " << (limit / num) << " eps: " << err << std::endl; - TEST_CHECK(close_to((*i)->m_quota / sample_time, limit / num, err)); - } - sum /= sample_time; - std::cerr << "sum: " << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 50)); -} - -void test_connections_variable_rate(int num, int limit, int torrent_limit) -{ - std::cerr << "\ntest connections variable rate" << num - << " l: " << limit - << " t: " << torrent_limit - << std::endl; - bandwidth_manager manager(0); - global_bwc.throttle(0); - - bandwidth_channel t1; - if (torrent_limit) - t1.throttle(torrent_limit); - - connections_t v; - spawn_connections(v, manager, t1, num, "p"); - std::for_each(v.begin(), v.end() - , boost::bind(&peer_connection::throttle, _1, limit)); - - run_test(v, manager, boost::bind(&do_change_peer_rate - , boost::ref(v), limit)); - - if (torrent_limit > 0 && limit * num > torrent_limit) - limit = torrent_limit / num; - - float sum = 0.f; - float err = limit * 0.3f; - for (connections_t::iterator i = v.begin() - , end(v.end()); i != end; ++i) - { - sum += (*i)->m_quota; - - std::cerr << (*i)->m_quota / sample_time - << " target: " << limit << " eps: " << err << std::endl; - TEST_CHECK(close_to((*i)->m_quota / sample_time, limit, err)); - } - sum /= sample_time; - std::cerr << "sum: " << sum << " target: " << (limit * num) << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit * num, limit * 0.3f * num)); -} - -void test_single_peer(int limit, bool torrent_limit) -{ - std::cerr << "\ntest single peer " << limit << " " << torrent_limit << std::endl; - bandwidth_manager manager(0); - bandwidth_channel t1; - global_bwc.throttle(0); - - if (torrent_limit) - t1.throttle(limit); - else - global_bwc.throttle(limit); - - connections_t v; - spawn_connections(v, manager, t1, 1, "p"); - run_test(v, manager); - - float sum = 0.f; - for (connections_t::iterator i = v.begin() - , end(v.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 1000)); -} - -void test_torrents(int num, int limit1, int limit2, int global_limit) -{ - std::cerr << "\ntest equal torrents " << num - << " l1: " << limit1 - << " l2: " << limit2 - << " g: " << global_limit << std::endl; - bandwidth_manager manager(0); - global_bwc.throttle(global_limit); - - bandwidth_channel t1; - bandwidth_channel t2; - - t1.throttle(limit1); - t2.throttle(limit2); - - connections_t v1; - spawn_connections(v1, manager, t1, num, "t1p"); - connections_t v2; - spawn_connections(v2, manager, t2, num, "t2p"); - connections_t v; - std::copy(v1.begin(), v1.end(), std::back_inserter(v)); - std::copy(v2.begin(), v2.end(), std::back_inserter(v)); - run_test(v, manager); - - if (global_limit > 0 && global_limit < limit1 + limit2) - { - limit1 = (std::min)(limit1, global_limit / 2); - limit2 = global_limit - limit1; - } - float sum = 0.f; - for (connections_t::iterator i = v1.begin() - , end(v1.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit1 << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit1, 1000)); - - sum = 0.f; - for (connections_t::iterator i = v2.begin() - , end(v2.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit2 << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit2, 1000)); -} - -void test_torrents_variable_rate(int num, int limit, int global_limit) -{ - std::cerr << "\ntest torrents variable rate" << num - << " l: " << limit - << " g: " << global_limit << std::endl; - bandwidth_manager manager(0); - global_bwc.throttle(global_limit); - - bandwidth_channel t1; - bandwidth_channel t2; - - t1.throttle(limit); - t2.throttle(limit); - - connections_t v1; - spawn_connections(v1, manager, t1, num, "t1p"); - connections_t v2; - spawn_connections(v2, manager, t2, num, "t2p"); - connections_t v; - std::copy(v1.begin(), v1.end(), std::back_inserter(v)); - std::copy(v2.begin(), v2.end(), std::back_inserter(v)); - - run_test(v, manager, boost::bind(&do_change_rate, boost::ref(t1), boost::ref(t2), limit)); - - if (global_limit > 0 && global_limit < 2 * limit) - limit = global_limit / 2; - - float sum = 0.f; - for (connections_t::iterator i = v1.begin() - , end(v1.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 1000)); - - sum = 0.f; - for (connections_t::iterator i = v2.begin() - , end(v2.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 1000)); -} - -void test_peer_priority(int limit, bool torrent_limit) -{ - std::cerr << "\ntest peer priority " << limit << " " << torrent_limit << std::endl; - bandwidth_manager manager(0); - bandwidth_channel t1; - global_bwc.throttle(0); - - if (torrent_limit) - t1.throttle(limit); - else - global_bwc.throttle(limit); - - connections_t v1; - spawn_connections(v1, manager, t1, 10, "p"); - connections_t v; - std::copy(v1.begin(), v1.end(), std::back_inserter(v)); - boost::intrusive_ptr p( - new peer_connection(manager, t1, 1, false, "no-priority")); - v.push_back(p); - run_test(v, manager); - - float sum = 0.f; - for (connections_t::iterator i = v1.begin() - , end(v1.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 50)); - - std::cerr << "non-prioritized rate: " << p->m_quota / sample_time - << " target: " << (limit / 200 / 10) << std::endl; - TEST_CHECK(close_to(p->m_quota / sample_time, limit / 200 / 10, 5)); -} - -void test_no_starvation(int limit) -{ - std::cerr << "\ntest no starvation " << limit << std::endl; - bandwidth_manager manager(0); - bandwidth_channel t1; - bandwidth_channel t2; - - global_bwc.throttle(limit); - - const int num_peers = 20; - - connections_t v1; - spawn_connections(v1, manager, t1, num_peers, "p"); - connections_t v; - std::copy(v1.begin(), v1.end(), std::back_inserter(v)); - boost::intrusive_ptr p( - new peer_connection(manager, t2, 1, false, "no-priority")); - v.push_back(p); - run_test(v, manager); - - float sum = 0.f; - for (connections_t::iterator i = v.begin() - , end(v.end()); i != end; ++i) - { - sum += (*i)->m_quota; - } - sum /= sample_time; - std::cerr << sum << " target: " << limit << std::endl; - TEST_CHECK(sum > 0); - TEST_CHECK(close_to(sum, limit, 50)); - - std::cerr << "non-prioritized rate: " << p->m_quota / sample_time - << " target: " << (limit / 200 / num_peers) << std::endl; - TEST_CHECK(close_to(p->m_quota / sample_time, limit / 200 / num_peers, 5)); -} - -int test_main() -{ - using namespace libtorrent; - - test_equal_connections(2, 20); - test_equal_connections(2, 2000); - test_equal_connections(2, 20000); - test_equal_connections(3, 20000); - test_equal_connections(5, 20000); - test_equal_connections(7, 20000); - test_equal_connections(33, 60000); - test_equal_connections(33, 500000); - test_connections_variable_rate(2, 20, 0); - test_connections_variable_rate(5, 20000, 0); - test_connections_variable_rate(3, 2000, 6000); - test_connections_variable_rate(5, 2000, 30000); - test_connections_variable_rate(33, 500000, 0); - test_torrents(2, 400, 400, 0); - test_torrents(2, 100, 500, 0); - test_torrents(2, 3000, 3000, 6000); - test_torrents(1, 40000, 40000, 0); - test_torrents(24, 50000, 50000, 0); - test_torrents(5, 6000, 6000, 3000); - test_torrents(5, 6000, 5000, 4000); - test_torrents(5, 20000, 20000, 30000); - test_torrents_variable_rate(5, 6000, 3000); - test_torrents_variable_rate(5, 20000, 30000); - test_single_peer(40000, true); - test_single_peer(40000, false); - test_peer_priority(40000, false); - test_peer_priority(40000, true); - test_no_starvation(40000); - - return 0; -} - - diff --git a/libtorrent_utp/test/test_bdecode_performance.cpp b/libtorrent_utp/test/test_bdecode_performance.cpp deleted file mode 100644 index b726bdd77..000000000 --- a/libtorrent_utp/test/test_bdecode_performance.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "libtorrent/lazy_entry.hpp" -#include -#include - -#include "test.hpp" -#include "libtorrent/time.hpp" - -using namespace libtorrent; - -int test_main() -{ - using namespace libtorrent; - - ptime start(time_now()); - - for (int i = 0; i < 100000; ++i) - { - char b[] = "d1:ai12453e1:b3:aaa1:c3:bbbe"; - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); - } - ptime stop(time_now()); - - std::cout << "done in " << total_milliseconds(stop - start) / 100. << " seconds per million message" << std::endl; - return 0; -} - diff --git a/libtorrent_utp/test/test_bencoding.cpp b/libtorrent_utp/test/test_bencoding.cpp deleted file mode 100644 index d51841754..000000000 --- a/libtorrent_utp/test/test_bencoding.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/bencode.hpp" -#include "libtorrent/lazy_entry.hpp" -#include -#include -#include - -#include "test.hpp" - -using namespace libtorrent; - -// test vectors from bittorrent protocol description -// http://www.bittorrent.com/protocol.html - -std::string encode(entry const& e) -{ - std::string ret; - bencode(std::back_inserter(ret), e); - return ret; -} - -entry decode(std::string const& str) -{ - return bdecode(str.begin(), str.end()); -} - -int test_main() -{ - using namespace libtorrent; - - // ** strings ** - { - entry e("spam"); - TEST_CHECK(encode(e) == "4:spam"); - TEST_CHECK(decode(encode(e)) == e); - } - - // ** integers ** - { - entry e(3); - TEST_CHECK(encode(e) == "i3e"); - TEST_CHECK(decode(encode(e)) == e); - } - - { - entry e(-3); - TEST_CHECK(encode(e) == "i-3e"); - TEST_CHECK(decode(encode(e)) == e); - } - - { - entry e(int(0)); - TEST_CHECK(encode(e) == "i0e"); - TEST_CHECK(decode(encode(e)) == e); - } - - // ** lists ** - { - entry::list_type l; - l.push_back(entry("spam")); - l.push_back(entry("eggs")); - entry e(l); - TEST_CHECK(encode(e) == "l4:spam4:eggse"); - TEST_CHECK(decode(encode(e)) == e); - } - - // ** dictionaries ** - { - entry e(entry::dictionary_t); - e["spam"] = entry("eggs"); - e["cow"] = entry("moo"); - TEST_CHECK(encode(e) == "d3:cow3:moo4:spam4:eggse"); - TEST_CHECK(decode(encode(e)) == e); - } - - { - char b[] = "i12453e"; - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); - TORRENT_ASSERT(ret == 0); -#if TORRENT_USE_IOSTREAM - std::cout << e << std::endl; -#endif - std::pair section = e.data_section(); - TORRENT_ASSERT(std::memcmp(b, section.first, section.second) == 0); - TORRENT_ASSERT(section.second == sizeof(b) - 1); - TORRENT_ASSERT(e.type() == lazy_entry::int_t); - TORRENT_ASSERT(e.int_value() == 12453); - } - - { - char b[] = "26:abcdefghijklmnopqrstuvwxyz"; - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); - TORRENT_ASSERT(ret == 0); -#if TORRENT_USE_IOSTREAM - std::cout << e << std::endl; -#endif - std::pair section = e.data_section(); - TORRENT_ASSERT(std::memcmp(b, section.first, section.second) == 0); - TORRENT_ASSERT(section.second == sizeof(b) - 1); - TORRENT_ASSERT(e.type() == lazy_entry::string_t); - TORRENT_ASSERT(e.string_value() == std::string("abcdefghijklmnopqrstuvwxyz")); - TORRENT_ASSERT(e.string_length() == 26); - } - - { - char b[] = "li12453e3:aaae"; - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); - TORRENT_ASSERT(ret == 0); -#if TORRENT_USE_IOSTREAM - std::cout << e << std::endl; -#endif - std::pair section = e.data_section(); - TORRENT_ASSERT(std::memcmp(b, section.first, section.second) == 0); - TORRENT_ASSERT(section.second == sizeof(b) - 1); - TORRENT_ASSERT(e.type() == lazy_entry::list_t); - TORRENT_ASSERT(e.list_size() == 2); - TORRENT_ASSERT(e.list_at(0)->type() == lazy_entry::int_t); - TORRENT_ASSERT(e.list_at(1)->type() == lazy_entry::string_t); - TORRENT_ASSERT(e.list_at(0)->int_value() == 12453); - TORRENT_ASSERT(e.list_at(1)->string_value() == std::string("aaa")); - TORRENT_ASSERT(e.list_at(1)->string_length() == 3); - section = e.list_at(1)->data_section(); - TORRENT_ASSERT(std::memcmp("3:aaa", section.first, section.second) == 0); - TORRENT_ASSERT(section.second == 5); - } - - { - char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e"; - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(b, b + sizeof(b)-1, e, ec); - TORRENT_ASSERT(ret == 0); -#if TORRENT_USE_IOSTREAM - std::cout << e << std::endl; -#endif - std::pair section = e.data_section(); - TORRENT_ASSERT(std::memcmp(b, section.first, section.second) == 0); - TORRENT_ASSERT(section.second == sizeof(b) - 1); - TORRENT_ASSERT(e.type() == lazy_entry::dict_t); - TORRENT_ASSERT(e.dict_size() == 4); - TORRENT_ASSERT(e.dict_find("a")->type() == lazy_entry::int_t); - TORRENT_ASSERT(e.dict_find("a")->int_value() == 12453); - TORRENT_ASSERT(e.dict_find("b")->type() == lazy_entry::string_t); - TORRENT_ASSERT(e.dict_find("b")->string_value() == std::string("aaa")); - TORRENT_ASSERT(e.dict_find("b")->string_length() == 3); - TORRENT_ASSERT(e.dict_find("c")->type() == lazy_entry::string_t); - TORRENT_ASSERT(e.dict_find("c")->string_value() == std::string("bbb")); - TORRENT_ASSERT(e.dict_find("c")->string_length() == 3); - TORRENT_ASSERT(e.dict_find_string_value("X") == "0123456789"); - } - - // test invalid encoding - { - char buf[] = - { 0x64 , 0x31 , 0x3a , 0x61 , 0x64 , 0x32 , 0x3a , 0x69 - , 0x64 , 0x32 , 0x30 , 0x3a , 0x2a , 0x21 , 0x19 , 0x89 - , 0x9f , 0xcd , 0x5f , 0xc9 , 0xbc , 0x80 , 0xc1 , 0x76 - , 0xfe , 0xe0 , 0xc6 , 0x84 , 0x2d , 0xf6 , 0xfc , 0xb8 - , 0x39 , 0x3a , 0x69 , 0x6e , 0x66 , 0x6f , 0x5f , 0x68 - , 0x61 , 0xae , 0x68 , 0x32 , 0x30 , 0x3a , 0x14 , 0x78 - , 0xd5 , 0xb0 , 0xdc , 0xf6 , 0x82 , 0x42 , 0x32 , 0xa0 - , 0xd6 , 0x88 , 0xeb , 0x48 , 0x57 , 0x01 , 0x89 , 0x40 - , 0x4e , 0xbc , 0x65 , 0x31 , 0x3a , 0x71 , 0x39 , 0x3a - , 0x67 , 0x65 , 0x74 , 0x5f , 0x70 , 0x65 , 0x65 , 0x72 - , 0x78 , 0xff , 0x3a , 0x74 , 0x38 , 0x3a , 0xaa , 0xd4 - , 0xa1 , 0x88 , 0x7a , 0x8d , 0xc3 , 0xd6 , 0x31 , 0x3a - , 0x79 , 0x31 , 0xae , 0x71 , 0x65 , 0}; - - printf("%s\n", buf); - lazy_entry e; - error_code ec; - int ret = lazy_bdecode(buf, buf + sizeof(buf), e, ec); - TEST_CHECK(ret == -1); - } - return 0; -} - diff --git a/libtorrent_utp/test/test_buffer.cpp b/libtorrent_utp/test/test_buffer.cpp deleted file mode 100644 index 8f55c5aad..000000000 --- a/libtorrent_utp/test/test_buffer.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - Copyright (c) 2003 - 2005, 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 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. - -*/ - -#include -#include -#include -#include -#include -#include - -#include "libtorrent/buffer.hpp" -#include "libtorrent/chained_buffer.hpp" -#include "libtorrent/socket.hpp" - -#include "test.hpp" - -using namespace libtorrent; - -/* -template -T const& min_(T const& x, T const& y) -{ - return x < y ? x : y; -} - -void test_speed() -{ - buffer b; - - char data[32]; - - srand(0); - - boost::timer t; - - int const iterations = 5000000; - int const step = iterations / 20; - - for (int i = 0; i < iterations; ++i) - { - int x = rand(); - - if (i % step == 0) std::cerr << "."; - - std::size_t n = rand() % 32; - n = 32; - - if (x % 2) - { - b.insert(data, data + n); - } - else - { - b.erase(min_(b.size(), n)); - } - } - - float t1 = t.elapsed(); - std::cerr << "buffer elapsed: " << t.elapsed() << "\n"; - - std::vector v; - - srand(0); - t.restart(); - - for (int i = 0; i < iterations; ++i) - { - int x = rand(); - - if (i % step == 0) std::cerr << "."; - - std::size_t n = rand() % 32; - n = 32; - - if (x % 2) - { - v.insert(v.end(), data, data + n); - } - else - { - v.erase(v.begin(), v.begin() + min_(v.size(), n)); - } - } - - float t2 = t.elapsed(); - std::cerr << "std::vector elapsed: " << t.elapsed() << "\n"; - - assert(t1 < t2); -} -*/ - -// -- test buffer -- - -void test_buffer() -{ - char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - - buffer b; - - TEST_CHECK(b.size() == 0); - TEST_CHECK(b.capacity() == 0); - TEST_CHECK(b.empty()); - - b.resize(10); - TEST_CHECK(b.size() == 10); - TEST_CHECK(b.capacity() == 10); - - std::memcpy(b.begin(), data, 10); - b.reserve(50); - TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); - TEST_CHECK(b.capacity() == 50); - - b.erase(b.begin() + 6, b.end()); - TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0); - TEST_CHECK(b.capacity() == 50); - TEST_CHECK(b.size() == 6); - - b.insert(b.begin(), data + 5, data + 10); - TEST_CHECK(b.capacity() == 50); - TEST_CHECK(b.size() == 11); - TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0); - - b.clear(); - TEST_CHECK(b.size() == 0); - TEST_CHECK(b.capacity() == 50); - - b.insert(b.end(), data, data + 10); - TEST_CHECK(b.size() == 10); - TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0); - - b.erase(b.begin(), b.end()); - TEST_CHECK(b.capacity() == 50); - TEST_CHECK(b.size() == 0); - - buffer().swap(b); - TEST_CHECK(b.capacity() == 0); - -} - -// -- test chained buffer -- - -std::set buffer_list; - -void free_buffer(char* m) -{ - std::set::iterator i = buffer_list.find(m); - TEST_CHECK(i != buffer_list.end()); - - buffer_list.erase(i); - std::free(m); -} - -char* allocate_buffer(int size) -{ - char* mem = (char*)std::malloc(size); - buffer_list.insert(mem); - return mem; -} - -template -int copy_buffers(T const& b, char* target) -{ - int copied = 0; - for (typename T::const_iterator i = b.begin() - , end(b.end()); i != end; ++i) - { - memcpy(target, libtorrent::asio::buffer_cast(*i), libtorrent::asio::buffer_size(*i)); - target += libtorrent::asio::buffer_size(*i); - copied += libtorrent::asio::buffer_size(*i); - } - return copied; -} - -bool compare_chained_buffer(chained_buffer& b, char const* mem, int size) -{ - if (size == 0) return true; - std::vector flat(size); - std::list const& iovec2 = b.build_iovec(size); - int copied = copy_buffers(iovec2, &flat[0]); - TEST_CHECK(copied == size); - return std::memcmp(&flat[0], mem, size) == 0; -} - -void test_chained_buffer() -{ - char data[] = "foobar"; - { - chained_buffer b; - - TEST_CHECK(b.empty()); - TEST_CHECK(b.capacity() == 0); - TEST_CHECK(b.size() == 0); - TEST_CHECK(b.space_in_last_buffer() == 0); - TEST_CHECK(buffer_list.empty()); - - char* b1 = allocate_buffer(512); - std::memcpy(b1, data, 6); - b.append_buffer(b1, 512, 6, (void(*)(char*))&free_buffer); - TEST_CHECK(buffer_list.size() == 1); - - TEST_CHECK(b.capacity() == 512); - TEST_CHECK(b.size() == 6); - TEST_CHECK(!b.empty()); - TEST_CHECK(b.space_in_last_buffer() == 512 - 6); - - b.pop_front(3); - - TEST_CHECK(b.capacity() == 512); - TEST_CHECK(b.size() == 3); - TEST_CHECK(!b.empty()); - TEST_CHECK(b.space_in_last_buffer() == 512 - 6); - - bool ret = b.append(data, 6); - - TEST_CHECK(ret == true); - TEST_CHECK(b.capacity() == 512); - TEST_CHECK(b.size() == 9); - TEST_CHECK(!b.empty()); - TEST_CHECK(b.space_in_last_buffer() == 512 - 12); - - ret = b.append(data, 1024); - - TEST_CHECK(ret == false); - - char* b2 = allocate_buffer(512); - std::memcpy(b2, data, 6); - b.append_buffer(b2, 512, 6, (void(*)(char*))&free_buffer); - TEST_CHECK(buffer_list.size() == 2); - - char* b3 = allocate_buffer(512); - std::memcpy(b3, data, 6); - b.append_buffer(b3, 512, 6, (void(*)(char*))&free_buffer); - TEST_CHECK(buffer_list.size() == 3); - - TEST_CHECK(b.capacity() == 512 * 3); - TEST_CHECK(b.size() == 21); - TEST_CHECK(!b.empty()); - TEST_CHECK(b.space_in_last_buffer() == 512 - 6); - - TEST_CHECK(compare_chained_buffer(b, "barfoobar", 9)); - - for (int i = 1; i < 21; ++i) - TEST_CHECK(compare_chained_buffer(b, "barfoobarfoobarfoobar", i)); - - b.pop_front(5 + 6); - - TEST_CHECK(buffer_list.size() == 2); - TEST_CHECK(b.capacity() == 512 * 2); - TEST_CHECK(b.size() == 10); - TEST_CHECK(!b.empty()); - TEST_CHECK(b.space_in_last_buffer() == 512 - 6); - - char const* str = "obarfooba"; - TEST_CHECK(compare_chained_buffer(b, str, 9)); - - for (int i = 0; i < 9; ++i) - { - b.pop_front(1); - ++str; - TEST_CHECK(compare_chained_buffer(b, str, 8 - i)); - TEST_CHECK(b.size() == 9 - i); - } - - char* b4 = allocate_buffer(20); - std::memcpy(b4, data, 6); - std::memcpy(b4 + 6, data, 6); - b.append_buffer(b4, 20, 12, (void(*)(char*))&free_buffer); - TEST_CHECK(b.space_in_last_buffer() == 8); - - ret = b.append(data, 6); - TEST_CHECK(ret == true); - TEST_CHECK(b.space_in_last_buffer() == 2); - std::cout << b.space_in_last_buffer() << std::endl; - ret = b.append(data, 2); - TEST_CHECK(ret == true); - TEST_CHECK(b.space_in_last_buffer() == 0); - std::cout << b.space_in_last_buffer() << std::endl; - - char* b5 = allocate_buffer(20); - std::memcpy(b4, data, 6); - b.append_buffer(b5, 20, 6, (void(*)(char*))&free_buffer); - - b.pop_front(22); - TEST_CHECK(b.size() == 5); - } - TEST_CHECK(buffer_list.empty()); -} - -int test_main() -{ - test_buffer(); - test_chained_buffer(); - return 0; -} - diff --git a/libtorrent_utp/test/test_dht.cpp b/libtorrent_utp/test/test_dht.cpp deleted file mode 100644 index 5d56eebe1..000000000 --- a/libtorrent_utp/test/test_dht.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - -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_DISABLE_DHT - -#include "libtorrent/session.hpp" -#include "libtorrent/kademlia/node.hpp" // for verify_message -#include "libtorrent/bencode.hpp" -#include - -#include "test.hpp" - -using namespace libtorrent; - -int dht_port = 48199; - -void send_dht_msg(datagram_socket& sock, char const* msg, lazy_entry* reply - , char const* t = "10", char const* info_hash = 0, char const* name = 0 - , char const* token = 0, int port = 0) -{ - entry e; - e["q"] = msg; - e["t"] = t; - e["y"] = "q"; - entry::dictionary_type& a = e["a"].dict(); - a["id"] = "00000000000000000000"; - if (info_hash) a["info_hash"] = info_hash; - if (name) a["n"] = name; - if (token) a["token"] = token; - if (port) a["port"] = port; - char msg_buf[1500]; - int size = bencode(msg_buf, e); - - error_code ec; - sock.send_to(asio::buffer(msg_buf, size) - , udp::endpoint(address::from_string("127.0.0.1"), dht_port), 0, ec); - TEST_CHECK(!ec); - if (ec) std::cout << ec.message() << std::endl; - - static char inbuf[1500]; - udp::endpoint ep; - size = sock.receive_from(asio::buffer(inbuf, sizeof(inbuf)), ep, 0, ec); - TEST_CHECK(!ec); - if (ec) std::cout << ec.message() << std::endl; - - int ret = lazy_bdecode(inbuf, inbuf + size, *reply, ec); - TEST_CHECK(ret == 0); -} - -int test_main() -{ - session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(dht_port, 49000)); - - // DHT should be running on port 48199 now - - io_service ios; - error_code ec; - datagram_socket sock(ios); - - sock.open(udp::v4(), ec); - TEST_CHECK(!ec); - if (ec) std::cout << ec.message() << std::endl; - - lazy_entry response; - lazy_entry const* parsed[5]; - char error_string[200]; - bool ret; - - // ====== ping ====== - - send_dht_msg(sock, "ping", &response, "10"); - - dht::key_desc_t pong_desc[] = { - {"y", lazy_entry::string_t, 1, 0}, - {"t", lazy_entry::string_t, 2, 0}, - }; - - fprintf(stderr, "msg: %s\n", print_entry(response).c_str()); - ret = dht::verify_message(&response, pong_desc, parsed, 2, error_string, sizeof(error_string)); - TEST_CHECK(ret); - if (ret) - { - TEST_CHECK(parsed[0]->string_value() == "r"); - TEST_CHECK(parsed[1]->string_value() == "10"); - } - else - { - fprintf(stderr, "invalid ping response: %s\n", error_string); - } - - // ====== invalid message ====== - - send_dht_msg(sock, "find_node", &response, "10"); - - dht::key_desc_t err_desc[] = { - {"y", lazy_entry::string_t, 1, 0}, - {"e", lazy_entry::list_t, 0, 0}, - }; - - fprintf(stderr, "msg: %s\n", print_entry(response).c_str()); - ret = dht::verify_message(&response, err_desc, parsed, 2, error_string, sizeof(error_string)); - TEST_CHECK(ret); - if (ret) - { - TEST_CHECK(parsed[0]->string_value() == "e"); - TEST_CHECK(parsed[1]->list_size() >= 2); - if (parsed[1]->list_size() >= 2 - && parsed[1]->list_at(0)->type() == lazy_entry::int_t - && parsed[1]->list_at(1)->type() == lazy_entry::string_t) - { - TEST_CHECK(parsed[1]->list_at(1)->string_value() == "missing 'target' key"); - } - else - { - TEST_ERROR("invalid error response"); - } - } - else - { - fprintf(stderr, "invalid error response: %s\n", error_string); - } - - // ====== get_peers ====== - - send_dht_msg(sock, "get_peers", &response, "10", "01010101010101010101"); - - dht::key_desc_t peer1_desc[] = { - {"y", lazy_entry::string_t, 1, 0}, - {"r", lazy_entry::dict_t, 0, 0}, - }; - - std::string token; - fprintf(stderr, "msg: %s\n", print_entry(response).c_str()); - ret = dht::verify_message(&response, peer1_desc, parsed, 2, error_string, sizeof(error_string)); - TEST_CHECK(ret); - if (ret) - { - TEST_CHECK(parsed[0]->string_value() == "r"); - token = parsed[1]->dict_find_string_value("token"); - } - else - { - fprintf(stderr, "invalid get_peers response: %s\n", error_string); - } - - // ====== announce ====== - - send_dht_msg(sock, "announce_peer", &response, "10", "01010101010101010101", "test", token.c_str(), 8080); - - dht::key_desc_t ann_desc[] = { - {"y", lazy_entry::string_t, 1, 0}, - }; - - fprintf(stderr, "msg: %s\n", print_entry(response).c_str()); - ret = dht::verify_message(&response, ann_desc, parsed, 1, error_string, sizeof(error_string)); - TEST_CHECK(ret); - if (ret) - { - TEST_CHECK(parsed[0]->string_value() == "r"); - } - else - { - fprintf(stderr, "invalid announce response: %s\n", error_string); - } - - // ====== get_peers ====== - - send_dht_msg(sock, "get_peers", &response, "10", "01010101010101010101"); - - dht::key_desc_t peer2_desc[] = { - {"y", lazy_entry::string_t, 1, 0}, - {"r", lazy_entry::dict_t, 0, 0}, - }; - - fprintf(stderr, "msg: %s\n", print_entry(response).c_str()); - ret = dht::verify_message(&response, peer2_desc, parsed, 2, error_string, sizeof(error_string)); - TEST_CHECK(ret); - if (ret) - { - TEST_CHECK(parsed[0]->string_value() == "r"); - TEST_EQUAL(parsed[1]->dict_find_string_value("n"), "test"); - } - else - { - fprintf(stderr, "invalid get_peers response: %s\n", error_string); - } - - return 0; -} - -#else - -int test_main() -{ - return 0; -} - -#endif - diff --git a/libtorrent_utp/test/test_fast_extension.cpp b/libtorrent_utp/test/test_fast_extension.cpp deleted file mode 100644 index f418a6b60..000000000 --- a/libtorrent_utp/test/test_fast_extension.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/* - -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. - -*/ - -#include "test.hpp" -#include "setup_transfer.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/io.hpp" -#include -#include -#include - -using namespace libtorrent; - -int read_message(stream_socket& s, char* buffer) -{ - using namespace libtorrent::detail; - error_code ec; - libtorrent::asio::read(s, libtorrent::asio::buffer(buffer, 4) - , libtorrent::asio::transfer_all(), ec); - if (ec) - { - std::cout << time_now_string() << ": " << ec.message() << std::endl; - exit(1); - } - char* ptr = buffer; - int length = read_int32(ptr); - - libtorrent::asio::read(s, libtorrent::asio::buffer(buffer, length) - , libtorrent::asio::transfer_all(), ec); - if (ec) - { - std::cout << time_now_string() << ": " << ec.message() << std::endl; - exit(1); - } - return length; -} - -char const* message_name[] = {"choke", "unchoke", "interested", "not_interested" - , "have", "bitfield", "request", "piece", "cancel", "dht_port", "", "", "" - , "suggest_piece", "have_all", "have_none", "reject_request", "allowed_fast"}; - -void send_allow_fast(stream_socket& s, int piece) -{ - std::cout << time_now_string() << " ==> allow fast: " << piece << std::endl; - using namespace libtorrent::detail; - char msg[] = "\0\0\0\x05\x11\0\0\0\0"; - char* ptr = msg + 5; - write_int32(piece, ptr); - error_code ec; - libtorrent::asio::write(s, libtorrent::asio::buffer(msg, 9) - , libtorrent::asio::transfer_all(), ec); -} - -void send_suggest_piece(stream_socket& s, int piece) -{ - std::cout << time_now_string() << " ==> suggest piece: " << piece << std::endl; - using namespace libtorrent::detail; - char msg[] = "\0\0\0\x05\x0d\0\0\0\0"; - char* ptr = msg + 5; - write_int32(piece, ptr); - error_code ec; - libtorrent::asio::write(s, libtorrent::asio::buffer(msg, 9) - , libtorrent::asio::transfer_all(), ec); -} - -void send_keepalive(stream_socket& s) -{ - std::cout << time_now_string() << " ==> keepalive" << std::endl; - char msg[] = "\0\0\0\0"; - error_code ec; - libtorrent::asio::write(s, libtorrent::asio::buffer(msg, 4) - , libtorrent::asio::transfer_all(), ec); -} - -void send_unchoke(stream_socket& s) -{ - std::cout << time_now_string() << " ==> unchoke" << std::endl; - char msg[] = "\0\0\0\x01\x01"; - error_code ec; - libtorrent::asio::write(s, libtorrent::asio::buffer(msg, 5) - , libtorrent::asio::transfer_all(), ec); -} - -void do_handshake(stream_socket& s, sha1_hash const& ih, char* buffer) -{ - char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" - " " // space for info-hash - "aaaaaaaaaaaaaaaaaaaa" // peer-id - "\0\0\0\x01\x0e"; // have_all - std::cout << time_now_string() << " ==> handshake" << std::endl; - std::cout << time_now_string() << " ==> have_all" << std::endl; - error_code ec; - std::memcpy(handshake + 28, ih.begin(), 20); - libtorrent::asio::write(s, libtorrent::asio::buffer(handshake, sizeof(handshake) - 1) - , libtorrent::asio::transfer_all(), ec); - - // read handshake - libtorrent::asio::read(s, libtorrent::asio::buffer(buffer, 68) - , libtorrent::asio::transfer_all(), ec); - if (ec) - { - std::cout << time_now_string() << ": " << ec.message() << std::endl; - exit(1); - } - std::cout << time_now_string() << " <== handshake" << std::endl; - - TEST_CHECK(buffer[0] == 19); - TEST_CHECK(std::memcmp(buffer + 1, "BitTorrent protocol", 19) == 0); - - char* extensions = buffer + 20; - // check for fast extension support - TEST_CHECK(extensions[7] & 0x4); - -#ifndef TORRENT_DISABLE_EXTENSIONS - // check for extension protocol support - TEST_CHECK(extensions[5] & 0x10); -#endif - -#ifndef TORRENT_DISABLE_DHT - // check for DHT support - TEST_CHECK(extensions[7] & 0x1); -#endif - - TEST_CHECK(std::memcmp(buffer + 28, ih.begin(), 20) == 0); -} - -// makes sure that pieces that are allowed and then -// rejected aren't requested again -void test_reject_fast() -{ - std::cerr << " === test reject ===" << std::endl; - - boost::intrusive_ptr t = ::create_torrent(); - sha1_hash ih = t->info_hash(); - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48900, 49000), "0.0.0.0", 0); - error_code ec; - add_torrent_params p; - p.ti = t; - p.save_path = "./tmp1_fast"; - ses1.add_torrent(p, ec); - - test_sleep(2000); - - io_service ios; - stream_socket s(ios); - s.connect(tcp::endpoint(address::from_string("127.0.0.1", ec), ses1.listen_port()), ec); - - char recv_buffer[1000]; - do_handshake(s, ih, recv_buffer); - - std::vector allowed_fast; - allowed_fast.push_back(0); - allowed_fast.push_back(1); - allowed_fast.push_back(2); - allowed_fast.push_back(3); - - std::for_each(allowed_fast.begin(), allowed_fast.end() - , boost::bind(&send_allow_fast, boost::ref(s), _1)); - - while (!allowed_fast.empty()) - { - read_message(s, recv_buffer); - int msg = recv_buffer[0]; - if (msg >= 0 && msg < int(sizeof(message_name)/sizeof(message_name[0]))) - std::cerr << time_now_string() << " <== " << message_name[msg] << std::endl; - else - std::cerr << time_now_string() << " <== " << msg << std::endl; - if (recv_buffer[0] != 0x6) continue; - - using namespace libtorrent::detail; - char* ptr = recv_buffer + 1; - int piece = read_int32(ptr); - - std::vector::iterator i = std::find(allowed_fast.begin() - , allowed_fast.end(), piece); - TEST_CHECK(i != allowed_fast.end()); - if (i != allowed_fast.end()) - allowed_fast.erase(i); - // send reject request - recv_buffer[0] = 0x10; - error_code ec; - std::cerr << time_now_string() << " ==> reject" << std::endl; - libtorrent::asio::write(s, libtorrent::asio::buffer("\0\0\0\x0d", 4) - , libtorrent::asio::transfer_all(), ec); - libtorrent::asio::write(s, libtorrent::asio::buffer(recv_buffer, 13) - , libtorrent::asio::transfer_all(), ec); - } -} - -void test_respect_suggest() -{ - std::cerr << " === test suggest ===" << std::endl; - boost::intrusive_ptr t = ::create_torrent(); - sha1_hash ih = t->info_hash(); - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48900, 49000), "0.0.0.0", 0); - - error_code ec; - add_torrent_params p; - p.ti = t; - p.save_path = "./tmp1_fast"; - ses1.add_torrent(p, ec); - - test_sleep(2000); - - io_service ios; - stream_socket s(ios); - s.connect(tcp::endpoint(address::from_string("127.0.0.1", ec), ses1.listen_port()), ec); - - char recv_buffer[1000]; - do_handshake(s, ih, recv_buffer); - - std::vector suggested; - suggested.push_back(0); - suggested.push_back(1); - suggested.push_back(2); - suggested.push_back(3); - - std::for_each(suggested.begin(), suggested.end() - , boost::bind(&send_suggest_piece, boost::ref(s), _1)); - - send_unchoke(s); - - send_keepalive(s); - - int fail_counter = 100; - while (!suggested.empty() && fail_counter > 0) - { - read_message(s, recv_buffer); - std::cerr << time_now_string() << " <== "; - int msg = recv_buffer[0]; - if (msg >= 0 && msg < int(sizeof(message_name)/sizeof(message_name[0]))) - std::cerr << message_name[msg] << std::endl; - else - std::cerr << msg << std::endl; - fail_counter--; - if (recv_buffer[0] != 0x6) continue; - - using namespace libtorrent::detail; - char* ptr = recv_buffer + 1; - int piece = read_int32(ptr); - - std::vector::iterator i = std::find(suggested.begin() - , suggested.end(), piece); - TEST_CHECK(i != suggested.end()); - if (i != suggested.end()) - suggested.erase(i); - // send reject request - recv_buffer[0] = 0x10; - error_code ec; - std::cerr << time_now_string() << " ==> reject" << std::endl; - libtorrent::asio::write(s, libtorrent::asio::buffer("\0\0\0\x0d", 4) - , libtorrent::asio::transfer_all(), ec); - libtorrent::asio::write(s, libtorrent::asio::buffer(recv_buffer, 13) - , libtorrent::asio::transfer_all(), ec); - } - TEST_CHECK(fail_counter > 0); -} - -int test_main() -{ - test_reject_fast(); - test_respect_suggest(); - return 0; -} - diff --git a/libtorrent_utp/test/test_hasher.cpp b/libtorrent_utp/test/test_hasher.cpp deleted file mode 100644 index b11868355..000000000 --- a/libtorrent_utp/test/test_hasher.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/hasher.hpp" -#include -#include "libtorrent/escape_string.hpp" // from_hex - -#include "test.hpp" - -using namespace libtorrent; - -// test vectors from RFC 3174 -// http://www.faqs.org/rfcs/rfc3174.html - -char const* test_array[4] = -{ - "abc", - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "a", - "0123456701234567012345670123456701234567012345670123456701234567" -}; - -long int repeat_count[4] = { 1, 1, 1000000, 10 }; - -char const* result_array[4] = -{ - "A9993E364706816ABA3E25717850C26C9CD0D89D", - "84983E441C3BD26EBAAE4AA1F95129E5E54670F1", - "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F", - "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452" -}; - - -int test_main() -{ - using namespace libtorrent; - - for (int test = 0; test < 4; ++test) - { - hasher h; - for (int i = 0; i < repeat_count[test]; ++i) - h.update(test_array[test], std::strlen(test_array[test])); - - sha1_hash result; - from_hex(result_array[test], 40, (char*)&result[0]); - TEST_CHECK(result == h.final()); - } - - return 0; -} - diff --git a/libtorrent_utp/test/test_http_connection.cpp b/libtorrent_utp/test/test_http_connection.cpp deleted file mode 100644 index 40069cb4c..000000000 --- a/libtorrent_utp/test/test_http_connection.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* - -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. - -*/ - -#include "test.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/socket_io.hpp" // print_endpoint -#include "libtorrent/connection_queue.hpp" -#include "libtorrent/http_connection.hpp" -#include "setup_transfer.hpp" - -#include -#include -#include - -using namespace libtorrent; - -io_service ios; -connection_queue cq(ios); - -int connect_handler_called = 0; -int handler_called = 0; -int data_size = 0; -int http_status = 0; -error_code g_error_code; -char data_buffer[4000]; - -void print_http_header(http_parser const& p) -{ - std::cerr << " < " << p.status_code() << " " << p.message() << std::endl; - - for (std::map::const_iterator i - = p.headers().begin(), end(p.headers().end()); i != end; ++i) - { - std::cerr << " < " << i->first << ": " << i->second << std::endl; - } -} - -void http_connect_handler(http_connection& c) -{ - ++connect_handler_called; - TEST_CHECK(c.socket().is_open()); - error_code ec; - std::cerr << "connected to: " << print_endpoint(c.socket().remote_endpoint(ec)) - << std::endl; -// this is not necessarily true when using a proxy and proxying hostnames -// TEST_CHECK(c.socket().remote_endpoint(ec).address() == address::from_string("127.0.0.1", ec)); -} - -void http_handler(error_code const& ec, http_parser const& parser - , char const* data, int size, http_connection& c) -{ - ++handler_called; - data_size = size; - g_error_code = ec; - - if (parser.header_finished()) - { - http_status = parser.status_code(); - if (http_status == 200) - { - TEST_CHECK(memcmp(data, data_buffer, size) == 0); - } - } - print_http_header(parser); -} - -void reset_globals() -{ - connect_handler_called = 0; - handler_called = 0; - data_size = 0; - http_status = 0; - g_error_code = error_code(); -} - -void run_test(std::string const& url, int size, int status, int connected - , boost::optional ec, proxy_settings const& ps) -{ - reset_globals(); - - std::cerr << " ===== TESTING: " << url << " =====" << std::endl; - - std::cerr << " expecting: size: " << size - << " status: " << status - << " connected: " << connected - << " error: " << (ec?ec->message():"no error") << std::endl; - - boost::shared_ptr h(new http_connection(ios, cq - , &::http_handler, true, &::http_connect_handler)); - h->get(url, seconds(1), 0, &ps); - ios.reset(); - error_code e; - ios.run(e); - - std::cerr << "connect_handler_called: " << connect_handler_called << std::endl; - std::cerr << "handler_called: " << handler_called << std::endl; - std::cerr << "status: " << http_status << std::endl; - std::cerr << "size: " << data_size << std::endl; - std::cerr << "error_code: " << g_error_code.message() << std::endl; - TEST_CHECK(connect_handler_called == connected); - TEST_CHECK(handler_called == 1); - TEST_CHECK(data_size == size || size == -1); - TEST_CHECK(!ec || g_error_code == *ec); - TEST_CHECK(http_status == status || status == -1); -} - -void run_suite(std::string const& protocol, proxy_settings const& ps, int port) -{ - if (ps.type != proxy_settings::none) - { - start_proxy(ps.port, ps.type); - } - char const* test_name[] = {"no", "SOCKS4", "SOCKS5" - , "SOCKS5 password protected", "HTTP", "HTTP password protected"}; - std::cout << "\n\n********************** using " << test_name[ps.type] - << " proxy **********************\n" << std::endl; - - typedef boost::optional err; - // this requires the hosts file to be modified -// run_test(protocol + "://test.dns.ts:8001/test_file", 3216, 200, 1, error_code(), ps); - - char url[256]; - snprintf(url, sizeof(url), "%s://127.0.0.1:%d/", protocol.c_str(), port); - std::string url_base(url); - - run_test(url_base + "relative/redirect", 3216, 200, 2, error_code(), ps); - run_test(url_base + "redirect", 3216, 200, 2, error_code(), ps); - run_test(url_base + "infinite_redirect", 0, 301, 6, error_code(), ps); - run_test(url_base + "test_file", 3216, 200, 1, error_code(), ps); - run_test(url_base + "test_file.gz", 3216, 200, 1, error_code(), ps); - run_test(url_base + "non-existing-file", -1, 404, 1, err(), ps); - - // only run the tests to handle NX_DOMAIN if we have a proper internet - // connection that doesn't inject false DNS responses (like Comcast does) - hostent* h = gethostbyname("non-existent-domain.se"); - if (h == 0 && h_errno == HOST_NOT_FOUND) - { - // if we're going through an http proxy, we won't get the same error as if the hostname - // resolution failed - if ((ps.type == proxy_settings::http || ps.type == proxy_settings::http_pw) && protocol != "https") - run_test(protocol + "://non-existent-domain.se/non-existing-file", -1, 502, 1, err(), ps); - else - run_test(protocol + "://non-existent-domain.se/non-existing-file", -1, -1, 0, err(), ps); - } - - if (ps.type != proxy_settings::none) - stop_proxy(ps.port); -} - -int test_main() -{ - std::srand(std::time(0)); - std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand); - error_code ec; - file test_file("test_file", file::write_only, ec); - TEST_CHECK(!ec); - if (ec) fprintf(stderr, "file error: %s\n", ec.message().c_str()); - file::iovec_t b = { data_buffer, 3216}; - test_file.writev(0, &b, 1, ec); - TEST_CHECK(!ec); - if (ec) fprintf(stderr, "file error: %s\n", ec.message().c_str()); - test_file.close(); - std::system("gzip -9 -c test_file > test_file.gz"); - - proxy_settings ps; - ps.hostname = "127.0.0.1"; - ps.port = 8034; - ps.username = "testuser"; - ps.password = "testpass"; - - int port = start_web_server(); - for (int i = 0; i < 5; ++i) - { - ps.type = (proxy_settings::proxy_type)i; - run_suite("http", ps, port); - } - stop_web_server(); - -#ifdef TORRENT_USE_OPENSSL - port = start_web_server(true); - for (int i = 0; i < 5; ++i) - { - ps.type = (proxy_settings::proxy_type)i; - run_suite("https", ps, port); - } - stop_web_server(); -#endif - - std::remove("test_file"); - return 0; -} - diff --git a/libtorrent_utp/test/test_ip_filter.cpp b/libtorrent_utp/test/test_ip_filter.cpp deleted file mode 100644 index f7d009e37..000000000 --- a/libtorrent_utp/test/test_ip_filter.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/ip_filter.hpp" -#include - -#include "test.hpp" -#include "libtorrent/socket_io.hpp" - -/* - -Currently this test only tests that the filter can handle -IPv4 addresses. Maybe it should be extended to IPv6 as well, -but the actual code is just a template, so it is probably -pretty safe to assume that as long as it works for IPv4 it -also works for IPv6. - -*/ - -using namespace libtorrent; - -template -bool compare(ip_range const& lhs - , ip_range const& rhs) -{ - return lhs.first == rhs.first - && lhs.last == rhs.last - && lhs.flags == rhs.flags; -} - -#define IP(x) address::from_string(x, ec) -#define IP4(x) address_v4::from_string(x, ec) - -void test_rules_invariant(std::vector > const& r, ip_filter const& f) -{ - typedef std::vector >::const_iterator iterator; - TEST_CHECK(!r.empty()); - if (r.empty()) return; - - error_code ec; - TEST_CHECK(r.front().first == IP("0.0.0.0")); - TEST_CHECK(r.back().last == IP("255.255.255.255")); - - iterator i = r.begin(); - iterator j = boost::next(i); - for (iterator i(r.begin()), j(boost::next(r.begin())) - , end(r.end()); j != end; ++j, ++i) - { - TEST_CHECK(f.access(i->last) == i->flags); - TEST_CHECK(f.access(j->first) == j->flags); - TEST_CHECK(i->last.to_ulong() + 1 == j->first.to_ulong()); - } -} - -int test_main() -{ - using namespace libtorrent; - - std::vector > range; - error_code ec; - - // **** test joining of ranges at the end **** - ip_range expected1[] = - { - {IP4("0.0.0.0"), IP4("0.255.255.255"), 0} - , {IP4("1.0.0.0"), IP4("3.0.0.0"), ip_filter::blocked} - , {IP4("3.0.0.1"), IP4("255.255.255.255"), 0} - }; - - { - ip_filter f; - f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); - f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); - - } - - // **** test joining of ranges at the start **** - - { - ip_filter f; - f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); - f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); - - } - - - // **** test joining of overlapping ranges at the start **** - - { - ip_filter f; - f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); - f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); - - } - - - // **** test joining of overlapping ranges at the end **** - - { - ip_filter f; - f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked); - f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare)); - - } - - - // **** test joining of multiple overlapping ranges 1 **** - - { - ip_filter f; - f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); - f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked); - f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked); - f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked); - - f.add_rule(IP("1.0.1.0"), IP("9.0.0.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - ip_range expected[] = - { - {IP4("0.0.0.0"), IP4("0.255.255.255"), 0} - , {IP4("1.0.0.0"), IP4("9.0.0.0"), ip_filter::blocked} - , {IP4("9.0.0.1"), IP4("255.255.255.255"), 0} - }; - - TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare)); - - } - - // **** test joining of multiple overlapping ranges 2 **** - - { - ip_filter f; - f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked); - f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked); - f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked); - f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked); - - f.add_rule(IP("0.0.1.0"), IP("7.0.4.0"), ip_filter::blocked); - -#if TORRENT_USE_IPV6 - range = boost::get<0>(f.export_filter()); -#else - range = f.export_filter(); -#endif - test_rules_invariant(range, f); - - TEST_CHECK(range.size() == 3); - ip_range expected[] = - { - {IP4("0.0.0.0"), IP4("0.0.0.255"), 0} - , {IP4("0.0.1.0"), IP4("8.0.0.0"), ip_filter::blocked} - , {IP4("8.0.0.1"), IP4("255.255.255.255"), 0} - }; - - TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare)); - - } - - port_filter pf; - - // default contructed port filter should allow any port - TEST_CHECK(pf.access(0) == 0); - TEST_CHECK(pf.access(65535) == 0); - TEST_CHECK(pf.access(6881) == 0); - - // block port 100 - 300 - pf.add_rule(100, 300, port_filter::blocked); - - TEST_CHECK(pf.access(0) == 0); - TEST_CHECK(pf.access(99) == 0); - TEST_CHECK(pf.access(100) == port_filter::blocked); - TEST_CHECK(pf.access(150) == port_filter::blocked); - TEST_CHECK(pf.access(300) == port_filter::blocked); - TEST_CHECK(pf.access(301) == 0); - TEST_CHECK(pf.access(6881) == 0); - TEST_CHECK(pf.access(65535) == 0); - - return 0; -} - diff --git a/libtorrent_utp/test/test_lsd.cpp b/libtorrent_utp/test/test_lsd.cpp deleted file mode 100644 index ab193b061..000000000 --- a/libtorrent_utp/test/test_lsd.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/thread.hpp" -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include - -void test_lsd() -{ - using namespace libtorrent; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48100, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49100, 50000), "0.0.0.0", 0); - - session_settings settings; - settings.allow_multiple_connections_per_ip = true; - ses1.set_settings(settings); - ses2.set_settings(settings); - - ses1.start_lsd(); - ses2.start_lsd(); - torrent_handle tor1; - torrent_handle tor2; - - using boost::tuples::ignore; - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, false, "_lsd"); - - for (int i = 0; i < 30; ++i) - { - print_alerts(ses1, "ses1", true); - print_alerts(ses2, "ses2", true); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - std::cerr - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << std::endl; - - if (st2.is_seeding /*&& st3.is_seeding*/) break; - test_sleep(1000); - } - - TEST_CHECK(tor2.status().is_seeding); - - if (tor2.status().is_seeding) std::cerr << "done\n"; -} - -int test_main() -{ - using namespace libtorrent; - - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_lsd", ec); - remove_all("./tmp2_lsd", ec); - remove_all("./tmp3_lsd", ec); - - test_lsd(); - - remove_all("./tmp1_lsd", ec); - remove_all("./tmp2_lsd", ec); - remove_all("./tmp3_lsd", ec); - - return 0; -} - - - diff --git a/libtorrent_utp/test/test_metadata_extension.cpp b/libtorrent_utp/test/test_metadata_extension.cpp deleted file mode 100644 index 8d47b1678..000000000 --- a/libtorrent_utp/test/test_metadata_extension.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/thread.hpp" -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include "libtorrent/extensions/metadata_transfer.hpp" -#include "libtorrent/extensions/ut_metadata.hpp" -#include - -using boost::tuples::ignore; - -void test_transfer(bool clear_files, bool disconnect - , boost::shared_ptr (*constructor)(libtorrent::torrent*, void*)) -{ - using namespace libtorrent; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48100, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49100, 50000), "0.0.0.0", 0); - ses1.add_extension(constructor); - ses2.add_extension(constructor); - torrent_handle tor1; - torrent_handle tor2; -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::forced; - pes.in_enc_policy = pe_settings::forced; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); -#endif - - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, clear_files, true, true, "_meta"); - - for (int i = 0; i < 80; ++i) - { - // make sure this function can be called on - // torrents without metadata - if (!disconnect) tor2.status(); - print_alerts(ses1, "ses1", false, true); - print_alerts(ses2, "ses2", false, true); - - if (disconnect && tor2.is_valid()) ses2.remove_torrent(tor2); - if (!disconnect && tor2.status().has_metadata) break; - test_sleep(100); - } - - if (disconnect) return; - - TEST_CHECK(tor2.status().has_metadata); - std::cerr << "waiting for transfer to complete\n"; - - for (int i = 0; i < 30; ++i) - { - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - std::cerr - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << st1.num_peers << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers - << std::endl; - if (st2.is_seeding) break; - test_sleep(1000); - } - - TEST_CHECK(tor2.status().is_seeding); - if (tor2.status().is_seeding) std::cerr << "done\n"; - - error_code ec; - remove_all("./tmp1_meta", ec); - remove_all("./tmp2_meta", ec); - remove_all("./tmp3_meta", ec); -} - -int test_main() -{ - using namespace libtorrent; - - // test to disconnect one client prematurely - test_transfer(true, true, &create_metadata_plugin); - // test where one has data and one doesn't - test_transfer(true, false, &create_metadata_plugin); - // test where both have data (to trigger the file check) - test_transfer(false, false, &create_metadata_plugin); - - // test to disconnect one client prematurely - test_transfer(true, true, &create_ut_metadata_plugin); - // test where one has data and one doesn't - test_transfer(true, false, &create_ut_metadata_plugin); - // test where both have data (to trigger the file check) - test_transfer(false, false, &create_ut_metadata_plugin); - - error_code ec; - remove_all("./tmp1", ec); - remove_all("./tmp2", ec); - - return 0; -} - diff --git a/libtorrent_utp/test/test_natpmp.cpp b/libtorrent_utp/test/test_natpmp.cpp deleted file mode 100644 index cb0fd8a67..000000000 --- a/libtorrent_utp/test/test_natpmp.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "libtorrent/natpmp.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/connection_queue.hpp" -#include -#include -#include -#include - -using namespace libtorrent; - -void callback(int mapping, int port, error_code const& err) -{ - std::cerr - << "mapping: " << mapping - << ", port: " << port - << ", error: \"" << err.message() << "\"\n"; -} - -void log_callback(char const* line) -{ - std::cerr << line << std::endl; -} - -int main(int argc, char* argv[]) -{ - io_service ios; - std::string user_agent = "test agent"; - - if (argc != 3) - { - std::cerr << "usage: " << argv[0] << " tcp-port udp-port" << std::endl; - return 1; - } - - connection_queue cc(ios); - boost::intrusive_ptr natpmp_handler = new natpmp(ios, address_v4() - , &callback, &log_callback); - - deadline_timer timer(ios); - - int tcp_map = natpmp_handler->add_mapping(natpmp::tcp, atoi(argv[1]), atoi(argv[1])); - int udp_map = natpmp_handler->add_mapping(natpmp::udp, atoi(argv[2]), atoi(argv[2])); - - error_code ec; - timer.expires_from_now(seconds(2), ec); - timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios))); - std::cerr << "mapping ports TCP: " << argv[1] - << " UDP: " << argv[2] << std::endl; - - ios.reset(); - ios.run(ec); - timer.expires_from_now(seconds(2), ec); - timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios))); - std::cerr << "removing mapping " << tcp_map << std::endl; - natpmp_handler->delete_mapping(tcp_map); - - ios.reset(); - ios.run(ec); - std::cerr << "removing mappings" << std::endl; - natpmp_handler->close(); - - ios.reset(); - ios.run(ec); - std::cerr << "closing" << std::endl; -} - - diff --git a/libtorrent_utp/test/test_pe_crypto.cpp b/libtorrent_utp/test/test_pe_crypto.cpp deleted file mode 100644 index 42c3cae59..000000000 --- a/libtorrent_utp/test/test_pe_crypto.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - -Copyright (c) 2007, 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 "libtorrent/hasher.hpp" -#include "libtorrent/pe_crypto.hpp" -#include "libtorrent/session.hpp" - -#include "setup_transfer.hpp" -#include "test.hpp" - -#ifndef TORRENT_DISABLE_ENCRYPTION - -void display_pe_policy(libtorrent::pe_settings::enc_policy policy) -{ - using namespace libtorrent; - using std::cerr; - - if (policy == pe_settings::disabled) cerr << "disabled "; - else if (policy == pe_settings::enabled) cerr << "enabled "; - else if (policy == pe_settings::forced) cerr << "forced "; -} - -void display_pe_settings(libtorrent::pe_settings s) -{ - using namespace libtorrent; - using std::cerr; - - cerr << "out_enc_policy - "; - display_pe_policy(s.out_enc_policy); - cerr << "\tin_enc_policy - "; - display_pe_policy(s.in_enc_policy); - - cerr << "\nenc_level - "; - if (s.allowed_enc_level == pe_settings::plaintext) cerr << "plaintext "; - else if (s.allowed_enc_level == pe_settings::rc4) cerr << "rc4 "; - else if (s.allowed_enc_level == pe_settings::both) cerr << "both "; - - cerr << "\t\tprefer_rc4 - "; - (s.prefer_rc4) ? cerr << "true" : cerr << "false"; - cerr << "\n\n"; -} - -void test_transfer(libtorrent::pe_settings::enc_policy policy, - libtorrent::pe_settings::enc_level level = libtorrent::pe_settings::both, - bool pref_rc4 = false) -{ - using namespace libtorrent; - using std::cerr; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48800, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49800, 50000), "0.0.0.0", 0); - pe_settings s; - - s.out_enc_policy = libtorrent::pe_settings::enabled; - s.in_enc_policy = libtorrent::pe_settings::enabled; - - s.allowed_enc_level = pe_settings::both; - ses2.set_pe_settings(s); - - s.out_enc_policy = policy; - s.in_enc_policy = policy; - s.allowed_enc_level = level; - s.prefer_rc4 = pref_rc4; - ses1.set_pe_settings(s); - -// s = ses1.get_pe_settings(); -// cerr << " Session1 \n"; -// display_pe_settings(s); -// s = ses2.get_pe_settings(); -// cerr << " Session2 \n"; -// display_pe_settings(s); - - torrent_handle tor1; - torrent_handle tor2; - - using boost::tuples::ignore; - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, true, "_pe"); - - std::cerr << "waiting for transfer to complete\n"; - - for (int i = 0; i < 50; ++i) - { - torrent_status s = tor2.status(); - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - - if (s.is_seeding) break; - test_sleep(1000); - } - - TEST_CHECK(tor2.status().is_seeding); - if (tor2.status().is_seeding) std::cerr << "done\n"; - ses1.remove_torrent(tor1); - ses2.remove_torrent(tor2); - - error_code ec; - remove_all("./tmp1_pe", ec); - remove_all("./tmp2_pe", ec); - remove_all("./tmp3_pe", ec); -} - - -int test_main() -{ - using namespace libtorrent; - int repcount = 128; - - for (int rep = 0; rep < repcount; ++rep) - { - dh_key_exchange DH1, DH2; - - DH1.compute_secret(DH2.get_local_key()); - DH2.compute_secret(DH1.get_local_key()); - - TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())); - } - - dh_key_exchange DH1, DH2; - DH1.compute_secret(DH2.get_local_key()); - DH2.compute_secret(DH1.get_local_key()); - - TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())); - - sha1_hash test1_key = hasher("test1_key",8).final(); - sha1_hash test2_key = hasher("test2_key",8).final(); - - RC4_handler RC41(test2_key, test1_key); - RC4_handler RC42(test1_key, test2_key); - - for (int rep = 0; rep < repcount; ++rep) - { - std::size_t buf_len = rand() % (512 * 1024); - char* buf = new char[buf_len]; - char* zero_buf = new char[buf_len]; - - std::fill(buf, buf + buf_len, 0); - std::fill(zero_buf, zero_buf + buf_len, 0); - - RC41.encrypt(buf, buf_len); - RC42.decrypt(buf, buf_len); - TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); - - RC42.encrypt(buf, buf_len); - RC41.decrypt(buf, buf_len); - TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); - - delete[] buf; - delete[] zero_buf; - } - - - test_transfer(pe_settings::disabled); - - test_transfer(pe_settings::forced, pe_settings::plaintext); - test_transfer(pe_settings::forced, pe_settings::rc4); - test_transfer(pe_settings::forced, pe_settings::both, false); - test_transfer(pe_settings::forced, pe_settings::both, true); - - test_transfer(pe_settings::enabled, pe_settings::plaintext); - test_transfer(pe_settings::enabled, pe_settings::rc4); - test_transfer(pe_settings::enabled, pe_settings::both, false); - test_transfer(pe_settings::enabled, pe_settings::both, true); - - return 0; -} - -#else - -int test_main() -{ - std::cerr << "PE test not run because it's disabled" << std::endl; - return 0; -} - -#endif - diff --git a/libtorrent_utp/test/test_pex.cpp b/libtorrent_utp/test/test_pex.cpp deleted file mode 100644 index af31acae8..000000000 --- a/libtorrent_utp/test/test_pex.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/extensions/ut_pex.hpp" -#include "libtorrent/thread.hpp" -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include - -void test_pex() -{ - using namespace libtorrent; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48200, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49200, 50000), "0.0.0.0", 0); - session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50200, 51000), "0.0.0.0", 0); - - // this is to avoid everything finish from a single peer - // immediately. To make the swarm actually connect all - // three peers before finishing. - float rate_limit = 1000; - session_settings set = ses1.settings(); - set.upload_rate_limit = rate_limit; - ses1.set_settings(set); - - // make the peer connecting the two worthless to transfer - // data, to force peer 3 to connect directly to peer 1 through pex - set = ses2.settings(); - set.download_rate_limit = rate_limit; - set.upload_rate_limit = 2000; - ses2.set_settings(set); - - set = ses3.settings(); - set.download_rate_limit = rate_limit; - set.upload_rate_limit = rate_limit / 2; - ses3.set_settings(set); - - ses1.add_extension(&create_ut_pex_plugin); - ses2.add_extension(&create_ut_pex_plugin); - -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::forced; - pes.in_enc_policy = pe_settings::forced; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); - ses3.set_pe_settings(pes); -#endif - - torrent_handle tor1; - torrent_handle tor2; - torrent_handle tor3; - - boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, false, "_pex"); - - int mask = alert::all_categories & ~(alert::progress_notification | alert::performance_warning); - ses1.set_alert_mask(mask); - ses2.set_alert_mask(mask); - ses3.set_alert_mask(mask); - - test_sleep(1000); - - // in this test, ses1 is a seed, ses2 is connected to ses1 and ses3. - // the expected behavior is that ses2 will introduce ses1 and ses3 to each other - error_code ec; - tor2.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec), ses1.listen_port())); - tor2.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec), ses3.listen_port())); - - torrent_status st1; - torrent_status st2; - torrent_status st3; - for (int i = 0; i < 90; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - print_alerts(ses3, "ses3"); - - st1 = tor1.status(); - st2 = tor2.status(); - st3 = tor3.status(); - - std::cerr - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << st1.num_peers << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers << " - " - << "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st3.progress * 100) << "% " - << st3.num_peers - << std::endl; - - if (st1.num_peers == 2 && st2.num_peers == 2 && st3.num_peers == 2) - break; - - if (st3.state == torrent_status::seeding) break; - test_sleep(1000); - } - - TEST_CHECK(st1.num_peers == 2 && st2.num_peers == 2 && st3.num_peers == 2) - - if (!tor2.status().is_seeding && tor3.status().is_seeding) std::cerr << "done\n"; -} - -int test_main() -{ - using namespace libtorrent; - - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_pex", ec); - remove_all("./tmp2_pex", ec); - remove_all("./tmp3_pex", ec); - - test_pex(); - - remove_all("./tmp1_pex", ec); - remove_all("./tmp2_pex", ec); - remove_all("./tmp3_pex", ec); - - return 0; -} - diff --git a/libtorrent_utp/test/test_piece_picker.cpp b/libtorrent_utp/test/test_piece_picker.cpp deleted file mode 100644 index a4d1d09d4..000000000 --- a/libtorrent_utp/test/test_piece_picker.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/piece_picker.hpp" -#include "libtorrent/policy.hpp" -#include "libtorrent/bitfield.hpp" -#include -#include -#include -#include -#include -#include -#include - -#include "test.hpp" - -using namespace libtorrent; - -const int blocks_per_piece = 4; - -bitfield string2vec(char const* have_str) -{ - const int num_pieces = strlen(have_str); - bitfield have(num_pieces, false); - for (int i = 0; i < num_pieces; ++i) - if (have_str[i] != ' ') have.set_bit(i); - return have; -} - -// availability is a string where each character is the -// availability of that piece, '1', '2' etc. -// have_str is a string where each character represents a -// piece, ' ' means we don't have the piece and any other -// character means we have it -boost::shared_ptr setup_picker( - char const* availability - , char const* have_str - , char const* priority - , char const* partial) -{ - const int num_pieces = strlen(availability); - assert(int(strlen(have_str)) == num_pieces); - - boost::shared_ptr p(new piece_picker); - p->init(blocks_per_piece, blocks_per_piece, num_pieces); - - for (int i = 0; i < num_pieces; ++i) - { - const int avail = availability[i] - '0'; - assert(avail >= 0); - - for (int j = 0; j < avail; ++j) p->inc_refcount(i); - } - - bitfield have = string2vec(have_str); - - for (int i = 0; i < num_pieces; ++i) - { - if (partial[i] == 0) break; - - if (partial[i] == ' ') continue; - - int blocks = 0; - if (partial[i] >= '0' && partial[i] <= '9') - blocks = partial[i] - '0'; - else - blocks = partial[i] - 'a' + 10; - - int counter = 0; - for (int j = 0; j < 4; ++j) - { - TEST_CHECK(!p->is_finished(piece_block(i, j))); - if ((blocks & (1 << j)) == 0) continue; - ++counter; - bool ret = p->mark_as_downloading(piece_block(i, j), 0, piece_picker::slow); - TEST_CHECK(ret == true); - TEST_CHECK(p->is_requested(piece_block(i, j)) == bool(blocks & (1 << j))); - p->mark_as_writing(piece_block(i, j), 0); - TEST_CHECK(!p->is_finished(piece_block(i, j))); - // trying to mark a block as requested after it has been completed - // should fail (return false) - ret = p->mark_as_downloading(piece_block(i, j), 0, piece_picker::slow); - TEST_CHECK(ret == false); - p->mark_as_finished(piece_block(i, j), 0); - - TEST_CHECK(p->is_downloaded(piece_block(i, j)) == bool(blocks & (1 << j))); - TEST_CHECK(p->is_finished(piece_block(i, j)) == bool(blocks & (1 << j))); - } - - piece_picker::downloading_piece st; - p->piece_info(i, st); - TEST_CHECK(st.writing == 0); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.index == i); - - TEST_CHECK(st.finished == counter); - TEST_CHECK(st.finished + st.requested + st.writing == counter); - - TEST_CHECK(p->is_piece_finished(i) == (counter == 4)); - } - - for (int i = 0; i < num_pieces; ++i) - { - if (priority[i] == 0) break; - const int prio = priority[i] - '0'; - assert(prio >= 0); - p->set_piece_priority(i, prio); - - TEST_CHECK(p->piece_priority(i) == prio); - } - - for (int i = 0; i < num_pieces; ++i) - { - if (!have[i]) continue; - p->we_have(i); - for (int j = 0; j < blocks_per_piece; ++j) - TEST_CHECK(p->is_finished(piece_block(i, j))); - } - - std::vector availability_vec; - p->get_availability(availability_vec); - for (int i = 0; i < num_pieces; ++i) - { - const int avail = availability[i] - '0'; - assert(avail >= 0); - TEST_CHECK(avail == availability_vec[i]); - } - -#ifdef TORRENT_DEBUG - p->check_invariant(); -#endif - return p; -} - -bool verify_pick(boost::shared_ptr p - , std::vector const& picked, bool allow_multi_blocks = false) -{ -#ifdef TORRENT_DEBUG - p->check_invariant(); -#endif - if (!allow_multi_blocks) - { - for (std::vector::const_iterator i = picked.begin() - , end(picked.end()); i != end; ++i) - { - if (p->num_peers(*i) > 0) return false; - } - } - - // make sure there are no duplicated - std::set blocks; - std::copy(picked.begin(), picked.end() - , std::insert_iterator >(blocks, blocks.end())); - std::cerr << " verify: " << picked.size() << " " << blocks.size() << std::endl; - return picked.size() == blocks.size(); -} - -void print_pick(std::vector const& picked) -{ - for (int i = 0; i < int(picked.size()); ++i) - { - std::cout << "(" << picked[i].piece_index << ", " << picked[i].block_index << ") "; - } - std::cout << std::endl; -} - -void print_title(char const* name) -{ - std::cerr << "==== " << name << " ====\n"; -} - -std::vector pick_pieces(boost::shared_ptr const& p, char const* availability - , int num_blocks, int prefer_whole_pieces, void* peer_struct, piece_picker::piece_state_t state - , int options, std::vector const& suggested_pieces) -{ - std::vector picked; - p->pick_pieces(string2vec(availability), picked, num_blocks, prefer_whole_pieces, peer_struct - , state, options, suggested_pieces); - print_pick(picked); - TEST_CHECK(verify_pick(p, picked)); - return picked; -} - -int test_pick(boost::shared_ptr const& p, int options = piece_picker::rarest_first) -{ - const std::vector empty_vector; - std::vector picked = pick_pieces(p, "*******", 1, 0, 0 - , piece_picker::fast, options, empty_vector); - if (picked.empty()) return -1; - return picked[0].piece_index; -} - -int test_main() -{ - - int tmp1; - int tmp2; - int tmp3; - tcp::endpoint endp; - piece_picker::downloading_piece st; - policy::ipv4_peer peer_struct(endp, true, 0); - std::vector picked; - boost::shared_ptr p; - const std::vector empty_vector; - int options = piece_picker::rarest_first; - -// ======================================================== - - // test abort_download - print_title("test abort_download"); - p = setup_picker("1111111", " ", "7110000", ""); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - - p->abort_download(piece_block(piece_block(0,0))); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - - p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == true); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end()); - - p->abort_download(piece_block(0,0)); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - - p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); - p->mark_as_downloading(piece_block(0,1), &tmp1, piece_picker::fast); - p->abort_download(piece_block(0,0)); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - - p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); - p->mark_as_writing(piece_block(0,0), &tmp1); - p->write_failed(piece_block(0,0)); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - - p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); - p->mark_as_writing(piece_block(0,0), &tmp1); - p->mark_as_finished(piece_block(0,0), &tmp1); - p->abort_download(piece_block(0,0)); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) == picked.end()); - - p = setup_picker("1111111", " ", "7110000", ""); - p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); - p->mark_as_finished(piece_block(0,1), 0); - p->piece_info(0, st); - TEST_CHECK(st.requested == 1); - TEST_CHECK(st.finished == 1); - TEST_CHECK(st.state == piece_picker::fast); - p->abort_download(piece_block(0,0)); - p->piece_info(0, st); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.finished == 1); - TEST_CHECK(st.state == piece_picker::none); - picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast - , options, empty_vector); - TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(0,0)) != picked.end()); - -// ======================================================== - - // make sure the block that is picked is from piece 1, since it - // it is the piece with the lowest availability - print_title("test pick lowest availability"); - p = setup_picker("2223333", "* * * ", "", ""); - TEST_CHECK(test_pick(p) == 1); - -// ======================================================== - - // make sure pieces with equal priority and availability - // are picked at random - print_title("test random pick at same priority"); - std::map random_prio_pieces; - for (int i = 0; i < 100; ++i) - { - p = setup_picker("1111112", " ", "", ""); - ++random_prio_pieces[test_pick(p)]; - } - TEST_CHECK(random_prio_pieces.size() == 6); - for (std::map::iterator i = random_prio_pieces.begin() - , end(random_prio_pieces.end()); i != end; ++i) - std::cout << i->first << ": " << i->second << " "; - std::cout << std::endl; - -// ======================================================== - - // make sure the block that is picked is from piece 5, since it - // has the highest priority among the available pieces - print_title("test pick highest priority"); - p = setup_picker("1111111", "* * * ", "1111121", ""); - TEST_CHECK(test_pick(p) == 5); - -// ======================================================== - - print_title("test reverse rarest first"); - p = setup_picker("4179253", " ", "", ""); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, &peer_struct, piece_picker::fast - , piece_picker::rarest_first | piece_picker::reverse, empty_vector); - int expected_common_pieces[] = {3, 2, 5, 0, 6, 4, 1}; - for (int i = 0; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(expected_common_pieces[i / blocks_per_piece], i % blocks_per_piece)); - - // piece 3 should be prioritized since it's a partial - p = setup_picker("1122111", " ", "3333333", " 1 "); - TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 3); - -// ======================================================== - - // make sure the 4 blocks are picked from the same piece if - // whole pieces are preferred. Priority and availability is more - // important. Piece 1 has the lowest availability even though - // it is not a whole piece - print_title("test pick whole pieces"); - p = setup_picker("2212222", " ", "1111111", "1023460"); - picked = pick_pieces(p, "****** ", 1, 1, &peer_struct, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) == 3); - for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) - TEST_CHECK(picked[i].piece_index == 2); - - p = setup_picker("1111111", " ", "1111111", ""); - picked = pick_pieces(p, "****** ", 1, 1, &peer_struct, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) == blocks_per_piece); - for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) - TEST_CHECK(picked[i].block_index == i); - - p = setup_picker("2221222", " ", "", ""); - picked = pick_pieces(p, "*******", 1, 7, &peer_struct, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); - for (int i = 0; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); - -// ======================================================== - - // test the distributed copies function. It should include ourself - // in the availability. i.e. piece 0 has availability 2. - // there are 2 pieces with availability 2 and 5 with availability 3 - print_title("test distributed copies"); - p = setup_picker("1233333", "* ", "", ""); - std::pair dc = p->distributed_copies(); - TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); - -// ======================================================== - - // make sure filtered pieces are ignored - print_title("test filtered pieces"); - p = setup_picker("1111111", " ", "0010000", ""); - TEST_CHECK(test_pick(p, piece_picker::rarest_first) == 2); - TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 2); - TEST_CHECK(test_pick(p, piece_picker::sequential) == 2); - TEST_CHECK(test_pick(p, piece_picker::sequential | piece_picker::reverse) == 2); - -// ======================================================== - - // make sure we_dont_have works - print_title("test we_dont_have"); - p = setup_picker("1111111", "*******", "0100000", ""); - TEST_CHECK(p->have_piece(1)); - TEST_CHECK(p->have_piece(2)); - p->we_dont_have(1); - p->we_dont_have(2); - TEST_CHECK(!p->have_piece(1)); - TEST_CHECK(!p->have_piece(2)); - picked = pick_pieces(p, "*** ** ", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - TEST_CHECK(picked.front().piece_index == 1); - -// ======================================================== - - // make sure init preserves priorities - print_title("test init"); - p = setup_picker("1111111", " ", "1111111", ""); - - TEST_CHECK(p->num_filtered() == 0); - TEST_CHECK(p->num_have_filtered() == 0); - TEST_CHECK(p->num_have() == 0); - - p->set_piece_priority(0, 0); - TEST_CHECK(p->num_filtered() == 1); - TEST_CHECK(p->num_have_filtered() == 0); - TEST_CHECK(p->num_have() == 0); - - p->we_have(0); - - TEST_CHECK(p->num_filtered() == 0); - TEST_CHECK(p->num_have_filtered() == 1); - TEST_CHECK(p->num_have() == 1); - - p->init(blocks_per_piece, blocks_per_piece, blocks_per_piece * 7); - TEST_CHECK(p->piece_priority(0) == 0); - TEST_CHECK(p->num_filtered() == 1); - TEST_CHECK(p->num_have_filtered() == 0); - TEST_CHECK(p->num_have() == 0); - -// ======================================================== - - // make sure requested blocks aren't picked - print_title("test don't pick requested blocks"); - p = setup_picker("1111111", " ", "", ""); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - piece_block first = picked.front(); - p->mark_as_downloading(picked.front(), &peer_struct, piece_picker::fast); - TEST_CHECK(p->num_peers(picked.front()) == 1); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - TEST_CHECK(picked.front() != first); - -// ======================================================== - - // make sure downloading pieces have higher priority - print_title("test downloading piece priority"); - p = setup_picker("1111111", " ", "", ""); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - first = picked.front(); - p->mark_as_downloading(picked.front(), &peer_struct, piece_picker::fast); - TEST_CHECK(p->num_peers(picked.front()) == 1); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - TEST_CHECK(picked.front() != first); - TEST_CHECK(picked.front().piece_index == first.piece_index); - -// ======================================================== - - // make sure downloading pieces closer to completion have higher priority - // piece 3 has only 1 block from being completed, and should be picked - print_title("test downloading piece order"); - p = setup_picker("1111111", " ", "", "013700f"); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast - , options | piece_picker::prioritize_partials, empty_vector); - TEST_CHECK(int(picked.size()) > 0); - TEST_CHECK(picked.front() == piece_block(3, 3)); - -// ======================================================== - - // test sequential download - print_title("test sequential download"); - p = setup_picker("7654321", " ", "", ""); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0, piece_picker::fast - , piece_picker::sequential, empty_vector); - TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); - for (int i = 0; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); - -// ======================================================== - - // test reverse sequential download - print_title("test reverse sequential download"); - p = setup_picker("7654321", " ", "", ""); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0, piece_picker::fast - , piece_picker::sequential | piece_picker::reverse, empty_vector); - TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); - for (int i = 0; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(6 - (i / blocks_per_piece), i % blocks_per_piece)); - -// ======================================================== - - // test cursors - print_title("test cursors"); - p = setup_picker("7654321", " ", "", ""); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); - p->we_have(1); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); - p->we_have(0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); - p->we_have(5); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); - p->we_have(6); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 5); - p->we_have(4); - p->we_have(3); - p->we_have(2); - TEST_CHECK(p->cursor() == 7); - TEST_CHECK(p->reverse_cursor() == 0); - p->we_dont_have(3); - TEST_CHECK(p->cursor() == 3); - TEST_CHECK(p->reverse_cursor() == 4); - - p = setup_picker("7654321", " ", "", ""); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); - p->set_piece_priority(1, 0); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); - p->set_piece_priority(0, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); - p->set_piece_priority(5, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); - p->set_piece_priority(6, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 5); - p->set_piece_priority(4, 0); - p->set_piece_priority(3, 0); - p->set_piece_priority(2, 0); - TEST_CHECK(p->cursor() == 7); - TEST_CHECK(p->reverse_cursor() == 0); - p->set_piece_priority(3, 1); - TEST_CHECK(p->cursor() == 3); - TEST_CHECK(p->reverse_cursor() == 4); - - -// ======================================================== - - // test piece priorities - print_title("test piece priorities"); - p = setup_picker("5555555", " ", "7654321", ""); - TEST_CHECK(p->num_filtered() == 0); - TEST_CHECK(p->num_have_filtered() == 0); - p->set_piece_priority(0, 0); - TEST_CHECK(p->num_filtered() == 1); - TEST_CHECK(p->num_have_filtered() == 0); - p->mark_as_finished(piece_block(0,0), 0); - p->we_have(0); - TEST_CHECK(p->num_filtered() == 0); - TEST_CHECK(p->num_have_filtered() == 1); - - p->we_dont_have(0); - p->set_piece_priority(0, 7); - - picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0 - , piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) == 7 * blocks_per_piece); - - for (int i = 0; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(i / blocks_per_piece, i % blocks_per_piece)); - - // test changing priority on a piece we have - p->we_have(0); - p->set_piece_priority(0, 0); - p->set_piece_priority(0, 1); - p->set_piece_priority(0, 0); - - std::vector prios; - p->piece_priorities(prios); - TEST_CHECK(prios.size() == 7); - int prio_comp[] = {0, 6, 5, 4, 3, 2, 1}; - TEST_CHECK(std::equal(prios.begin(), prios.end(), prio_comp)); - - std::vector filter; - p->filtered_pieces(filter); - TEST_CHECK(prios.size() == 7); - bool filter_comp[] = {true, false, false, false, false, false, false}; - TEST_CHECK(std::equal(filter.begin(), filter.end(), filter_comp)); - -// ======================================================== - - // test restore_piece - print_title("test restore piece"); - p = setup_picker("1234567", " ", "", ""); - p->mark_as_finished(piece_block(0,0), 0); - p->mark_as_finished(piece_block(0,1), 0); - p->mark_as_finished(piece_block(0,2), 0); - p->mark_as_finished(piece_block(0,3), 0); - - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 1); - TEST_CHECK(picked.front().piece_index == 1); - - p->restore_piece(0); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 1); - TEST_CHECK(picked.front().piece_index == 0); - - p->mark_as_finished(piece_block(0,0), 0); - p->mark_as_finished(piece_block(0,1), 0); - p->mark_as_finished(piece_block(0,2), 0); - p->mark_as_finished(piece_block(0,3), 0); - p->set_piece_priority(0, 0); - - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 1); - TEST_CHECK(picked.front().piece_index == 1); - - p->restore_piece(0); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 1); - TEST_CHECK(picked.front().piece_index == 1); - - p->set_piece_priority(0, 1); - picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 1); - TEST_CHECK(picked.front().piece_index == 0); - -// ======================================================== - - // test random mode - print_title("test random pick"); - p = setup_picker("1234567", " ", "1111122", ""); - std::set random_pieces; - for (int i = 0; i < 100; ++i) - random_pieces.insert(test_pick(p, 0)); - TEST_CHECK(random_pieces.size() == 7); - - random_pieces.clear(); - for (int i = 0; i < 7; ++i) - { - int piece = test_pick(p, 0); - p->we_have(piece); - random_pieces.insert(piece); - } - TEST_CHECK(random_pieces.size() == 7); - -// ======================================================== - - // make sure that blocks from a slow piece are picked - // by a fast peer if there are no other options - print_title("test downloading piece affinity"); - p = setup_picker("1111111", " ", "", ""); - p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::slow); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, 0, &tmp2 - , piece_picker::fast, options | piece_picker::speed_affinity, empty_vector); - TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end()); - // piece 2 sould be the last one (least matching piece to pick) - TEST_CHECK(picked[7 * blocks_per_piece - 2].piece_index == 2); - TEST_CHECK(picked[7 * blocks_per_piece - 3].piece_index == 2); - TEST_CHECK(picked[7 * blocks_per_piece - 4].piece_index == 2); - - // test the affinity of pieces with the same speed state - p = setup_picker("1111111", " ", "", ""); - p->mark_as_downloading(piece_block(3,2), &tmp1, piece_picker::slow); - p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::medium); - p->mark_as_downloading(piece_block(4,2), &tmp1, piece_picker::fast); - picked = pick_pieces(p, "*******", 2 * blocks_per_piece, 0, 0 - , piece_picker::fast, piece_picker::prioritize_partials | piece_picker::speed_affinity - , empty_vector); - TEST_CHECK(picked.size() == 2 * blocks_per_piece); - TEST_CHECK(picked[0].piece_index == 4); - TEST_CHECK(picked[blocks_per_piece - 1].piece_index == 2); - TEST_CHECK(picked[2 * blocks_per_piece - 2].piece_index == 3); - -// ======================================================== - - // make sure the piece picker will pick pieces that - // are already requested from other peers if it has to - print_title("test picking downloading blocks"); - p = setup_picker("1111111", " ", "", ""); - p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::fast); - p->mark_as_downloading(piece_block(1,2), &tmp1, piece_picker::slow); - - picked.clear(); - p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 - , piece_picker::fast, piece_picker::prioritize_partials, empty_vector); - TEST_CHECK(verify_pick(p, picked, true)); - print_pick(picked); - TEST_CHECK(picked.size() == 7 * blocks_per_piece); - - picked.clear(); - p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 - , piece_picker::fast, piece_picker::prioritize_partials - | piece_picker::rarest_first, empty_vector); - TEST_CHECK(verify_pick(p, picked, true)); - print_pick(picked); - TEST_CHECK(picked.size() == 7 * blocks_per_piece); - - picked.clear(); - p->pick_pieces(string2vec("*******"), picked, 7 * blocks_per_piece, 0, 0 - , piece_picker::fast, piece_picker::rarest_first, empty_vector); - TEST_CHECK(verify_pick(p, picked, true)); - print_pick(picked); - TEST_CHECK(picked.size() == 7 * blocks_per_piece); - -// ======================================================== - - // test clear_peer - print_title("test clear_peer"); - p = setup_picker("1123333", " ", "", ""); - p->mark_as_downloading(piece_block(0, 0), &tmp1, piece_picker::slow); - p->mark_as_downloading(piece_block(0, 1), &tmp2, piece_picker::slow); - p->mark_as_downloading(piece_block(0, 2), &tmp3, piece_picker::slow); - p->mark_as_downloading(piece_block(1, 1), &tmp1, piece_picker::slow); - p->mark_as_downloading(piece_block(2, 1), &tmp2, piece_picker::slow); - p->mark_as_downloading(piece_block(3, 1), &tmp3, piece_picker::slow); - - std::vector dls; - void* expected_dls1[] = {&tmp1, &tmp2, &tmp3, 0}; - void* expected_dls2[] = {0, &tmp1, 0, 0}; - void* expected_dls3[] = {0, &tmp2, 0, 0}; - void* expected_dls4[] = {0, &tmp3, 0, 0}; - void* expected_dls5[] = {&tmp1, 0, &tmp3, 0}; - p->get_downloaders(dls, 0); - TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls1)); - p->get_downloaders(dls, 1); - TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls2)); - p->get_downloaders(dls, 2); - TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls3)); - p->get_downloaders(dls, 3); - TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls4)); - - p->clear_peer(&tmp2); - p->get_downloaders(dls, 0); - TEST_CHECK(std::equal(dls.begin(), dls.end(), expected_dls5)); - -// ======================================================== - - // test have_all and have_none - print_title("test have_all and have_none"); - p = setup_picker("0123333", "* ", "", ""); - dc = p->distributed_copies(); - std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; - TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); - p->inc_refcount_all(); - dc = p->distributed_copies(); - TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); - p->dec_refcount_all(); - dc = p->distributed_copies(); - std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; - TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); - p->inc_refcount(0); - p->dec_refcount_all(); - dc = p->distributed_copies(); - std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; - TEST_CHECK(dc == std::make_pair(0, 6000 / 7)); - TEST_CHECK(test_pick(p) == 2); - -// ======================================================== - - // test have_all and have_none - print_title("test have_all and have_none with sequential download"); - p = setup_picker("0123333", "* ", "", ""); - dc = p->distributed_copies(); - std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; - TEST_CHECK(dc == std::make_pair(1, 5000 / 7)); - p->inc_refcount_all(); - dc = p->distributed_copies(); - std::cout << "distributed copies: " << dc.first << "." << (dc.second / 1000.f) << std::endl; - TEST_CHECK(dc == std::make_pair(2, 5000 / 7)); - TEST_CHECK(test_pick(p) == 1); - -// ======================================================== - - // test inc_ref and dec_ref - print_title("test inc_ref dec_ref"); - p = setup_picker("1233333", " * ", "", ""); - TEST_CHECK(test_pick(p) == 0); - - p->dec_refcount(0); - TEST_CHECK(test_pick(p) == 1); - - p->dec_refcount(4); - p->dec_refcount(4); - TEST_CHECK(test_pick(p) == 4); - - // decrease refcount on something that's not in the piece list - p->dec_refcount(5); - p->inc_refcount(5); - - bitfield bits(7); - bits.clear_all(); - bits.set_bit(0); - p->inc_refcount(bits); - bits.clear_all(); - bits.set_bit(4); - p->dec_refcount(bits); - TEST_CHECK(test_pick(p) == 0); - -// ======================================================== - - // test unverified_blocks, marking blocks and get_downloader - print_title("test unverified blocks"); - p = setup_picker("1111111", " ", "", "0300700"); - TEST_CHECK(p->unverified_blocks() == 2 + 3); - TEST_CHECK(p->get_downloader(piece_block(4, 0)) == 0); - TEST_CHECK(p->get_downloader(piece_block(4, 1)) == 0); - TEST_CHECK(p->get_downloader(piece_block(4, 2)) == 0); - TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0); - p->mark_as_downloading(piece_block(4, 3), &peer_struct, piece_picker::fast); - TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); - p->piece_info(4, st); - TEST_CHECK(st.requested == 1); - TEST_CHECK(st.writing == 0); - TEST_CHECK(st.finished == 3); - TEST_CHECK(p->unverified_blocks() == 2 + 3); - p->mark_as_writing(piece_block(4, 3), &peer_struct); - TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); - p->piece_info(4, st); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.writing == 1); - TEST_CHECK(st.finished == 3); - TEST_CHECK(p->unverified_blocks() == 2 + 3); - p->mark_as_finished(piece_block(4, 3), &peer_struct); - TEST_CHECK(p->get_downloader(piece_block(4, 3)) == &peer_struct); - p->piece_info(4, st); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.writing == 0); - TEST_CHECK(st.finished == 4); - TEST_CHECK(p->unverified_blocks() == 2 + 4); - p->we_have(4); - p->piece_info(4, st); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.writing == 0); - TEST_CHECK(st.finished == 4); - TEST_CHECK(p->get_downloader(piece_block(4, 3)) == 0); - TEST_CHECK(p->unverified_blocks() == 2); - -// ======================================================== - - // test prefer_whole_pieces - print_title("test prefer whole pieces"); - p = setup_picker("1111111", " ", "", ""); - picked = pick_pieces(p, "*******", 1, 3, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); - piece_block b = picked.front(); - for (int i = 1; i < int(picked.size()); ++i) - { - TEST_CHECK(picked[i].piece_index * blocks_per_piece + picked[i].block_index - == b.piece_index * blocks_per_piece + b.block_index + 1); - b = picked[i]; - } - - picked = pick_pieces(p, "*******", 1, 3, 0, piece_picker::fast, options, empty_vector); - TEST_CHECK(int(picked.size()) >= 3 * blocks_per_piece); - b = picked.front(); - for (int i = 1; i < int(picked.size()); ++i) - { - TEST_CHECK(picked[i].piece_index * blocks_per_piece + picked[i].block_index - == b.piece_index * blocks_per_piece + b.block_index + 1); - b = picked[i]; - } - - // make sure pieces that don't match the 'whole pieces' requirement - // are picked if there's no other choice - p = setup_picker("1111111", " ", "", ""); - p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::fast); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, 1, 0 - , piece_picker::fast, options, empty_vector); - TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1); - TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end()); - -//#error test picking with partial pieces and other peers present so that both backup_pieces and backup_pieces2 are used - -// ======================================================== - - // test parole mode - print_title("test parole mode"); - p = setup_picker("3333133", " ", "", ""); - p->mark_as_finished(piece_block(0, 0), 0); - picked = pick_pieces(p, "*******", 1, 1, 0, piece_picker::fast - , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); - TEST_CHECK(int(picked.size()) == blocks_per_piece - 1); - for (int i = 1; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(0, i + 1)); - - // make sure that the partial piece is not picked by a - // peer that is has not downloaded/requested the other blocks - picked = pick_pieces(p, "*******", 1, 1, &peer_struct, piece_picker::fast - , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); - TEST_CHECK(int(picked.size()) == blocks_per_piece); - for (int i = 1; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(4, i)); - -// ======================================================== - - // test suggested pieces - print_title("test suggested pieces"); - p = setup_picker("1111222233334444", " ", "", ""); - int v[] = {1, 5}; - std::vector suggested_pieces(v, v + 2); - - picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces); - TEST_CHECK(int(picked.size()) >= blocks_per_piece); - for (int i = 1; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(1, i)); - p->set_piece_priority(0, 0); - p->set_piece_priority(1, 0); - p->set_piece_priority(2, 0); - p->set_piece_priority(3, 0); - - picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces); - TEST_CHECK(int(picked.size()) >= blocks_per_piece); - for (int i = 1; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(5, i)); - - p = setup_picker("1111222233334444", "**** ", "", ""); - picked = pick_pieces(p, "****************", 1, 1, 0, piece_picker::fast, options, suggested_pieces); - TEST_CHECK(int(picked.size()) >= blocks_per_piece); - for (int i = 1; i < int(picked.size()); ++i) - TEST_CHECK(picked[i] == piece_block(5, i)); - -// MISSING TESTS: -// 1. abort_download -// 2. write_failed - -/* - - p.pick_pieces(peer1, picked, 1, false, 0, piece_picker::fast, true); - TEST_CHECK(int(picked.size()) == 1); - TEST_CHECK(picked.front().piece_index == 2); - - // now pick a piece from peer2. The block is supposed to be - // from piece 3, since it is the rarest piece that peer has. - picked.clear(); - p.pick_pieces(peer2, picked, 1, false, 0, piece_picker::fast, true); - TEST_CHECK(int(picked.size()) == 1); - TEST_CHECK(picked.front().piece_index == 3); - - // same thing for peer3. - - picked.clear(); - p.pick_pieces(peer3, picked, 1, false, 0, piece_picker::fast, true); - TEST_CHECK(int(picked.size()) == 1); - TEST_CHECK(picked.front().piece_index == 5); - - // now, if all peers would have piece 1 (the piece we have partially) - // it should be prioritized over picking a completely new piece. - peer3[1] = true; - p.inc_refcount(1); - - picked.clear(); - p.pick_pieces(peer3, picked, 1, false, 0, piece_picker::fast, true); - TEST_CHECK(int(picked.size()) == 1); - TEST_CHECK(picked.front().piece_index == 1); - // and the block picked should not be 0 or 2 - // since we already have those blocks - - TEST_CHECK(picked.front().block_index != 0); - TEST_CHECK(picked.front().block_index != 2); - - // now, if we mark piece 1 and block 0 in piece 2 - // as being downloaded and picks a block from peer1, - // it should pick a block from piece 2. But since - // block 0 is marked as requested from another peer, - // the piece_picker will continue to pick blocks - // until it can return at least 1 block (since we - // tell it we want one block) that is not being - // downloaded from anyone else. This is to make it - // possible for us to determine if we want to request - // the block from more than one peer. - // Both piece 1 and 2 are partial pieces, but pice - // 2 is the rarest, so that's why it is picked. - - // we have block 0 and 2 already, so we can't mark - // them as begin downloaded. - p.mark_as_downloading(piece_block(1, 1), &peer_struct, piece_picker::fast); - p.mark_as_downloading(piece_block(1, 3), &peer_struct, piece_picker::fast); - p.mark_as_downloading(piece_block(2, 0), &peer_struct, piece_picker::fast); - - std::vector const& downloads = p.get_download_queue(); - TEST_CHECK(downloads.size() == 2); - TEST_CHECK(downloads[0].index == 1); - TEST_CHECK(downloads[0].info[0].state == piece_picker::block_info::state_finished); - TEST_CHECK(downloads[0].info[1].state == piece_picker::block_info::state_requested); - TEST_CHECK(downloads[0].info[2].state == piece_picker::block_info::state_finished); - TEST_CHECK(downloads[0].info[3].state == piece_picker::block_info::state_requested); - - TEST_CHECK(downloads[1].index == 2); - TEST_CHECK(downloads[1].info[0].state == piece_picker::block_info::state_requested); - TEST_CHECK(downloads[1].info[1].state == piece_picker::block_info::state_none); - TEST_CHECK(downloads[1].info[2].state == piece_picker::block_info::state_none); - TEST_CHECK(downloads[1].info[3].state == piece_picker::block_info::state_none); - - TEST_CHECK(p.is_requested(piece_block(1, 1))); - TEST_CHECK(p.is_requested(piece_block(1, 3))); - TEST_CHECK(p.is_requested(piece_block(2, 0))); - TEST_CHECK(!p.is_requested(piece_block(2, 1))); - - picked.clear(); - p.pick_pieces(peer1, picked, 1, false, 0, piece_picker::fast, true); - TEST_CHECK(int(picked.size()) == 2); - - piece_block expected3[] = { piece_block(2, 0), piece_block(2, 1) }; - TEST_CHECK(std::equal(picked.begin() - , picked.end(), expected3)); - - // now, if we assume we're downloading at such a speed that - // we might prefer to download whole pieces at a time from - // this peer. It should not pick piece 1 or 2 (since those are - // partially selected) - - picked.clear(); - p.pick_pieces(peer1, picked, 1, true, 0, piece_picker::fast, true); - - // it will pick 4 blocks, since we said we - // wanted whole pieces. - TEST_CHECK(int(picked.size()) == 4); - - piece_block expected4[] = - { - piece_block(3, 0), piece_block(3, 1) - , piece_block(3, 2), piece_block(3, 3) - }; - - TEST_CHECK(std::equal(picked.begin() - , picked.end(), expected4)); - - // now, try the same thing, but pick as many pieces as possible - // to make sure it can still fall back on partial pieces - - picked.clear(); - p.pick_pieces(peer1, picked, 100, true, 0, piece_picker::fast, true); - - TEST_CHECK(int(picked.size()) == 12); - - piece_block expected5[] = - { - piece_block(3, 0), piece_block(3, 1) - , piece_block(3, 2), piece_block(3, 3) - , piece_block(5, 0), piece_block(5, 1) - , piece_block(5, 2), piece_block(5, 3) - , piece_block(2, 0), piece_block(2, 1) - , piece_block(2, 2), piece_block(2, 3) - }; - - TEST_CHECK(std::equal(picked.begin() - , picked.end(), expected5)); - - // now, try the same thing, but pick as many pieces as possible - // to make sure it can still fall back on partial pieces - - picked.clear(); - p.pick_pieces(peer1, picked, 100, true, &peer_struct, piece_picker::fast, true); - - TEST_CHECK(int(picked.size()) == 11); - - piece_block expected6[] = - { - piece_block(2, 1), piece_block(2, 2) - , piece_block(2, 3) - , piece_block(3, 0), piece_block(3, 1) - , piece_block(3, 2), piece_block(3, 3) - , piece_block(5, 0), piece_block(5, 1) - , piece_block(5, 2), piece_block(5, 3) - }; - - TEST_CHECK(std::equal(picked.begin() - , picked.end(), expected6)); - - // make sure the piece picker allows filtered pieces - // to become available - p.mark_as_finished(piece_block(4, 2), 0); -*/ - return 0; -} - diff --git a/libtorrent_utp/test/test_primitives.cpp b/libtorrent_utp/test/test_primitives.cpp deleted file mode 100644 index f45fc98f5..000000000 --- a/libtorrent_utp/test/test_primitives.cpp +++ /dev/null @@ -1,1407 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/magnet_uri.hpp" -#include "libtorrent/parse_url.hpp" -#include "libtorrent/http_tracker_connection.hpp" -#include "libtorrent/buffer.hpp" -#include "libtorrent/xml_parse.hpp" -#include "libtorrent/upnp.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/bitfield.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/escape_string.hpp" -#include "libtorrent/broadcast_socket.hpp" -#include "libtorrent/identify_client.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/packet_buffer.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/timestamp_history.hpp" -#include "libtorrent/enum_net.hpp" -#ifndef TORRENT_DISABLE_DHT -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/kademlia/routing_table.hpp" -#include "libtorrent/kademlia/node.hpp" -#endif -#include -#include -#include -#include - -#include "test.hpp" - -using namespace libtorrent; -using namespace boost::tuples; - -namespace libtorrent { - TORRENT_EXPORT std::string sanitize_path(std::string const& p); -} - -sha1_hash to_hash(char const* s) -{ - sha1_hash ret; - from_hex(s, 40, (char*)&ret[0]); - return ret; -} - -tuple feed_bytes(http_parser& parser, char const* str) -{ - tuple ret(0, 0, false); - tuple prev(0, 0, false); - for (int chunks = 1; chunks < 70; ++chunks) - { - ret = make_tuple(0, 0, false); - parser.reset(); - buffer::const_interval recv_buf(str, str); - for (; *str;) - { - int chunk_size = (std::min)(chunks, int(strlen(recv_buf.end))); - if (chunk_size == 0) break; - recv_buf.end += chunk_size; - int payload, protocol; - bool error = false; - tie(payload, protocol) = parser.incoming(recv_buf, error); - ret.get<0>() += payload; - ret.get<1>() += protocol; - ret.get<2>() |= error; -// std::cerr << payload << ", " << protocol << ", " << chunk_size << std::endl; - TORRENT_ASSERT(payload + protocol == chunk_size); - } - TEST_CHECK(prev == make_tuple(0, 0, false) || ret == prev); - prev = ret; - } - return ret; -} - -void parser_callback(std::string& out, int token, char const* s, char const* val) -{ - switch (token) - { - case xml_start_tag: out += "B"; break; - case xml_end_tag: out += "F"; break; - case xml_empty_tag: out += "E"; break; - case xml_declaration_tag: out += "D"; break; - case xml_comment: out += "C"; break; - case xml_string: out += "S"; break; - case xml_attribute: out += "A"; break; - case xml_parse_error: out += "P"; break; - default: TEST_CHECK(false); - } - out += s; - if (token == xml_attribute) - { - TEST_CHECK(val != 0); - out += "V"; - out += val; - } - else - { - TEST_CHECK(val == 0); - } -} - -#ifndef TORRENT_DISABLE_DHT -void add_and_replace(libtorrent::dht::node_id& dst, libtorrent::dht::node_id const& add) -{ - bool carry = false; - for (int k = 19; k >= 0; --k) - { - int sum = dst[k] + add[k] + (carry?1:0); - dst[k] = sum & 255; - carry = sum > 255; - } -} - -void node_push_back(void* userdata, libtorrent::dht::node_entry const& n) -{ - using namespace libtorrent::dht; - std::vector* nv = (std::vector*)userdata; - nv->push_back(n); -} - -void nop(void* userdata, libtorrent::dht::node_entry const& n) {} - -#endif - -char upnp_xml[] = -"" -"" -"1" -"0" -"" -"http://192.168.0.1:5678" -"" -"" -"urn:schemas-upnp-org:device:InternetGatewayDevice:1" -"" -"http://192.168.0.1:80" -"D-Link Router" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"uuid:upnp-InternetGatewayDevice-1_0-12345678900001" -"123456789001" -"" -"" -"urn:schemas-upnp-org:service:Layer3Forwarding:1" -"urn:upnp-org:serviceId:L3Forwarding1" -"/Layer3Forwarding" -"/Layer3Forwarding" -"/Layer3Forwarding.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANDevice:1" -"WANDevice" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"1" -"http://support.dlink.com" -"12345678900001" -"uuid:upnp-WANDevice-1_0-12345678900001" -"123456789001" -"" -"" -"" -"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" -"" -"urn:upnp-org:serviceId:WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANConnectionDevice:1" -"WAN Connection Device" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"1" -"http://support.dlink.com" -"12345678900001" -"uuid:upnp-WANConnectionDevice-1_0-12345678900001" -"123456789001" -"" -"" -"urn:schemas-upnp-org:service:WANIPConnection:1" -"urn:upnp-org:serviceId:WANIPConnection" -"/WANIPConnection" -"/WANIPConnection" -"/WANIPConnection.xml" -"" -"" -"" -"" -"" -"" -"" -""; - -char upnp_xml2[] = -"" -"" -"1" -"0" -"" -"http://192.168.1.1:49152" -"" -"" -"urn:schemas-upnp-org:device:InternetGatewayDevice:1" -"" -"LINKSYS WAG200G Gateway" -"LINKSYS" -"http://www.linksys.com" -"LINKSYS WAG200G Gateway" -"Wireless-G ADSL Home Gateway" -"WAG200G" -"http://www.linksys.com" -"123456789" -"uuid:8d401597-1dd2-11b2-a7d4-001ee5947cac" -"WAG200G" -"" -"" -"urn:schemas-upnp-org:service:Layer3Forwarding:1" -"urn:upnp-org:serviceId:L3Forwarding1" -"/upnp/control/L3Forwarding1" -"/upnp/event/L3Forwarding1" -"/l3frwd.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANDevice:1" -"WANDevice" -"LINKSYS" -"http://www.linksys.com/" -"Residential Gateway" -"Internet Connection Sharing" -"1" -"http://www.linksys.com/" -"0000001" -"uuid:8d401596-1dd2-11b2-a7d4-001ee5947cac" -"WAG200G" -"" -"" -"" -"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" -"" -"urn:upnp-org:serviceId:WANCommonIFC1" -"/upnp/control/WANCommonIFC1" -"/upnp/event/WANCommonIFC1" -"/cmnicfg.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANConnectionDevice:1" -"WANConnectionDevice" -"LINKSYS" -"http://www.linksys.com/" -"Residential Gateway" -"Internet Connection Sharing" -"1" -"http://www.linksys.com/" -"0000001" -"uuid:8d401597-1dd2-11b2-a7d3-001ee5947cac" -"WAG200G" -"" -"" -"" -"urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" -"" -"urn:upnp-org:serviceId:WANEthLinkC1" -"/upnp/control/WANEthLinkC1" -"/upnp/event/WANEthLinkC1" -"/wanelcfg.xml" -"" -"" -"urn:schemas-upnp-org:service:WANPPPConnection:1" -"urn:upnp-org:serviceId:WANPPPConn1" -"/upnp/control/WANPPPConn1" -"/upnp/event/WANPPPConn1" -"/pppcfg.xml" -"" -"" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:LANDevice:1" -"LANDevice" -"LINKSYS" -"http://www.linksys.com/" -"Residential Gateway" -"Residential Gateway" -"1" -"http://www.linksys.com/" -"0000001" -"uuid:8d401596-1dd2-11b2-a7d3-001ee5947cac" -"WAG200G" -"" -"" -"" -"urn:schemas-upnp-org:service:LANHostConfigManagement:1" -"" -"urn:upnp-org:serviceId:LANHostCfg1" -"/upnp/control/LANHostCfg1" -"/upnp/event/LANHostCfg1" -"/lanhostc.xml" -"" -"" -"" -"" -"http://192.168.1.1/index.htm" -"" -""; - -struct parse_state -{ - parse_state(): in_service(false) {} - 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; -}; - -namespace libtorrent -{ - // defined in torrent_info.cpp - TORRENT_EXPORT bool verify_encoding(std::string& target, bool path = true); -} - -TORRENT_EXPORT void find_control_url(int type, char const* string, parse_state& state); - -int test_main() -{ - using namespace libtorrent; - error_code ec; - int ret = 0; - - // test timestamp_history - { - timestamp_history h; - TEST_EQUAL(h.add_sample(0x32, false), 0); - TEST_EQUAL(h.base(), 0x32); - TEST_EQUAL(h.add_sample(0x33, false), 0x1); - TEST_EQUAL(h.base(), 0x32); - TEST_EQUAL(h.add_sample(0x3433, false), 0x3401); - TEST_EQUAL(h.base(), 0x32); - TEST_EQUAL(h.add_sample(0x30, false), 0); - TEST_EQUAL(h.base(), 0x30); - - // test that wrapping of the timestamp is properly handled - h.add_sample(0xfffffff3, false); - TEST_EQUAL(h.base(), 0xfffffff3); - } - - // test packet_buffer - { - packet_buffer pb; - - TEST_EQUAL(pb.capacity(), 0); - TEST_EQUAL(pb.size(), 0); - TEST_EQUAL(pb.span(), 0); - - pb.insert(123, (void*)123); - TEST_EQUAL(pb.at(123 + 16), 0); - - TEST_CHECK(pb.at(123) == (void*)123); - TEST_CHECK(pb.capacity() > 0); - TEST_EQUAL(pb.size(), 1); - TEST_EQUAL(pb.span(), 1); - TEST_EQUAL(pb.cursor(), 123); - - pb.insert(125, (void*)125); - - TEST_CHECK(pb.at(125) == (void*)125); - TEST_EQUAL(pb.size(), 2); - TEST_EQUAL(pb.span(), 3); - TEST_EQUAL(pb.cursor(), 123); - - pb.insert(500, (void*)500); - TEST_EQUAL(pb.size(), 3); - TEST_EQUAL(pb.span(), 501 - 123); - TEST_EQUAL(pb.capacity(), 512); - - TEST_CHECK(pb.remove(123) == (void*)123); - TEST_EQUAL(pb.size(), 2); - TEST_EQUAL(pb.span(), 501 - 125); - TEST_EQUAL(pb.cursor(), 125); - TEST_CHECK(pb.remove(125) == (void*)125); - TEST_EQUAL(pb.size(), 1); - TEST_EQUAL(pb.span(), 1); - TEST_EQUAL(pb.cursor(), 500); - - TEST_CHECK(pb.remove(500) == (void*)500); - TEST_EQUAL(pb.size(), 0); - TEST_EQUAL(pb.span(), 0); - - for (int i = 0; i < 0xff; ++i) - { - int index = (i + 0xfff0) & 0xffff; - pb.insert(index, (void*)(index + 1)); - fprintf(stderr, "insert: %u (mask: %x)\n", index, int(pb.capacity() - 1)); - TEST_EQUAL(pb.capacity(), 512); - if (i >= 14) - { - index = (index - 14) & 0xffff; - fprintf(stderr, "remove: %u\n", index); - TEST_CHECK(pb.remove(index) == (void*)(index + 1)); - TEST_EQUAL(pb.size(), 14); - } - } - } - - { - // test wrapping the indices - packet_buffer pb; - - TEST_EQUAL(pb.size(), 0); - - pb.insert(0xfffe, (void*)1); - TEST_CHECK(pb.at(0xfffe) == (void*)1); - - pb.insert(2, (void*)2); - TEST_CHECK(pb.at(2) == (void*)2); - - pb.remove(0xfffe); - TEST_CHECK(pb.at(0xfffe) == (void*)0); - TEST_CHECK(pb.at(2) == (void*)2); - } - - TEST_CHECK(error_code(errors::http_error).message() == "HTTP error"); - TEST_CHECK(error_code(errors::missing_file_sizes).message() == "missing or invalid 'file sizes' entry"); - TEST_CHECK(error_code(errors::unsupported_protocol_version).message() == "unsupported protocol version"); - TEST_CHECK(error_code(errors::no_i2p_router).message() == "no i2p router is set up"); - TEST_CHECK(error_code(errors::http_parse_error).message() == "Invalid HTTP header"); - TEST_CHECK(error_code(errors::error_code_max).message() == "Unknown error"); - - TEST_CHECK(errors::reserved129 == 129); - TEST_CHECK(errors::reserved159 == 159); - TEST_CHECK(errors::reserved109 == 109); - - { - // test session state load/restore - session* s = new session(fingerprint("LT",0,0,0,0), 0); - - session_settings sett; - sett.user_agent = "test"; - sett.tracker_receive_timeout = 1234; - sett.file_pool_size = 543; - sett.urlseed_wait_retry = 74; - sett.file_pool_size = 754; - sett.initial_picker_threshold = 351; - sett.upnp_ignore_nonrouters = 5326; - sett.coalesce_writes = 623; - sett.auto_scrape_interval = 753; - sett.close_redundant_connections = 245; - sett.auto_scrape_interval = 235; - sett.auto_scrape_min_interval = 62; - s->set_settings(sett); - -#ifndef TORRENT_DISABLE_DHT - dht_settings dhts; - dhts.max_peers_reply = 70; - s->set_dht_settings(dhts); -#endif -/* -#ifndef TORRENT_DISABLE_DHT - dht_settings dht_sett; - s->set_dht_settings(dht_sett); -#endif -*/ - entry session_state; - s->save_state(session_state); - - // test magnet link parsing - add_torrent_params p; - p.save_path = "."; - error_code ec; - const char* magnet_uri = "magnet:?xt=urn:btih:cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - "&tr=http://1&tr=http://2&tr=http://3&dn=foo&dht=127.0.0.1:43"; - torrent_handle t = add_magnet_uri(*s, magnet_uri, p, ec); - TEST_CHECK(!ec); - if (ec) fprintf(stderr, "%s\n", ec.message().c_str()); - - std::vector trackers = t.trackers(); - TEST_EQUAL(trackers.size(), 3); - if (trackers.size() > 0) - { - TEST_EQUAL(trackers[0].url, "http://1"); - fprintf(stderr, "1: %s\n", trackers[0].url.c_str()); - } - if (trackers.size() > 1) - { - TEST_EQUAL(trackers[1].url, "http://2"); - fprintf(stderr, "2: %s\n", trackers[1].url.c_str()); - } - if (trackers.size() > 2) - { - TEST_EQUAL(trackers[2].url, "http://3"); - fprintf(stderr, "3: %s\n", trackers[2].url.c_str()); - } - - TEST_EQUAL(to_hex(t.info_hash().to_string()), "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"); - - delete s; - s = new session(fingerprint("LT",0,0,0,0), 0); - - std::vector buf; - bencode(std::back_inserter(buf), session_state); - lazy_entry session_state2; - ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), session_state2, ec); - TEST_CHECK(ret == 0); - - fprintf(stderr, "session_state\n%s\n", print_entry(session_state2).c_str()); - - // make sure settings that haven't been changed from their defaults are not saved - TEST_CHECK(session_state2.dict_find("settings")->dict_find("optimistic_disk_retry") == 0); - - s->load_state(session_state2); -#define CMP_SET(x) TEST_CHECK(s->settings().x == sett.x) - - CMP_SET(user_agent); - CMP_SET(tracker_receive_timeout); - CMP_SET(file_pool_size); - CMP_SET(urlseed_wait_retry); - CMP_SET(file_pool_size); - CMP_SET(initial_picker_threshold); - CMP_SET(upnp_ignore_nonrouters); - CMP_SET(coalesce_writes); - CMP_SET(auto_scrape_interval); - CMP_SET(close_redundant_connections); - CMP_SET(auto_scrape_interval); - CMP_SET(auto_scrape_min_interval); - CMP_SET(max_peerlist_size); - CMP_SET(max_paused_peerlist_size); - CMP_SET(min_announce_interval); - CMP_SET(prioritize_partial_pieces); - CMP_SET(auto_manage_startup); - CMP_SET(rate_limit_ip_overhead); - CMP_SET(announce_to_all_trackers); - CMP_SET(announce_to_all_tiers); - CMP_SET(prefer_udp_trackers); - CMP_SET(strict_super_seeding); - CMP_SET(seeding_piece_quota); - delete s; - } - - // test path functions - TEST_EQUAL(combine_path("test1/", "test2"), "test1/test2"); -#ifdef TORRENT_WINDOWS - TEST_EQUAL(combine_path("test1\\", "test2"), "test1\\test2"); - TEST_EQUAL(combine_path("test1", "test2"), "test1\\test2"); -#else - TEST_EQUAL(combine_path("test1", "test2"), "test1/test2"); -#endif - - TEST_EQUAL(extension("blah"), ""); - TEST_EQUAL(extension("blah.exe"), ".exe"); - TEST_EQUAL(extension("blah.foo.bar"), ".bar"); - TEST_EQUAL(extension("blah.foo."), "."); - - TEST_EQUAL(filename("blah"), "blah"); - TEST_EQUAL(filename("/blah/foo/bar"), "bar"); - TEST_EQUAL(filename("/blah/foo/bar/"), "bar"); - TEST_EQUAL(filename("blah/"), "blah"); - -#ifdef TORRENT_WINDOWS - TEST_EQUAL(is_root_path("c:\\blah"), false); - TEST_EQUAL(is_root_path("c:\\"), true); - TEST_EQUAL(is_root_path("\\\\"), true); - TEST_EQUAL(is_root_path("\\\\foobar"), false); -#else - TEST_EQUAL(is_root_path("/blah"), false); - TEST_EQUAL(is_root_path("/"), true); -#endif - - // if has_parent_path() returns false - // parent_path() should return the empty string - TEST_EQUAL(parent_path("blah"), ""); - TEST_EQUAL(has_parent_path("blah"), false); - TEST_EQUAL(parent_path("/blah/foo/bar"), "/blah/foo/"); - TEST_EQUAL(has_parent_path("/blah/foo/bar"), true); - TEST_EQUAL(parent_path("/blah/foo/bar/"), "/blah/foo/"); - TEST_EQUAL(has_parent_path("/blah/foo/bar/"), true); - TEST_EQUAL(parent_path("/a"), "/"); - TEST_EQUAL(has_parent_path("/a"), true); - TEST_EQUAL(parent_path("/"), ""); - TEST_EQUAL(has_parent_path("/"), false); - TEST_EQUAL(parent_path(""), ""); - TEST_EQUAL(has_parent_path(""), false); -#ifdef TORRENT_WINDOWS - TEST_EQUAL(parent_path("\\\\"), ""); - TEST_EQUAL(has_parent_path("\\\\"), false); - TEST_EQUAL(parent_path("c:\\"), ""); - TEST_EQUAL(has_parent_path("c:\\"), false); - TEST_EQUAL(parent_path("c:\\a"), "c:\\"); - TEST_EQUAL(has_parent_path("c:\\a"), true); -#endif - -#ifdef TORRENT_WINDOWS - TEST_EQUAL(is_complete("c:\\"), true); - TEST_EQUAL(is_complete("c:\\foo\\bar"), true); - TEST_EQUAL(is_complete("\\\\foo\\bar"), true); - TEST_EQUAL(is_complete("foo/bar"), false); - TEST_EQUAL(is_complete("\\\\"), true); -#else - TEST_EQUAL(is_complete("/foo/bar"), true); - TEST_EQUAL(is_complete("foo/bar"), false); - TEST_EQUAL(is_complete("/"), true); - TEST_EQUAL(is_complete(""), false); -#endif - -#ifndef TORRENT_DISABLE_DHT - // test search_torrent_entry - - dht::search_torrent_entry ste1; - dht::search_torrent_entry ste2; - char const* ste1_tags[] = {"tag1", "tag2", "tag3", "tag4"}; - ste1.publish("ste1", ste1_tags, 4); - char const* ste11_tags[] = {"tag2", "tag3"}; - ste1.publish("ste1", ste11_tags, 2); - char const* ste2_tags[] = {"tag1", "tag2", "tag5", "tag6"}; - ste2.publish("ste2", ste2_tags, 4); - char const* ste21_tags[] = {"tag1", "tag5"}; - ste2.publish("ste2", ste21_tags, 2); - - char const* test_tags1[] = {"tag1", "tag2"}; - char const* test_tags2[] = {"tag3", "tag2"}; - int m1 = ste1.match(test_tags1, 2); - int m2 = ste2.match(test_tags1, 2); - TEST_CHECK(m1 == m2); - m1 = ste1.match(test_tags2, 2); - m2 = ste2.match(test_tags2, 2); - TEST_CHECK(m1 > m2); -#endif - - // test split_string - - char const* tags[10]; - char tags_str[] = " this is\ta test\t string\x01to be split and it cannot " - "extend over the limit of elements \t"; - ret = split_string(tags, 10, tags_str); - - TEST_CHECK(ret == 10); - TEST_CHECK(strcmp(tags[0], "this") == 0); - TEST_CHECK(strcmp(tags[1], "is") == 0); - TEST_CHECK(strcmp(tags[2], "a") == 0); - TEST_CHECK(strcmp(tags[3], "test") == 0); - TEST_CHECK(strcmp(tags[4], "string") == 0); - TEST_CHECK(strcmp(tags[5], "to") == 0); - TEST_CHECK(strcmp(tags[6], "be") == 0); - TEST_CHECK(strcmp(tags[7], "split") == 0); - TEST_CHECK(strcmp(tags[8], "and") == 0); - TEST_CHECK(strcmp(tags[9], "it") == 0); - - // test snprintf - - char msg[10]; - snprintf(msg, sizeof(msg), "too %s format string", "long"); - TEST_CHECK(strcmp(msg, "too long ") == 0); - - // test maybe_url_encode - - TEST_EQUAL(maybe_url_encode("http://test:test@abc.com/abc<>abc"), "http://test:test@abc.com:80/abc%3c%3eabc"); - TEST_EQUAL(maybe_url_encode("http://abc.com/foo bar"), "http://abc.com:80/foo%20bar"); - TEST_EQUAL(maybe_url_encode("abc"), "abc"); - TEST_EQUAL(maybe_url_encode("http://abc.com/abc"), "http://abc.com/abc"); - - // test sanitize_path - -#ifdef TORRENT_WINDOWS - TEST_EQUAL(sanitize_path("/a/b/c"), "a\\b\\c"); - TEST_EQUAL(sanitize_path("a/../c"), "a\\c"); -#else - TEST_EQUAL(sanitize_path("/a/b/c"), "a/b/c"); - TEST_EQUAL(sanitize_path("a/../c"), "a/c"); -#endif - TEST_EQUAL(sanitize_path("/.././c"), "c"); - TEST_EQUAL(sanitize_path("dev:"), ""); - TEST_EQUAL(sanitize_path("c:/b"), "b"); -#ifdef TORRENT_WINDOWS - TEST_EQUAL(sanitize_path("c:\\.\\c"), "c"); - TEST_EQUAL(sanitize_path("\\c"), "c"); -#else - TEST_EQUAL(sanitize_path("//./c"), "c"); -#endif - - // make sure the time classes have correct semantics - - TEST_EQUAL(total_milliseconds(milliseconds(100)), 100); - TEST_EQUAL(total_milliseconds(milliseconds(1)), 1); - TEST_EQUAL(total_milliseconds(seconds(1)), 1000); - - - if (supports_ipv6()) - { - // make sure the assumption we use in policy's peer list hold - std::multimap peers; - std::multimap::iterator i; - peers.insert(std::make_pair(address::from_string("::1", ec), 0)); - peers.insert(std::make_pair(address::from_string("::2", ec), 3)); - peers.insert(std::make_pair(address::from_string("::3", ec), 5)); - i = peers.find(address::from_string("::2", ec)); - TEST_CHECK(i != peers.end()); - if (i != peers.end()) - { - TEST_CHECK(i->first == address::from_string("::2", ec)); - TEST_CHECK(i->second == 3); - } - } - - // test identify_client - - TEST_CHECK(identify_client(peer_id("-AZ1234-............")) == "Azureus 1.2.3.4"); - TEST_CHECK(identify_client(peer_id("-AZ1230-............")) == "Azureus 1.2.3"); - TEST_CHECK(identify_client(peer_id("S123--..............")) == "Shadow 1.2.3"); - TEST_CHECK(identify_client(peer_id("M1-2-3--............")) == "Mainline 1.2.3"); - - // test to/from hex conversion - - char const* str = "0123456789012345678901234567890123456789"; - char bin[20]; - TEST_CHECK(from_hex(str, 40, bin)); - char hex[41]; - to_hex(bin, 20, hex); - TEST_CHECK(strcmp(hex, str) == 0); - - // test is_space - - TEST_CHECK(!is_space('C')); - TEST_CHECK(!is_space('\b')); - TEST_CHECK(!is_space('8')); - TEST_CHECK(!is_space('=')); - TEST_CHECK(is_space(' ')); - TEST_CHECK(is_space('\t')); - TEST_CHECK(is_space('\n')); - TEST_CHECK(is_space('\r')); - - // test to_lower - - TEST_CHECK(to_lower('C') == 'c'); - TEST_CHECK(to_lower('c') == 'c'); - TEST_CHECK(to_lower('-') == '-'); - TEST_CHECK(to_lower('&') == '&'); - - // test string_equal_no_case - - TEST_CHECK(string_equal_no_case("foobar", "FoobAR")); - TEST_CHECK(string_equal_no_case("foobar", "foobar")); - TEST_CHECK(!string_equal_no_case("foobar", "foobar ")); - TEST_CHECK(!string_equal_no_case("foobar", "F00")); - - // test string_begins_no_case - - TEST_CHECK(string_begins_no_case("foobar", "FoobAR --")); - TEST_CHECK(!string_begins_no_case("foobar", "F00")); - - // test itoa - - TEST_CHECK(to_string(345).elems == std::string("345")); - TEST_CHECK(to_string(-345).elems == std::string("-345")); - TEST_CHECK(to_string(0).elems == std::string("0")); - TEST_CHECK(to_string(1000000000).elems == std::string("1000000000")); - - // test url parsing - - TEST_CHECK(parse_url_components("http://foo:bar@host.com:80/path/to/file", ec) - == make_tuple("http", "foo:bar", "host.com", 80, "/path/to/file")); - - TEST_CHECK(parse_url_components("http://host.com/path/to/file", ec) - == make_tuple("http", "", "host.com", 80, "/path/to/file")); - - TEST_CHECK(parse_url_components("ftp://host.com:21/path/to/file", ec) - == make_tuple("ftp", "", "host.com", 21, "/path/to/file")); - - TEST_CHECK(parse_url_components("http://host.com/path?foo:bar@foo:", ec) - == make_tuple("http", "", "host.com", 80, "/path?foo:bar@foo:")); - - TEST_CHECK(parse_url_components("http://192.168.0.1/path/to/file", ec) - == make_tuple("http", "", "192.168.0.1", 80, "/path/to/file")); - - TEST_CHECK(parse_url_components("http://[2001:ff00::1]:42/path/to/file", ec) - == make_tuple("http", "", "[2001:ff00::1]", 42, "/path/to/file")); - - // base64 test vectors from http://www.faqs.org/rfcs/rfc4648.html - - TEST_CHECK(base64encode("") == ""); - TEST_CHECK(base64encode("f") == "Zg=="); - TEST_CHECK(base64encode("fo") == "Zm8="); - TEST_CHECK(base64encode("foo") == "Zm9v"); - TEST_CHECK(base64encode("foob") == "Zm9vYg=="); - TEST_CHECK(base64encode("fooba") == "Zm9vYmE="); - TEST_CHECK(base64encode("foobar") == "Zm9vYmFy"); - - // base32 test vectors from http://www.faqs.org/rfcs/rfc4648.html - - TEST_CHECK(base32encode("") == ""); - TEST_CHECK(base32encode("f") == "MY======"); - TEST_CHECK(base32encode("fo") == "MZXQ===="); - TEST_CHECK(base32encode("foo") == "MZXW6==="); - TEST_CHECK(base32encode("foob") == "MZXW6YQ="); - TEST_CHECK(base32encode("fooba") == "MZXW6YTB"); - TEST_CHECK(base32encode("foobar") == "MZXW6YTBOI======"); - - TEST_CHECK(base32decode("") == ""); - TEST_CHECK(base32decode("MY======") == "f"); - TEST_CHECK(base32decode("MZXQ====") == "fo"); - TEST_CHECK(base32decode("MZXW6===") == "foo"); - TEST_CHECK(base32decode("MZXW6YQ=") == "foob"); - TEST_CHECK(base32decode("MZXW6YTB") == "fooba"); - TEST_CHECK(base32decode("MZXW6YTBOI======") == "foobar"); - - TEST_CHECK(base32decode("MY") == "f"); - TEST_CHECK(base32decode("MZXW6YQ") == "foob"); - TEST_CHECK(base32decode("MZXW6YTBOI") == "foobar"); - TEST_CHECK(base32decode("mZXw6yTBO1======") == "foobar"); - - std::string test; - for (int i = 0; i < 255; ++i) - test += char(i); - - TEST_CHECK(base32decode(base32encode(test)) == test); - - // url_has_argument - - TEST_CHECK(url_has_argument("http://127.0.0.1/test", "test") == ""); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "bar") == ""); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24", "foo") == "24"); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "foo") == "24"); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "bar") == "23"); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "bar") == "23"); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "a") == "e"); - TEST_CHECK(url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "b") == ""); - - // escape_string - char const* test_string = "!@#$%^&*()-_=+/,. %?"; - TEST_EQUAL(escape_string(test_string, strlen(test_string)) - , "!%40%23%24%25%5e%26*()-_%3d%2b%2f%2c.%20%25%3f"); - - // escape_path - TEST_EQUAL(escape_path(test_string, strlen(test_string)) - , "!%40%23%24%25%5e%26*()-_%3d%2b/%2c.%20%25%3f"); - - TEST_CHECK(unescape_string(escape_path(test_string, strlen(test_string)), ec) == test_string); - TEST_CHECK(!ec); - - // need_encoding - char const* test_string2 = "!@$&()-_/,.%?"; - TEST_CHECK(need_encoding(test_string, strlen(test_string)) == true); - TEST_CHECK(need_encoding(test_string2, strlen(test_string2)) == false); - TEST_CHECK(need_encoding("\n", 1) == true); - - // maybe_url_encode - TEST_CHECK(maybe_url_encode("http://bla.com/\n") == "http://bla.com:80/%0a"); - std::cerr << maybe_url_encode("http://bla.com/\n") << std::endl; - TEST_CHECK(maybe_url_encode("?&") == "?&"); - - // unescape_string - TEST_CHECK(unescape_string(escape_string(test_string, strlen(test_string)), ec) - == test_string); - std::cerr << unescape_string(escape_string(test_string, strlen(test_string)), ec) << std::endl; - - // verify_encoding - test = "\b?filename=4"; - TEST_CHECK(!verify_encoding(test)); -#ifdef TORRENT_WINDOWS - TEST_CHECK(test == "__filename=4"); -#else - TEST_CHECK(test == "_?filename=4"); -#endif - - test = "filename=4"; - TEST_CHECK(verify_encoding(test)); - TEST_CHECK(test == "filename=4"); - - // HTTP request parser - http_parser parser; - boost::tuple received; - - received = feed_bytes(parser - , "HTTP/1.1 200 OK\r\n" - "Content-Length: 4\r\n" - "Content-Type: text/plain\r\n" - "\r\n" - "test"); - - TEST_CHECK(received == make_tuple(4, 64, false)); - TEST_CHECK(parser.finished()); - TEST_CHECK(std::equal(parser.get_body().begin, parser.get_body().end, "test")); - TEST_CHECK(parser.header("content-type") == "text/plain"); - TEST_CHECK(atoi(parser.header("content-length").c_str()) == 4); - - parser.reset(); - - TEST_CHECK(!parser.finished()); - - char const* upnp_response = - "HTTP/1.1 200 OK\r\n" - "ST:upnp:rootdevice\r\n" - "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n" - "Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc\r\n" - "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n" - "EXT:\r\n" - "Cache-Control:max-age=180\r\n" - "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; - - received = feed_bytes(parser, upnp_response); - - TEST_CHECK(received == make_tuple(0, int(strlen(upnp_response)), false)); - TEST_CHECK(parser.get_body().left() == 0); - TEST_CHECK(parser.header("st") == "upnp:rootdevice"); - TEST_CHECK(parser.header("location") - == "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc"); - TEST_CHECK(parser.header("ext") == ""); - TEST_CHECK(parser.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT"); - - parser.reset(); - TEST_CHECK(!parser.finished()); - - char const* upnp_notify = - "NOTIFY * HTTP/1.1\r\n" - "Host:239.255.255.250:1900\r\n" - "NT:urn:schemas-upnp-org:device:MediaServer:1\r\n" - "NTS:ssdp:alive\r\n" - "Location:http://10.0.1.15:2353/upnphost/udhisapi.dll?content=uuid:c17f2c31-d19b-4912-af94-651945c8a84e\r\n" - "USN:uuid:c17f0c32-d1db-4be8-ae94-25f94583026e::urn:schemas-upnp-org:device:MediaServer:1\r\n" - "Cache-Control:max-age=900\r\n" - "Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0\r\n"; - - received = feed_bytes(parser, upnp_notify); - - TEST_CHECK(received == make_tuple(0, int(strlen(upnp_notify)), false)); - TEST_CHECK(parser.method() == "notify"); - TEST_CHECK(parser.path() == "*"); - - parser.reset(); - TEST_CHECK(!parser.finished()); - - char const* bt_lsd = "BT-SEARCH * HTTP/1.1\r\n" - "Host: 239.192.152.143:6771\r\n" - "Port: 6881\r\n" - "Infohash: 12345678901234567890\r\n" - "\r\n"; - - received = feed_bytes(parser, bt_lsd); - - TEST_CHECK(received == make_tuple(0, int(strlen(bt_lsd)), false)); - TEST_CHECK(parser.method() == "bt-search"); - TEST_CHECK(parser.path() == "*"); - TEST_CHECK(atoi(parser.header("port").c_str()) == 6881); - TEST_CHECK(parser.header("infohash") == "12345678901234567890"); - - TEST_CHECK(parser.finished()); - - parser.reset(); - TEST_CHECK(!parser.finished()); - - // make sure we support trackers with incorrect line endings - char const* tracker_response = - "HTTP/1.1 200 OK\n" - "content-length: 5\n" - "content-type: test/plain\n" - "\n" - "\ntest"; - - received = feed_bytes(parser, tracker_response); - - TEST_CHECK(received == make_tuple(5, int(strlen(tracker_response) - 5), false)); - TEST_CHECK(parser.get_body().left() == 5); - - parser.reset(); - - // make sure we support content-range responses - // and that we're case insensitive - char const* web_seed_response = - "HTTP/1.1 206 OK\n" - "contEnt-rAngE: bYTes 0-4\n" - "conTent-TyPe: test/plain\n" - "\n" - "\ntest"; - - received = feed_bytes(parser, web_seed_response); - - TEST_CHECK(received == make_tuple(5, int(strlen(web_seed_response) - 5), false)); - TEST_CHECK(parser.content_range() == (std::pair(0, 4))); - TEST_CHECK(parser.content_length() == 5); - - parser.reset(); - - // make sure we support content-range responses - // and that we're case insensitive - char const* one_hundred_response = - "HTTP/1.1 100 Continue\n" - "\r\n" - "HTTP/1.1 200 OK\n" - "Content-Length: 4\r\n" - "Content-Type: test/plain\r\n" - "\r\n" - "test"; - - received = feed_bytes(parser, one_hundred_response); - - TEST_CHECK(received == make_tuple(4, int(strlen(one_hundred_response) - 4), false)); - TEST_EQUAL(parser.content_length(), 4); - - { - // test chunked encoding parser - char const chunk_header1[] = "f;this is a comment\r\n"; - size_type chunk_size; - int header_size; - bool ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + 10) - , &chunk_size, &header_size); - TEST_EQUAL(ret, false); - ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + sizeof(chunk_header1)) - , &chunk_size, &header_size); - TEST_EQUAL(ret, true); - TEST_EQUAL(chunk_size, 15); - TEST_EQUAL(header_size, sizeof(chunk_header1) - 1); - - char const chunk_header2[] = - "0;this is a comment\r\n" - "test1: foo\r\n" - "test2: bar\r\n" - "\r\n"; - - ret = parser.parse_chunk_header(buffer::const_interval(chunk_header2, chunk_header2 + sizeof(chunk_header2)) - , &chunk_size, &header_size); - TEST_EQUAL(ret, true); - TEST_EQUAL(chunk_size, 0); - TEST_EQUAL(header_size, sizeof(chunk_header2) - 1); - - TEST_EQUAL(parser.headers().find("test1")->second, "foo"); - TEST_EQUAL(parser.headers().find("test2")->second, "bar"); - } - - // test xml parser - char xml1[] = "foobar"; - std::string out1; - - xml_parse(xml1, xml1 + sizeof(xml1) - 1, boost::bind(&parser_callback - , boost::ref(out1), _1, _2, _3)); - std::cerr << out1 << std::endl; - TEST_CHECK(out1 == "BaSfooEbSbarFa"); - - char xml2[] = ""; - std::string out2; - - xml_parse(xml2, xml2 + sizeof(xml2) - 1, boost::bind(&parser_callback - , boost::ref(out2), _1, _2, _3)); - std::cerr << out2 << std::endl; - TEST_CHECK(out2 == "DxmlAversionV1.0EcAxV1AyV3BdAfooVbarFdAbooVfooCcomment"); - - char xml3[] = "foonum_pieces() == num_pieces); - - session_settings set; - set.disk_io_write_mode = set.disk_io_read_mode - = unbuffered ? session_settings::disable_os_cache_for_aligned_files - : session_settings::enable_os_cache; - - char* piece = page_aligned_allocator::malloc(piece_size); - - { // avoid having two storages use the same files - file_pool fp; - disk_buffer_pool dp(16 * 1024); - boost::scoped_ptr s( - default_storage_constructor(fs, 0, test_path, fp, std::vector())); - s->m_settings = &set; - s->m_disk_pool = &dp; - - int ret = 0; - - // write piece 1 (in slot 0) - ret = s->write(piece1, 0, 0, half); - if (ret != half) print_error(ret, s); - ret = s->write(piece1 + half, 0, half, half); - if (ret != half) print_error(ret, s); - - // test unaligned read (where the bytes are aligned) - ret = s->read(piece + 3, 0, 3, piece_size-9); - if (ret != piece_size - 9) print_error(ret, s); - TEST_CHECK(std::equal(piece+3, piece + piece_size-9, piece1+3)); - - // test unaligned read (where the bytes are not aligned) - ret = s->read(piece, 0, 3, piece_size-9); - if (ret != piece_size - 9) print_error(ret, s); - TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1+3)); - - // verify piece 1 - ret = s->read(piece, 0, 0, piece_size); - if (ret != piece_size) print_error(ret, s); - TEST_CHECK(std::equal(piece, piece + piece_size, piece1)); - - // do the same with piece 0 and 2 (in slot 1 and 2) - ret = s->write(piece0, 1, 0, piece_size); - if (ret != piece_size) print_error(ret, s); - ret = s->write(piece2, 2, 0, piece_size); - if (ret != piece_size) print_error(ret, s); - - // verify piece 0 and 2 - ret = s->read(piece, 1, 0, piece_size); - if (ret != piece_size) print_error(ret, s); - TEST_CHECK(std::equal(piece, piece + piece_size, piece0)); - - ret = s->read(piece, 2, 0, piece_size); - if (ret != piece_size) print_error(ret, s); - TEST_CHECK(std::equal(piece, piece + piece_size, piece2)); - - s->release_files(); - } - - // make sure the piece_manager can identify the pieces - { - file_pool fp; - libtorrent::asio::io_service ios; - disk_io_thread io(ios, boost::function(), fp); - boost::shared_ptr dummy(new int); - boost::intrusive_ptr pm = new piece_manager(dummy, info - , test_path, fp, io, default_storage_constructor, storage_mode, std::vector()); - libtorrent::mutex lock; - - error_code ec; - bool done = false; - lazy_entry frd; - pm->async_check_fastresume(&frd, boost::bind(&on_check_resume_data, _1, _2, &done)); - ios.reset(); - run_until(ios, done); - - done = false; - pm->async_check_files(boost::bind(&on_check_files, _1, _2, &done)); - run_until(ios, done); - - done = false; - peer_request r; - r.piece = 0; - r.start = 10; - r.length = 16 * 1024; - pm->async_read(r, boost::bind(&on_read, _1, _2, &done)); - run_until(ios, done); - - // test rename_file - remove(combine_path(test_path, "part0"), ec); - if (ec) std::cerr << "remove: " << ec.message() << std::endl; - TEST_CHECK(exists(combine_path(test_path, "temp_storage/test1.tmp"))); - TEST_CHECK(!exists(combine_path(test_path, "part0"))); - boost::function none; - done = false; - pm->async_rename_file(0, "part0", boost::bind(&signal_bool, &done, "rename_file")); - run_until(ios, done); - - TEST_CHECK(!exists(combine_path(test_path, "temp_storage/test1.tmp"))); - TEST_CHECK(!exists(combine_path(test_path, "temp_storage2"))); - TEST_CHECK(exists(combine_path(test_path, "part0"))); - - // test move_storage with two files in the root directory - TEST_CHECK(exists(combine_path(test_path, "temp_storage"))); - - done = false; - pm->async_move_storage(combine_path(test_path, "temp_storage2") - , boost::bind(on_move_storage, _1, &done, _2, combine_path(test_path, "temp_storage2"))); - run_until(ios, done); - - if (fs.num_files() > 1) - { - TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); - TEST_CHECK(exists(combine_path(test_path, "temp_storage2/temp_storage"))); - } - TEST_CHECK(exists(combine_path(test_path, "temp_storage2/part0"))); - - done = false; - pm->async_move_storage(test_path, boost::bind(on_move_storage, _1, &done, _2, test_path)); - run_until(ios, done); - - TEST_CHECK(exists(combine_path(test_path, "part0"))); - TEST_CHECK(!exists(combine_path(test_path, "temp_storage2/temp_storage"))); - TEST_CHECK(!exists(combine_path(test_path, "temp_storage2/part0"))); - - r.piece = 0; - r.start = 0; - r.length = block_size; - pm->async_read(r, boost::bind(&on_read_piece, _1, _2, piece0, block_size)); - r.piece = 1; - pm->async_read(r, boost::bind(&on_read_piece, _1, _2, piece1, block_size)); - r.piece = 2; - pm->async_read(r, boost::bind(&on_read_piece, _1, _2, piece2, block_size)); - std::cerr << "async_release_files" << std::endl; - done = false; - pm->async_release_files(boost::bind(&signal_bool, &done, "async_release_files")); - run_until(ios, done); - - std::cerr << "async_rename_file" << std::endl; - done = false; - pm->async_rename_file(0, "temp_storage/test1.tmp", boost::bind(&signal_bool, &done, "rename_file")); - run_until(ios, done); - - TEST_CHECK(!exists(combine_path(test_path, "part0"))); - TEST_CHECK(exists(combine_path(test_path, "temp_storage/test1.tmp"))); - - io.abort(); - io.join(); - remove_all(combine_path(test_path, "temp_storage2"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage2") - << "': " << ec.message() << std::endl; - remove_all(combine_path(test_path, "part0"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "part0") - << "': " << ec.message() << std::endl; - } - page_aligned_allocator::free(piece); -} - -void test_remove(std::string const& test_path, bool unbuffered) -{ - file_storage fs; - error_code ec; - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); - fs.add_file("temp_storage/test1.tmp", 8); - fs.add_file("temp_storage/folder1/test2.tmp", 8); - fs.add_file("temp_storage/folder2/test3.tmp", 0); - fs.add_file("temp_storage/_folder3/test4.tmp", 0); - fs.add_file("temp_storage/_folder3/subfolder/test5.tmp", 8); - libtorrent::create_torrent t(fs, 4, -1, 0); - - char buf_[4] = {0, 0, 0, 0}; - sha1_hash h = hasher(buf_, 4).final(); - for (int i = 0; i < 6; ++i) t.set_hash(i, h); - - std::vector buf; - bencode(std::back_inserter(buf), t.generate()); - boost::intrusive_ptr info(new torrent_info(&buf[0], buf.size(), ec)); - - session_settings set; - set.disk_io_write_mode = set.disk_io_read_mode - = unbuffered ? session_settings::disable_os_cache_for_aligned_files - : session_settings::enable_os_cache; - - file_pool fp; - disk_buffer_pool dp(16 * 1024); - boost::scoped_ptr s( - default_storage_constructor(fs, 0, test_path, fp, std::vector())); - s->m_settings = &set; - s->m_disk_pool = &dp; - - // allocate the files and create the directories - s->initialize(true); - TEST_CHECK(!s->error()); - if (s->error()) - fprintf(stderr, "%s: %s\n", s->error().message().c_str(), s->error_file().c_str()); - - TEST_CHECK(exists(combine_path(test_path, "temp_storage/_folder3/subfolder/test5.tmp"))); - TEST_CHECK(exists(combine_path(test_path, "temp_storage/folder2/test3.tmp"))); - - s->delete_files(); - - TEST_CHECK(!exists(combine_path(test_path, "temp_storage"))); -} - -namespace -{ - void check_files_fill_array(int ret, disk_io_job const& j, bool* array, bool* done) - { - std::cerr << "check_files_fill_array ret: " << ret - << " piece: " << j.piece - << " have: " << j.offset - << " str: " << j.str - << " e: " << j.error.message() - << std::endl; - - if (j.offset >= 0) array[j.offset] = true; - if (ret != piece_manager::need_full_check) - { - *done = true; - } - } -} - -void test_check_files(std::string const& test_path - , libtorrent::storage_mode_t storage_mode - , bool unbuffered) -{ - boost::intrusive_ptr info; - - error_code ec; - const int piece_size = 16 * 1024; - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - file_storage fs; - fs.add_file("temp_storage/test1.tmp", piece_size); - fs.add_file("temp_storage/test2.tmp", piece_size * 2); - fs.add_file("temp_storage/test3.tmp", piece_size); - - char piece0[piece_size]; - char piece2[piece_size]; - - std::generate(piece0, piece0 + piece_size, std::rand); - std::generate(piece2, piece2 + piece_size, std::rand); - - libtorrent::create_torrent t(fs, piece_size, -1, 0); - t.set_hash(0, hasher(piece0, piece_size).final()); - t.set_hash(1, sha1_hash(0)); - t.set_hash(2, sha1_hash(0)); - t.set_hash(3, hasher(piece2, piece_size).final()); - - create_directory(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "create_directory: " << ec.message() << std::endl; - - std::ofstream f; - f.open(combine_path(test_path, "temp_storage/test1.tmp").c_str() - , std::ios::trunc | std::ios::binary); - f.write(piece0, sizeof(piece0)); - f.close(); - f.open(combine_path(test_path, "temp_storage/test3.tmp").c_str() - , std::ios::trunc | std::ios::binary); - f.write(piece2, sizeof(piece2)); - f.close(); - - std::vector buf; - bencode(std::back_inserter(buf), t.generate()); - info = new torrent_info(&buf[0], buf.size(), ec); - - file_pool fp; - libtorrent::asio::io_service ios; - disk_io_thread io(ios, boost::function(), fp); - boost::shared_ptr dummy(new int); - boost::intrusive_ptr pm = new piece_manager(dummy, info - , test_path, fp, io, default_storage_constructor, storage_mode, std::vector()); - libtorrent::mutex lock; - - bool done = false; - lazy_entry frd; - pm->async_check_fastresume(&frd, boost::bind(&on_check_resume_data, _1, _2, &done)); - ios.reset(); - run_until(ios, done); - - bool pieces[4] = {false, false, false, false}; - done = false; - pm->async_check_files(boost::bind(&check_files_fill_array, _1, _2, pieces, &done)); - run_until(ios, done); - - TEST_EQUAL(pieces[0], true); - TEST_EQUAL(pieces[1], false); - TEST_EQUAL(pieces[2], false); - TEST_EQUAL(pieces[3], true); - io.abort(); - io.join(); -} - -void run_test(std::string const& test_path, bool unbuffered) -{ - std::cerr << "\n=== " << test_path << " ===\n" << std::endl; - - boost::intrusive_ptr info; - - { - error_code ec; - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - file_storage fs; - fs.add_file("temp_storage/test1.tmp", 17); - fs.add_file("temp_storage/test2.tmp", 612); - fs.add_file("temp_storage/test3.tmp", 0); - fs.add_file("temp_storage/test4.tmp", 0); - fs.add_file("temp_storage/test5.tmp", 3253); - fs.add_file("temp_storage/test6.tmp", 841); - const int last_file_size = 4 * piece_size - fs.total_size(); - fs.add_file("temp_storage/test7.tmp", last_file_size); - - libtorrent::create_torrent t(fs, piece_size, -1, 0); - t.set_hash(0, hasher(piece0, piece_size).final()); - t.set_hash(1, hasher(piece1, piece_size).final()); - t.set_hash(2, hasher(piece2, piece_size).final()); - - std::vector buf; - bencode(std::back_inserter(buf), t.generate()); - info = new torrent_info(&buf[0], buf.size(), ec); - std::cerr << "=== test 1 ===" << std::endl; - - run_storage_tests(info, fs, test_path, storage_mode_compact, unbuffered); - - // make sure the files have the correct size - std::string base = combine_path(test_path, "temp_storage"); - TEST_EQUAL(file_size(combine_path(base, "test1.tmp")), 17); - TEST_EQUAL(file_size(combine_path(base, "test2.tmp")), 612); - // these files should have been allocated since they are 0 sized - TEST_CHECK(exists(combine_path(base, "test3.tmp"))); - TEST_CHECK(exists(combine_path(base, "test4.tmp"))); - TEST_EQUAL(file_size(combine_path(base, "test5.tmp")), 3253); - TEST_EQUAL(file_size(combine_path(base, "test6.tmp")), 841); - TEST_EQUAL(file_size(combine_path(base, "test7.tmp")), last_file_size - piece_size); - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - } - -// ============================================== - - { - error_code ec; - file_storage fs; - fs.add_file("temp_storage/test1.tmp", 3 * piece_size); - libtorrent::create_torrent t(fs, piece_size, -1, 0); - TEST_CHECK(fs.file_path(*fs.begin()) == "temp_storage/test1.tmp"); - t.set_hash(0, hasher(piece0, piece_size).final()); - t.set_hash(1, hasher(piece1, piece_size).final()); - t.set_hash(2, hasher(piece2, piece_size).final()); - - std::vector buf; - bencode(std::back_inserter(buf), t.generate()); - info = new torrent_info(&buf[0], buf.size(), ec); - - std::cerr << "=== test 3 ===" << std::endl; - - run_storage_tests(info, fs, test_path, storage_mode_compact, unbuffered); - - TEST_EQUAL(file_size(combine_path(test_path, "temp_storage/test1.tmp")), piece_size * 3); - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - -// ============================================== - - std::cerr << "=== test 4 ===" << std::endl; - - run_storage_tests(info, fs, test_path, storage_mode_allocate, unbuffered); - - std::cerr << file_size(combine_path(test_path, "temp_storage/test1.tmp")) << std::endl; - TEST_EQUAL(file_size(combine_path(test_path, "temp_storage/test1.tmp")), 3 * piece_size); - - remove_all(combine_path(test_path, "temp_storage"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "temp_storage") - << "': " << ec.message() << std::endl; - - } - -// ============================================== - - std::cerr << "=== test 5 ===" << std::endl; - test_remove(test_path, unbuffered); - -// ============================================== - - std::cerr << "=== test 6 ===" << std::endl; - test_check_files(test_path, storage_mode_sparse, unbuffered); - test_check_files(test_path, storage_mode_compact, unbuffered); -} - -void test_fastresume(std::string const& test_path) -{ - error_code ec; - std::cout << "\n\n=== test fastresume ===" << std::endl; - remove_all(combine_path(test_path, "tmp1"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "tmp1") - << "': " << ec.message() << std::endl; - create_directory(combine_path(test_path, "tmp1"), ec); - if (ec) std::cerr << "create_directory '" << combine_path(test_path, "tmp1") - << "': " << ec.message() << std::endl; - std::ofstream file(combine_path(test_path, "tmp1/temporary").c_str()); - boost::intrusive_ptr t = ::create_torrent(&file); - file.close(); - TEST_CHECK(exists(combine_path(test_path, "tmp1/temporary"))); - - entry resume; - { - session ses(fingerprint(" ", 0,0,0,0), 0); - ses.set_alert_mask(alert::all_categories); - - error_code ec; - - add_torrent_params p; - p.ti = new torrent_info(*t); - p.save_path = combine_path(test_path, "tmp1"); - p.storage_mode = storage_mode_compact; - torrent_handle h = ses.add_torrent(p, ec); - - for (int i = 0; i < 10; ++i) - { - print_alerts(ses, "ses"); - test_sleep(1000); - torrent_status s = h.status(); - if (s.progress == 1.0f) - { - std::cout << "progress: 1.0f" << std::endl; - break; - } - } - resume = h.write_resume_data(); - ses.remove_torrent(h, session::delete_files); - } - TEST_CHECK(!exists(combine_path(test_path, "tmp1/temporary"))); -#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM - resume.print(std::cout); -#endif - - // make sure the fast resume check fails! since we removed the file - { - session ses(fingerprint(" ", 0,0,0,0), 0); - ses.set_alert_mask(alert::all_categories); - - add_torrent_params p; - p.ti = new torrent_info(*t); - p.save_path = combine_path(test_path, "tmp1"); - p.storage_mode = storage_mode_compact; - std::vector resume_buf; - bencode(std::back_inserter(resume_buf), resume); - p.resume_data = &resume_buf; - torrent_handle h = ses.add_torrent(p, ec); - - std::auto_ptr a = ses.pop_alert(); - ptime end = time_now() + seconds(20); - while (a.get() == 0 || dynamic_cast(a.get()) == 0) - { - if (ses.wait_for_alert(end - time_now()) == 0) - { - std::cerr << "wait_for_alert() expired" << std::endl; - break; - } - a = ses.pop_alert(); - assert(a.get()); - std::cerr << a->message() << std::endl; - } - TEST_CHECK(dynamic_cast(a.get()) != 0); - } - remove_all(combine_path(test_path, "tmp1"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "tmp1") - << "': " << ec.message() << std::endl; -} - -bool got_file_rename_alert(alert* a) -{ - return dynamic_cast(a) - || dynamic_cast(a); -} - -void test_rename_file_in_fastresume(std::string const& test_path) -{ - error_code ec; - std::cout << "\n\n=== test rename file in fastresume ===" << std::endl; - remove_all(combine_path(test_path, "tmp2"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "tmp2") - << "': " << ec.message() << std::endl; - create_directory(combine_path(test_path, "tmp2"), ec); - if (ec) std::cerr << "create_directory: " << ec.message() << std::endl; - std::ofstream file(combine_path(test_path, "tmp2/temporary").c_str()); - boost::intrusive_ptr t = ::create_torrent(&file); - file.close(); - TEST_CHECK(exists(combine_path(test_path, "tmp2/temporary"))); - - entry resume; - { - session ses(fingerprint(" ", 0,0,0,0), 0); - ses.set_alert_mask(alert::all_categories); - - - add_torrent_params p; - p.ti = new torrent_info(*t); - p.save_path = combine_path(test_path, "tmp2"); - p.storage_mode = storage_mode_compact; - torrent_handle h = ses.add_torrent(p, ec); - - h.rename_file(0, "testing_renamed_files"); - std::cout << "renaming file" << std::endl; - bool renamed = false; - for (int i = 0; i < 100; ++i) - { - if (print_alerts(ses, "ses", true, true, true, &got_file_rename_alert)) renamed = true; - test_sleep(1000); - torrent_status s = h.status(); - if (s.state == torrent_status::seeding && renamed) return; - } - std::cout << "stop loop" << std::endl; - torrent_status s = h.status(); - TEST_CHECK(s.state == torrent_status::seeding); - resume = h.write_resume_data(); - ses.remove_torrent(h); - } - TEST_CHECK(!exists(combine_path(test_path, "tmp2/temporary"))); - TEST_CHECK(exists(combine_path(test_path, "tmp2/testing_renamed_files"))); - TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end()); -#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM - resume.print(std::cout); -#endif - - // make sure the fast resume check succeeds, even though we renamed the file - { - session ses(fingerprint(" ", 0,0,0,0), 0); - ses.set_alert_mask(alert::all_categories); - - add_torrent_params p; - p.ti = new torrent_info(*t); - p.save_path = combine_path(test_path, "tmp2"); - p.storage_mode = storage_mode_compact; - std::vector resume_buf; - bencode(std::back_inserter(resume_buf), resume); - p.resume_data = &resume_buf; - torrent_handle h = ses.add_torrent(p, ec); - - for (int i = 0; i < 5; ++i) - { - print_alerts(ses, "ses"); - test_sleep(1000); - } - torrent_status stat = h.status(); - TEST_CHECK(stat.state == torrent_status::seeding); - - resume = h.write_resume_data(); - ses.remove_torrent(h); - } - TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end()); -#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM - resume.print(std::cout); -#endif - remove_all(combine_path(test_path, "tmp2"), ec); - if (ec) std::cerr << "remove_all '" << combine_path(test_path, "tmp2") - << "': " << ec.message() << std::endl; -} - -int test_main() -{ - - run_elevator_test(); - - // initialize test pieces - for (char* p = piece0, *end(piece0 + piece_size); p < end; ++p) - *p = rand(); - for (char* p = piece1, *end(piece1 + piece_size); p < end; ++p) - *p = rand(); - for (char* p = piece2, *end(piece2 + piece_size); p < end; ++p) - *p = rand(); - - std::vector test_paths; - char* env = std::getenv("TORRENT_TEST_PATHS"); - if (env == 0) - { - test_paths.push_back(current_working_directory()); - } - else - { - char* p = std::strtok(env, ";"); - while (p != 0) - { - test_paths.push_back(complete(p)); - p = std::strtok(0, ";"); - } - } - - std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&test_fastresume, _1)); - std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&test_rename_file_in_fastresume, _1)); - std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&run_test, _1, true)); - std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&run_test, _1, false)); - - return 0; -} - diff --git a/libtorrent_utp/test/test_swarm.cpp b/libtorrent_utp/test/test_swarm.cpp deleted file mode 100644 index 77636af41..000000000 --- a/libtorrent_utp/test/test_swarm.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/thread.hpp" -#include "libtorrent/time.hpp" -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include - -void test_swarm(bool super_seeding = false, bool strict = false, bool seed_mode = false, bool time_critical = false) -{ - using namespace libtorrent; - - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_swarm", ec); - remove_all("./tmp2_swarm", ec); - remove_all("./tmp3_swarm", ec); - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000), "0.0.0.0", 0); - session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50000, 51000), "0.0.0.0", 0); - - // this is to avoid everything finish from a single peer - // immediately. To make the swarm actually connect all - // three peers before finishing. - float rate_limit = 100000; - - session_settings settings; - settings.allow_multiple_connections_per_ip = true; - settings.ignore_limits_on_local_network = false; - settings.strict_super_seeding = strict; - - settings.upload_rate_limit = rate_limit; - ses1.set_settings(settings); - - settings.download_rate_limit = rate_limit / 2; - settings.upload_rate_limit = rate_limit; - ses2.set_settings(settings); - ses3.set_settings(settings); - -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::forced; - pes.in_enc_policy = pe_settings::forced; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); - ses3.set_pe_settings(pes); -#endif - - torrent_handle tor1; - torrent_handle tor2; - torrent_handle tor3; - - add_torrent_params p; - p.seed_mode = seed_mode; - // test using piece sizes smaller than 16kB - boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true - , false, true, "_swarm", 32 * 1024, 0, super_seeding, &p); - - int mask = alert::all_categories & ~(alert::progress_notification | alert::performance_warning); - ses1.set_alert_mask(mask); - ses2.set_alert_mask(mask); - ses3.set_alert_mask(mask); - - if (time_critical) - { - tor2.set_piece_deadline(2, 0); - tor2.set_piece_deadline(5, 1000); - tor2.set_piece_deadline(8, 2000); - } - - float sum_dl_rate2 = 0.f; - float sum_dl_rate3 = 0.f; - int count_dl_rates2 = 0; - int count_dl_rates3 = 0; - - for (int i = 0; i < 80; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - print_alerts(ses3, "ses3"); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - torrent_status st3 = tor3.status(); - - if (st2.progress < 1.f && st2.progress > 0.5f) - { - sum_dl_rate2 += st2.download_payload_rate; - ++count_dl_rates2; - } - if (st3.progress < 1.f && st3.progress > 0.5f) - { - sum_dl_rate3 += st3.download_rate; - ++count_dl_rates3; - } - - std::cerr - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << st1.num_peers << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers << " - " - << "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st3.progress * 100) << "% " - << st3.num_peers - << std::endl; - - if (st2.is_seeding && st3.is_seeding) break; - test_sleep(1000); - } - - TEST_CHECK(tor2.status().is_seeding); - TEST_CHECK(tor3.status().is_seeding); - - float average2 = sum_dl_rate2 / float(count_dl_rates2); - float average3 = sum_dl_rate3 / float(count_dl_rates3); - - std::cerr << average2 << std::endl; - std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - " - << (average3 / 1000.f) << "kB/s" << std::endl; - - if (tor2.status().is_seeding && tor3.status().is_seeding) std::cerr << "done\n"; - - // make sure the files are deleted - ses1.remove_torrent(tor1, session::delete_files); - ses2.remove_torrent(tor2, session::delete_files); - ses3.remove_torrent(tor3, session::delete_files); - - std::auto_ptr a = ses1.pop_alert(); - ptime end = time_now() + seconds(20); - while (a.get() == 0 || dynamic_cast(a.get()) == 0) - { - if (ses1.wait_for_alert(end - time_now()) == 0) - { - std::cerr << "wait_for_alert() expired" << std::endl; - break; - } - a = ses1.pop_alert(); - assert(a.get()); - std::cerr << a->message() << std::endl; - } - - TEST_CHECK(dynamic_cast(a.get()) != 0); - - // there shouldn't be any alerts generated from now on - // make sure that the timer in wait_for_alert() works - // this should time out (ret == 0) and it should take - // about 2 seconds - ptime start = time_now_hires(); - alert const* ret; - while ((ret = ses1.wait_for_alert(seconds(2)))) - { - a = ses1.pop_alert(); - std::cerr << ret->message() << std::endl; - start = time_now(); - } - TEST_CHECK(time_now_hires() - start < seconds(3)); - TEST_CHECK(time_now_hires() - start >= seconds(2)); - - TEST_CHECK(!exists("./tmp1_swarm/temporary")); - TEST_CHECK(!exists("./tmp2_swarm/temporary")); - TEST_CHECK(!exists("./tmp3_swarm/temporary")); - - remove_all("./tmp1_swarm", ec); - remove_all("./tmp2_swarm", ec); - remove_all("./tmp3_swarm", ec); -} - -int test_main() -{ - using namespace libtorrent; - - // with time critical pieces - test_swarm(false, false, false, true); - - // with seed mode - test_swarm(false, false, true); - - test_swarm(); - - // with super seeding - test_swarm(true); - - // with strict super seeding - test_swarm(true, true); - - return 0; -} - diff --git a/libtorrent_utp/test/test_threads.cpp b/libtorrent_utp/test/test_threads.cpp deleted file mode 100644 index 4f85f0a9d..000000000 --- a/libtorrent_utp/test/test_threads.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - -Copyright (c) 2010, 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/thread.hpp" -#include "test.hpp" - -using namespace libtorrent; - -void fun(condition* s, libtorrent::mutex* m, int i) -{ - fprintf(stderr, "thread %d waiting\n", i); - libtorrent::mutex::scoped_lock l(*m); - s->wait(l); - fprintf(stderr, "thread %d done\n", i); -} - -int test_main() -{ - condition cond; - libtorrent::mutex m; - std::list threads; - for (int i = 0; i < 20; ++i) - { - threads.push_back(new thread(boost::bind(&fun, &cond, &m, i))); - } - - // make sure all threads are waiting on the condition - sleep(10); - - libtorrent::mutex::scoped_lock l(m); - cond.signal_all(l); - l.unlock(); - - for (std::list::iterator i = threads.begin(); i != threads.end(); ++i) - { - (*i)->join(); - delete *i; - } - - return 0; -} - diff --git a/libtorrent_utp/test/test_torrent.cpp b/libtorrent_utp/test/test_torrent.cpp deleted file mode 100644 index d6900da62..000000000 --- a/libtorrent_utp/test/test_torrent.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/time.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/create_torrent.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/thread.hpp" -#include -#include - -#include "test.hpp" -#include "setup_transfer.hpp" - -using namespace libtorrent; - -void test_running_torrent(boost::intrusive_ptr info, size_type file_size) -{ - session ses(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 48140), "0.0.0.0", 0); - ses.set_alert_mask(alert::storage_notification); - - add_torrent_params p; - p.ti = info; - p.save_path = "."; - error_code ec; - torrent_handle h = ses.add_torrent(p, ec); - - test_sleep(500); - torrent_status st = h.status(); - - std::cout << "total_wanted: " << st.total_wanted << " : " << file_size * 3 << std::endl; - TEST_CHECK(st.total_wanted == file_size * 3); - std::cout << "total_wanted_done: " << st.total_wanted_done << " : 0" << std::endl; - TEST_CHECK(st.total_wanted_done == 0); - - std::vector prio(3, 1); - prio[0] = 0; - h.prioritize_files(prio); - - test_sleep(500); - st = h.status(); - - std::cout << "total_wanted: " << st.total_wanted << " : " << file_size * 2 << std::endl; - TEST_CHECK(st.total_wanted == file_size * 2); - std::cout << "total_wanted_done: " << st.total_wanted_done << " : 0" << std::endl; - TEST_CHECK(st.total_wanted_done == 0); - - prio[1] = 0; - h.prioritize_files(prio); - - test_sleep(500); - st = h.status(); - - std::cout << "total_wanted: " << st.total_wanted << " : " << file_size << std::endl; - TEST_CHECK(st.total_wanted == file_size); - std::cout << "total_wanted_done: " << st.total_wanted_done << " : 0" << std::endl; - TEST_CHECK(st.total_wanted_done == 0); - - if (info->num_pieces() > 0) - { - h.piece_priority(0, 1); - st = h.status(); - TEST_CHECK(st.pieces[0] == false); - std::vector piece(info->piece_length()); - for (int i = 0; i < int(piece.size()); ++i) - piece[i] = (i % 26) + 'A'; - h.add_piece(0, &piece[0]); - test_sleep(10000); - st = h.status(); - TEST_CHECK(st.pieces[0] == true); - - std::cout << "reading piece 0" << std::endl; - h.read_piece(0); - alert const* a = ses.wait_for_alert(seconds(10)); - bool passed = false; - while (a) - { - std::auto_ptr al = ses.pop_alert(); - assert(al.get()); - std::cout << " " << al->message() << std::endl; - if (read_piece_alert* rpa = dynamic_cast(al.get())) - { - std::cout << "SUCCEEDED!" << std::endl; - passed = true; - TEST_CHECK(memcmp(&piece[0], rpa->buffer.get(), piece.size()) == 0); - TEST_CHECK(rpa->size == info->piece_size(0)); - TEST_CHECK(rpa->piece == 0); - break; - } - a = ses.wait_for_alert(seconds(10)); - TEST_CHECK(a); - } - TEST_CHECK(passed); - } -} - -int test_main() -{ - { - remove("test_torrent_dir2/tmp1"); - remove("test_torrent_dir2/tmp2"); - remove("test_torrent_dir2/tmp3"); - file_storage fs; - size_type file_size = 1 * 1024 * 1024 * 1024; - fs.add_file("test_torrent_dir2/tmp1", file_size); - fs.add_file("test_torrent_dir2/tmp2", file_size); - fs.add_file("test_torrent_dir2/tmp3", file_size); - libtorrent::create_torrent t(fs, 4 * 1024 * 1024); - t.add_tracker("http://non-existing.com/announce"); - - std::vector piece(4 * 1024 * 1024); - for (int i = 0; i < int(piece.size()); ++i) - piece[i] = (i % 26) + 'A'; - - // calculate the hash for all pieces - sha1_hash ph = hasher(&piece[0], piece.size()).final(); - int num = t.num_pieces(); - TEST_CHECK(t.num_pieces() > 0); - for (int i = 0; i < num; ++i) - t.set_hash(i, ph); - - std::vector tmp; - std::back_insert_iterator > out(tmp); - bencode(out, t.generate()); - error_code ec; - boost::intrusive_ptr info(new torrent_info(&tmp[0], tmp.size(), ec)); - TEST_CHECK(info->num_pieces() > 0); - - test_running_torrent(info, file_size); - } - - { - file_storage fs; - - fs.add_file("test_torrent_dir2/tmp1", 0); - libtorrent::create_torrent t(fs, 4 * 1024 * 1024); - t.add_tracker("http://non-existing.com/announce"); - - std::vector tmp; - std::back_insert_iterator > out(tmp); - bencode(out, t.generate()); - error_code ec; - boost::intrusive_ptr info(new torrent_info(&tmp[0], tmp.size(), ec)); - test_running_torrent(info, 0); - } - - return 0; -} - - diff --git a/libtorrent_utp/test/test_trackers_extension.cpp b/libtorrent_utp/test/test_trackers_extension.cpp deleted file mode 100644 index fcd57e618..000000000 --- a/libtorrent_utp/test/test_trackers_extension.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/thread.hpp" -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include "libtorrent/extensions/metadata_transfer.hpp" -#include "libtorrent/extensions/ut_metadata.hpp" -#include "libtorrent/extensions/lt_trackers.hpp" - -using boost::tuples::ignore; - -int test_main() -{ - using namespace libtorrent; - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48130, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49130, 50000), "0.0.0.0", 0); - ses1.add_extension(create_lt_trackers_plugin); - ses2.add_extension(create_lt_trackers_plugin); - - add_torrent_params atp; - atp.info_hash = sha1_hash("12345678901234567890"); - atp.save_path = "./"; - error_code ec; - torrent_handle tor1 = ses1.add_torrent(atp, ec); - atp.tracker_url = "http://test.non-existent.com/announce"; - torrent_handle tor2 = ses2.add_torrent(atp, ec); - tor2.connect_peer(tcp::endpoint(address_v4::from_string("127.0.0.1"), ses1.listen_port())); - - for (int i = 0; i < 130; ++i) - { - // make sure this function can be called on - // torrents without metadata - print_alerts(ses1, "ses1", false, true); - print_alerts(ses2, "ses2", false, true); - - if (tor1.trackers().size() == 1) break; - test_sleep(1000); - } - - TEST_CHECK(tor1.trackers().size() == 1); - return 0; -} - diff --git a/libtorrent_utp/test/test_transfer.cpp b/libtorrent_utp/test/test_transfer.cpp deleted file mode 100644 index 9dd1ed8f1..000000000 --- a/libtorrent_utp/test/test_transfer.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/thread.hpp" -#include "libtorrent/time.hpp" -#include "libtorrent/file.hpp" -#include -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include -#include - -using namespace libtorrent; -using boost::tuples::ignore; - -// test the maximum transfer rate -void test_rate() -{ - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_transfer", ec); - remove_all("./tmp2_transfer", ec); - remove_all("./tmp1_transfer_moved", ec); - remove_all("./tmp2_transfer_moved", ec); - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48575, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49575, 50000), "0.0.0.0", 0); - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("./tmp1_transfer", ec); - std::ofstream file("./tmp1_transfer/temporary"); - boost::intrusive_ptr t = ::create_torrent(&file, 4 * 1024 * 1024, 7); - file.close(); - - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 - , true, false, true, "_transfer", 0, &t); - - ses1.set_alert_mask(alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification); - ses2.set_alert_mask(alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification); - - ptime start = time_now(); - - for (int i = 0; i < 70; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - std::cerr - << "up: \033[33m" << st1.upload_payload_rate / 1000000.f << "MB/s " - << " down: \033[32m" << st2.download_payload_rate / 1000000.f << "MB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << std::endl; - } - - if (st2.is_seeding) break; - test_sleep(100); - } - - TEST_CHECK(tor2.status().is_seeding); - - time_duration dt = time_now() - start; - - std::cerr << "downloaded " << t->total_size() << " bytes " - "in " << (total_milliseconds(dt) / 1000.f) << " seconds" << std::endl; - - std::cerr << "average download rate: " << (t->total_size() / (std::max)(total_milliseconds(dt), 1)) - << " kB/s" << std::endl; - -} - -void print_alert(std::auto_ptr) -{ - std::cout << "ses1 (alert dispatch function): "/* << a.message() */ << std::endl; -} - -// simulate a full disk -struct test_storage : storage_interface -{ - test_storage(file_storage const& fs, std::string const& p, file_pool& fp) - : m_lower_layer(default_storage_constructor(fs, 0, p, fp, std::vector())) - , m_written(0) - , m_limit(16 * 1024 * 2) - {} - - virtual bool initialize(bool allocate_files) - { return m_lower_layer->initialize(allocate_files); } - - virtual bool has_any_file() - { return m_lower_layer->has_any_file(); } - - virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) - { return m_lower_layer->readv(bufs, slot, offset, num_bufs); } - - virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) - { - int ret = m_lower_layer->writev(bufs, slot, offset, num_bufs); - if (ret > 0) m_written += ret; - if (m_written > m_limit) - { -#if BOOST_VERSION == 103500 - set_error("", error_code(boost::system::posix_error::no_space_on_device, get_posix_category())); -#elif BOOST_VERSION > 103500 - set_error("", error_code(boost::system::errc::no_space_on_device, get_posix_category())); -#else - set_error("", error_code(ENOSPC, get_posix_category())); -#endif - return -1; - } - return ret; - } - - virtual size_type physical_offset(int piece_index, int offset) - { return m_lower_layer->physical_offset(piece_index, offset); } - - virtual int read(char* buf, int slot, int offset, int size) - { return m_lower_layer->read(buf, slot, offset, size); } - - virtual int write(const char* buf, int slot, int offset, int size) - { - int ret = m_lower_layer->write(buf, slot, offset, size); - if (ret > 0) m_written += ret; - if (m_written > m_limit) - { -#if BOOST_VERSION == 103500 - set_error("", error_code(boost::system::posix_error::no_space_on_device, get_posix_category())); -#elif BOOST_VERSION > 103500 - set_error("", error_code(boost::system::errc::no_space_on_device, get_posix_category())); -#else - set_error("", error_code(ENOSPC, get_posix_category())); -#endif - return -1; - } - return ret; - } - - virtual int sparse_end(int start) const - { return m_lower_layer->sparse_end(start); } - - virtual bool move_storage(std::string const& save_path) - { return m_lower_layer->move_storage(save_path); } - - virtual bool verify_resume_data(lazy_entry const& rd, error_code& error) - { return m_lower_layer->verify_resume_data(rd, error); } - - virtual bool write_resume_data(entry& rd) const - { return m_lower_layer->write_resume_data(rd); } - - virtual bool move_slot(int src_slot, int dst_slot) - { return m_lower_layer->move_slot(src_slot, dst_slot); } - - virtual bool swap_slots(int slot1, int slot2) - { return m_lower_layer->swap_slots(slot1, slot2); } - - virtual bool swap_slots3(int slot1, int slot2, int slot3) - { return m_lower_layer->swap_slots3(slot1, slot2, slot3); } - - virtual bool release_files() { return m_lower_layer->release_files(); } - - virtual bool rename_file(int index, std::string const& new_filename) - { return m_lower_layer->rename_file(index, new_filename); } - - virtual bool delete_files() { return m_lower_layer->delete_files(); } - - virtual ~test_storage() {} - - boost::scoped_ptr m_lower_layer; - int m_written; - int m_limit; -}; - -storage_interface* test_storage_constructor(file_storage const& fs - , file_storage const*, std::string const& path, file_pool& fp, std::vector const&) -{ - return new test_storage(fs, path, fp); -} - -int tracker_responses = 0; - -bool on_alert(alert* a) -{ - if (alert_cast(a)) - ++tracker_responses; - return false; -} - -void test_transfer(int proxy_type, bool test_disk_full = false, bool test_allowed_fast = false) -{ - - char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"}; - - fprintf(stderr, "\n\n ==== TESTING %s proxy ====\n\n\n", test_name[proxy_type]); - - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_transfer", ec); - remove_all("./tmp2_transfer", ec); - remove_all("./tmp1_transfer_moved", ec); - remove_all("./tmp2_transfer_moved", ec); - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48075, 49000), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49075, 50000), "0.0.0.0", 0); - - int proxy_port = (rand() % 30000) + 10000; - if (proxy_type) - { - start_proxy(proxy_port, proxy_type); - proxy_settings ps; - ps.hostname = "127.0.0.1"; - ps.port = proxy_port; - ps.username = "testuser"; - ps.password = "testpass"; - ps.type = (proxy_settings::proxy_type)proxy_type; - ses1.set_proxy(ps); - ses2.set_proxy(ps); - } - - session_settings sett; - - if (test_allowed_fast) - { - sett.allowed_fast_set_size = 2000; - sett.unchoke_slots_limit = 0; - } - - sett.min_reconnect_time = 1; - sett.announce_to_all_trackers = true; - sett.announce_to_all_tiers = true; - // make sure we announce to both http and udp trackers - sett.prefer_udp_trackers = false; - - ses1.set_settings(sett); - ses2.set_settings(sett); - -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::disabled; - pes.in_enc_policy = pe_settings::disabled; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); -#endif - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("./tmp1_transfer", ec); - std::ofstream file("./tmp1_transfer/temporary"); - boost::intrusive_ptr t = ::create_torrent(&file, 16 * 1024, 13, false); - file.close(); - - int udp_tracker_port = start_tracker(); - int tracker_port = start_web_server(); - - char tracker_url[200]; - snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", tracker_port); - t->add_tracker(tracker_url); - - snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_tracker_port); - t->add_tracker(tracker_url); - - add_torrent_params addp(&test_storage_constructor); - - // test using piece sizes smaller than 16kB - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 - , true, false, true, "_transfer", 8 * 1024, &t, false, test_disk_full?&addp:0); - - // set half of the pieces to priority 0 - int num_pieces = tor2.get_torrent_info().num_pieces(); - std::vector priorities(num_pieces, 1); - std::fill(priorities.begin(), priorities.begin() + (num_pieces / 2), 0); - tor2.prioritize_pieces(priorities); - std::cerr << "setting priorities: "; - std::copy(priorities.begin(), priorities.end(), std::ostream_iterator(std::cerr, ", ")); - std::cerr << std::endl; - - ses1.set_alert_mask(alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification); - ses2.set_alert_mask(alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification); -// ses1.set_alert_dispatch(&print_alert); - - sett = ses2.settings(); - sett.download_rate_limit = tor2.get_torrent_info().piece_length() * 5; - ses2.set_settings(sett); - - // also test to move the storage of the downloader and the uploader - // to make sure it can handle switching paths - bool test_move_storage = false; - - tracker_responses = 0; - - for (int i = 0; i < 50; ++i) - { - print_alerts(ses1, "ses1", true, true, true, on_alert); - print_alerts(ses2, "ses2", true, true, true, on_alert); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - std::cerr - << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st1.progress * 100) << "% " - << st1.num_peers - << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers - << " cc: " << st2.connect_candidates - << std::endl; - } - - if (!test_move_storage && st2.progress > 0.25f) - { - test_move_storage = true; - tor1.move_storage("./tmp1_transfer_moved"); - tor2.move_storage("./tmp2_transfer_moved"); - std::cerr << "moving storage" << std::endl; - } - - if (test_disk_full && st2.upload_mode) - { - test_disk_full = false; - ((test_storage*)tor2.get_storage_impl())->m_limit = 16 * 1024 * 1024; - tor2.set_upload_mode(false); - continue; - } - - if (!test_disk_full && st2.is_finished) break; - - TEST_CHECK(st1.state == torrent_status::seeding - || st1.state == torrent_status::checking_files); - TEST_CHECK(st2.state == torrent_status::downloading - || (test_disk_full && !st2.error.empty())); - - test_sleep(100); - } - - // 1 announce per tracker to start - TEST_CHECK(tracker_responses >= 2); - - TEST_CHECK(!tor2.status().is_seeding); - TEST_CHECK(tor2.status().is_finished); - if (tor2.status().is_finished) - std::cerr << "torrent is finished (50% complete)" << std::endl; - - std::cerr << "force recheck" << std::endl; - tor2.force_recheck(); - - for (int i = 0; i < 50; ++i) - { - test_sleep(100); - print_alerts(ses2, "ses2"); - torrent_status st2 = tor2.status(); - if (i % 10 == 0) - std::cerr << "\033[0m" << int(st2.progress * 100) << "% " << std::endl; - if (st2.state != torrent_status::checking_files) break; - } - - std::vector priorities2 = tor2.piece_priorities(); - TEST_CHECK(std::equal(priorities.begin(), priorities.end(), priorities2.begin())); - - for (int i = 0; i < 5; ++i) - { - print_alerts(ses2, "ses2"); - torrent_status st2 = tor2.status(); -// std::cerr << "\033[0m" << int(st2.progress * 100) << "% " << std::endl; - TEST_CHECK(st2.state == torrent_status::finished); - test_sleep(100); - } - - tor2.pause(); - alert const* a = ses2.wait_for_alert(seconds(10)); - bool got_paused_alert = false; - while (a) - { - std::auto_ptr holder = ses2.pop_alert(); - std::cerr << "ses2: " << a->message() << std::endl; - if (alert_cast(a)) - { - got_paused_alert = true; - break; - } - a = ses2.wait_for_alert(seconds(10)); - } - TEST_CHECK(got_paused_alert); - - std::vector tr = tor2.trackers(); - tr.push_back(announce_entry("http://test.com/announce")); - tor2.replace_trackers(tr); - tr.clear(); - - tor2.save_resume_data(); - - std::vector resume_data; - a = ses2.wait_for_alert(seconds(10)); - while (a) - { - std::auto_ptr holder = ses2.pop_alert(); - std::cerr << "ses2: " << a->message() << std::endl; - if (alert_cast(a)) - { - bencode(std::back_inserter(resume_data) - , *alert_cast(a)->resume_data); - break; - } - a = ses2.wait_for_alert(seconds(10)); - } - TEST_CHECK(resume_data.size()); - - std::cerr << "saved resume data" << std::endl; - - ses2.remove_torrent(tor2); - - std::cerr << "removed" << std::endl; - - test_sleep(100); - - std::cout << "re-adding" << std::endl; - add_torrent_params p; - p.ti = t; - p.save_path = "./tmp2_transfer_moved"; - p.resume_data = &resume_data; - tor2 = ses2.add_torrent(p, ec); - ses2.set_alert_mask(alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification); - tor2.prioritize_pieces(priorities); - std::cout << "resetting priorities" << std::endl; - tor2.resume(); - - tr = tor2.trackers(); - TEST_CHECK(std::find_if(tr.begin(), tr.end() - , boost::bind(&announce_entry::url, _1) == "http://test.com/announce") != tr.end()); - - test_sleep(100); - - for (int i = 0; i < 5; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - TEST_CHECK(st1.state == torrent_status::seeding); - TEST_CHECK(st2.state == torrent_status::finished); - - test_sleep(100); - } - - TEST_CHECK(!tor2.status().is_seeding); - - std::fill(priorities.begin(), priorities.end(), 1); - tor2.prioritize_pieces(priorities); - std::cout << "setting priorities to 1" << std::endl; - - for (int i = 0; i < 130; ++i) - { - print_alerts(ses1, "ses1"); - print_alerts(ses2, "ses2"); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - std::cerr - << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st1.progress * 100) << "% " - << st1.num_peers - << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers - << " cc: " << st2.connect_candidates - << std::endl; - } - - if (tor2.status().is_finished) break; - - TEST_CHECK(st1.state == torrent_status::seeding); - TEST_CHECK(st2.state == torrent_status::downloading); - - test_sleep(100); - } - - TEST_CHECK(tor2.status().is_seeding); - - stop_tracker(); - stop_web_server(); - if (proxy_type) stop_proxy(proxy_port); -} - -int test_main() -{ - using namespace libtorrent; - -#ifdef NDEBUG - // test rate only makes sense in release mode - test_rate(); -#endif - - // test with all kinds of proxies - for (int i = 0; i < 6; ++i) - test_transfer(i); - - // test with a (simulated) full disk - test_transfer(0, true); - - // test allowed fast - test_transfer(0, false, true); - - error_code ec; - remove_all("./tmp1_transfer", ec); - remove_all("./tmp2_transfer", ec); - remove_all("./tmp1_transfer_moved", ec); - remove_all("./tmp2_transfer_moved", ec); - - return 0; -} - diff --git a/libtorrent_utp/test/test_upnp.cpp b/libtorrent_utp/test/test_upnp.cpp deleted file mode 100644 index 0414422e8..000000000 --- a/libtorrent_utp/test/test_upnp.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/upnp.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/socket_io.hpp" // print_endpoint -#include "libtorrent/connection_queue.hpp" -#include "test.hpp" -#include "setup_transfer.hpp" -#include -#include -#include -#include -#include - -using namespace libtorrent; - -broadcast_socket* sock = 0; -int g_port = 0; - -char upnp_xml[] = -"" -"" -"1" -"0" -"" -"http://127.0.0.1:%d" -"" -"" -"urn:schemas-upnp-org:device:InternetGatewayDevice:1" -"" -"http://192.168.0.1:80" -"D-Link Router" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"uuid:upnp-InternetGatewayDevice-1_0-12345678900001" -"123456789001" -"" -"" -"urn:schemas-upnp-org:service:Layer3Forwarding:1" -"urn:upnp-org:serviceId:L3Forwarding1" -"/Layer3Forwarding" -"/Layer3Forwarding" -"/Layer3Forwarding.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANDevice:1" -"WANDevice" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"1" -"http://support.dlink.com" -"12345678900001" -"uuid:upnp-WANDevice-1_0-12345678900001" -"123456789001" -"" -"" -"" -"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" -"" -"urn:upnp-org:serviceId:WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig" -"/WANCommonInterfaceConfig.xml" -"" -"" -"" -"" -"urn:schemas-upnp-org:device:WANConnectionDevice:1" -"WAN Connection Device" -"D-Link" -"http://www.dlink.com" -"Internet Access Router" -"D-Link Router" -"1" -"http://support.dlink.com" -"12345678900001" -"uuid:upnp-WANConnectionDevice-1_0-12345678900001" -"123456789001" -"" -"" -"urn:schemas-upnp-org:service:WANIPConnection:1" -"urn:upnp-org:serviceId:WANIPConnection" -"/WANIPConnection" -"/WANIPConnection" -"/WANIPConnection.xml" -"" -"" -"" -"" -"" -"" -"" -""; - -char soap_add_response[] = - "" - "" - ""; - -char soap_delete_response[] = - "" - "" - ""; - -void incoming_msearch(udp::endpoint const& from, char* buffer - , int size) -{ - http_parser p; - bool error = false; - p.incoming(buffer::const_interval(buffer, buffer + size), error); - if (error || !p.header_finished()) - { - std::cerr << "*** malformed HTTP from " - << print_endpoint(from) << std::endl; - return; - } - - if (p.method() != "m-search") return; - - std::cerr << "< incoming m-search from " << from << std::endl; - - char msg[] = "HTTP/1.1 200 OK\r\n" - "ST:upnp:rootdevice\r\n" - "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n" - "Location: http://127.0.0.1:%d/upnp.xml\r\n" - "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n" - "EXT:\r\n" - "Cache-Control:max-age=180\r\n" - "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n"; - - TORRENT_ASSERT(g_port != 0); - char buf[sizeof(msg) + 30]; - int len = snprintf(buf, sizeof(buf), msg, g_port); - - error_code ec; - sock->send(buf, len, ec); - - if (ec) std::cerr << "*** error sending " << ec.message() << std::endl; -} - -void log_callback(char const* err) -{ - std::cerr << "UPnP: " << err << std::endl; - //TODO: store the log and verify that some key messages are there -} - -struct callback_info -{ - int mapping; - int port; - error_code ec; - bool operator==(callback_info const& e) - { return mapping == e.mapping && port == e.port && !ec == !e.ec; } -}; - -std::list callbacks; - -void callback(int mapping, int port, error_code const& err) -{ - callback_info info = {mapping, port, err}; - callbacks.push_back(info); - std::cerr << "mapping: " << mapping << ", port: " << port - << ", error: \"" << err.message() << "\"\n"; - //TODO: store the callbacks and verify that the ports were successful -} - -int test_main() -{ - libtorrent::io_service ios; - - g_port = start_web_server(); - FILE* xml_file = fopen("upnp.xml", "w+"); - fprintf(xml_file, upnp_xml, g_port); - fclose(xml_file); - - std::ofstream xml("WANIPConnection", std::ios::trunc); - xml.write(soap_add_response, sizeof(soap_add_response)-1); - xml.close(); - - sock = new broadcast_socket(ios, udp::endpoint(address_v4::from_string("239.255.255.250"), 1900) - , &incoming_msearch); - - std::string user_agent = "test agent"; - - connection_queue cc(ios); - boost::intrusive_ptr upnp_handler = new upnp(ios, cc, address_v4::from_string("127.0.0.1") - , user_agent, &callback, &log_callback, false); - upnp_handler->discover_device(); - - libtorrent::deadline_timer timer(ios); - error_code ec; - timer.expires_from_now(seconds(10), ec); - timer.async_wait(boost::bind(&libtorrent::io_service::stop, boost::ref(ios))); - - ios.reset(); - ios.run(ec); - - int mapping1 = upnp_handler->add_mapping(upnp::tcp, 500, 500); - int mapping2 = upnp_handler->add_mapping(upnp::udp, 501, 501); - timer.expires_from_now(seconds(10), ec); - timer.async_wait(boost::bind(&libtorrent::io_service::stop, boost::ref(ios))); - - ios.reset(); - ios.run(ec); - - xml.open("WANIPConnection", std::ios::trunc); - xml.write(soap_delete_response, sizeof(soap_delete_response)-1); - xml.close(); - - std::cerr << "router: " << upnp_handler->router_model() << std::endl; - TEST_CHECK(upnp_handler->router_model() == "D-Link Router"); - upnp_handler->close(); - sock->close(); - - ios.reset(); - ios.run(ec); - - callback_info expected1 = {mapping1, 500, error_code()}; - callback_info expected2 = {mapping2, 501, error_code()}; - TEST_CHECK(std::count(callbacks.begin(), callbacks.end(), expected1) == 1); - TEST_CHECK(std::count(callbacks.begin(), callbacks.end(), expected2) == 1); - - stop_web_server(); - - delete sock; - return 0; -} - - diff --git a/libtorrent_utp/test/test_utp.cpp b/libtorrent_utp/test/test_utp.cpp deleted file mode 100644 index 22ec980a4..000000000 --- a/libtorrent_utp/test/test_utp.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/session_settings.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/thread.hpp" -#include "libtorrent/time.hpp" -#include "libtorrent/file.hpp" -#include -#include - -#include "test.hpp" -#include "setup_transfer.hpp" -#include -#include - -using namespace libtorrent; -using boost::tuples::ignore; - -void test_transfer() -{ - // in case the previous run was terminated - error_code ec; - remove_all("./tmp1_utp", ec); - remove_all("./tmp2_utp", ec); - - session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48885, 49930), "0.0.0.0", 0); - session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49885, 50930), "0.0.0.0", 0); - - session_settings sett; - - sett.enable_outgoing_tcp = false; - sett.min_reconnect_time = 1; - sett.announce_to_all_trackers = true; - sett.announce_to_all_tiers = true; - // make sure we announce to both http and udp trackers - sett.prefer_udp_trackers = false; - - // for performance testing -// sett.disable_hash_checks = true; -// sett.utp_delayed_ack = 0; - - // disable this to use regular size packets over loopback -// sett.utp_dynamic_sock_buf = false; - - ses1.set_settings(sett); - ses2.set_settings(sett); - -#ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings pes; - pes.out_enc_policy = pe_settings::disabled; - pes.in_enc_policy = pe_settings::disabled; - ses1.set_pe_settings(pes); - ses2.set_pe_settings(pes); -#endif - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("./tmp1_utp", ec); - std::ofstream file("./tmp1_utp/temporary"); - boost::intrusive_ptr t = ::create_torrent(&file, 16 * 1024, 1000, false); - file.close(); - - // for performance testing - add_torrent_params atp; -// atp.storage = &disabled_storage_constructor; - - // test using piece sizes smaller than 16kB - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0 - , true, false, true, "_utp", 8 * 1024, &t, false, &atp); - - for (int i = 0; i < 300; ++i) - { - print_alerts(ses1, "ses1", true, true, true); - print_alerts(ses2, "ses2", true, true, true); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - std::cerr - << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st1.progress * 100) << "% " - << st1.num_peers - << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers - << " cc: " << st2.connect_candidates - << std::endl; - - if (st2.is_finished) break; - - TEST_CHECK(st1.state == torrent_status::seeding - || st1.state == torrent_status::checking_files); - TEST_CHECK(st2.state == torrent_status::downloading); - - test_sleep(500); - } - - TEST_CHECK(tor1.status().is_finished); - TEST_CHECK(tor2.status().is_finished); -} - -int test_main() -{ - using namespace libtorrent; - - test_transfer(); - - error_code ec; - remove_all("./tmp1_utp", ec); - remove_all("./tmp2_utp", ec); - - return 0; -} - diff --git a/libtorrent_utp/test/test_web_seed.cpp b/libtorrent_utp/test/test_web_seed.cpp deleted file mode 100644 index 4fece66ae..000000000 --- a/libtorrent_utp/test/test_web_seed.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - -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. - -*/ - -#include "libtorrent/session.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/file_pool.hpp" -#include "libtorrent/storage.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/create_torrent.hpp" -#include "libtorrent/thread.hpp" -#include -#include -#include - -#include "test.hpp" -#include "setup_transfer.hpp" - -using namespace libtorrent; - -// proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw -void test_transfer(boost::intrusive_ptr torrent_file - , int proxy, int port, char const* protocol, bool url_seed, bool chunked_encoding) -{ - using namespace libtorrent; - - session ses(fingerprint(" ", 0,0,0,0), 0); - session_settings settings; - settings.max_queued_disk_bytes = 256 * 1024; - ses.set_settings(settings); - ses.set_alert_mask(~alert::progress_notification); - ses.listen_on(std::make_pair(51000, 52000)); - error_code ec; - remove_all("./tmp2_web_seed", ec); - - char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"}; - - fprintf(stderr, "\n\n ==== TESTING === proxy: %s ==== protocol: %s ==== seed: %s === transfer-encoding: %s\n\n\n" - , test_name[proxy], protocol, url_seed ? "URL seed" : "HTTP seed", chunked_encoding ? "chunked": "none"); - - if (proxy) - { - start_proxy(8002, proxy); - proxy_settings ps; - ps.hostname = "127.0.0.1"; - ps.port = 8002; - ps.username = "testuser"; - ps.password = "testpass"; - ps.type = (proxy_settings::proxy_type)proxy; - ses.set_proxy(ps); - } - - add_torrent_params p; - p.auto_managed = false; - p.paused = false; - p.ti = torrent_file; - p.save_path = "./tmp2_web_seed"; - p.storage_mode = storage_mode_compact; - torrent_handle th = ses.add_torrent(p, ec); - - std::vector empty; - th.replace_trackers(empty); - - const size_type total_size = torrent_file->total_size(); - - float rate_sum = 0.f; - float ses_rate_sum = 0.f; - - cache_status cs; - - for (int i = 0; i < 30; ++i) - { - torrent_status s = th.status(); - session_status ss = ses.status(); - rate_sum += s.download_payload_rate; - ses_rate_sum += ss.payload_download_rate; - - cs = ses.get_cache_status(); - if (cs.blocks_read < 1) cs.blocks_read = 1; - if (cs.blocks_written < 1) cs.blocks_written = 1; - - std::cerr << (s.progress * 100.f) << " %" - << " torrent rate: " << (s.download_rate / 1000.f) << " kB/s" - << " session rate: " << (ss.download_rate / 1000.f) << " kB/s" - << " session total: " << ss.total_payload_download - << " torrent total: " << s.total_payload_download - << " rate sum:" << ses_rate_sum - << " cache: " << cs.cache_size - << " rcache: " << cs.read_cache_size - << " buffers: " << cs.total_used_buffers - << std::endl; - - print_alerts(ses, " >> ses"); - - if (s.is_seeding /* && ss.download_rate == 0.f*/) - { - TEST_EQUAL(s.total_payload_download - s.total_redundant_bytes, total_size); - // we need to sleep here a bit to let the session sync with the torrent stats - test_sleep(1000); - TEST_EQUAL(ses.status().total_payload_download - ses.status().total_redundant_bytes - , total_size); - break; - } - test_sleep(500); - } - - TEST_EQUAL(cs.cache_size, 0); - TEST_EQUAL(cs.total_used_buffers, 0); - - std::cerr << "total_size: " << total_size - << " rate_sum: " << rate_sum - << " session_rate_sum: " << ses_rate_sum - << " session total download: " << ses.status().total_payload_download - << " torrent total download: " << th.status().total_payload_download - << " redundant: " << th.status().total_redundant_bytes - << std::endl; - - // the rates for each second should sum up to the total, with a 10% error margin -// TEST_CHECK(fabs(rate_sum - total_size) < total_size * .1f); -// TEST_CHECK(fabs(ses_rate_sum - total_size) < total_size * .1f); - - TEST_CHECK(th.status().is_seeding); - - if (proxy) stop_proxy(8002); - - TEST_CHECK(exists(combine_path("./tmp2_web_seed", torrent_file->files().file_path( - torrent_file->file_at(0))))); - remove_all("./tmp2_web_seed", ec); -} - -void save_file(char const* filename, char const* data, int size) -{ - error_code ec; - file out(filename, file::write_only, ec); - TEST_CHECK(!ec); - if (ec) - { - fprintf(stderr, "ERROR opening file '%s': %s\n", filename, ec.message().c_str()); - return; - } - file::iovec_t b = { (void*)data, size }; - out.writev(0, &b, 1, ec); - TEST_CHECK(!ec); - if (ec) - { - fprintf(stderr, "ERROR writing file '%s': %s\n", filename, ec.message().c_str()); - return; - } - -} - -sha1_hash file_hash(std::string const& name) -{ - std::vector buf; - load_file(name, buf); - if (buf.empty()) return sha1_hash(0); - hasher h(&buf[0], buf.size()); - return h.final(); -} - -// test_url_seed determines whether to use url-seed or http-seed -int run_suite(char const* protocol, bool test_url_seed, bool chunked_encoding) -{ - using namespace libtorrent; - - error_code ec; - create_directories("./tmp1_web_seed/test_torrent_dir", ec); - - file_storage fs; - std::srand(10); - int piece_size = 16; - if (test_url_seed) - { - int file_sizes[] = - { 5, 16 - 5, 16, 17, 10, 30, 30, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - ,1,1,1,1,1,1,13,65,34,75,2,3,4,5,23,9,43,4,43,6, 4}; - - char* random_data = (char*)malloc(300000); - for (int i = 0; i != sizeof(file_sizes)/sizeof(file_sizes[0]); ++i) - { - std::generate(random_data, random_data + 300000, &std::rand); - char filename[200]; - snprintf(filename, sizeof(filename), "./tmp1_web_seed/test_torrent_dir/test%d", i); - save_file(filename, random_data, file_sizes[i]); - } - - add_files(fs, "./tmp1_web_seed/test_torrent_dir"); - free(random_data); - } - else - { - piece_size = 64 * 1024; - char* random_data = (char*)malloc(64 * 1024 * 25); - std::generate(random_data, random_data + 64 * 1024 * 25, &std::rand); - save_file("./tmp1_web_seed/seed", random_data, 64 * 1024 * 25); - fs.add_file("seed", 64 * 1024 * 25); - free(random_data); - } - - int port = start_web_server(strcmp(protocol, "https") == 0, chunked_encoding); - - libtorrent::create_torrent t(fs, piece_size, 0, libtorrent::create_torrent::calculate_file_hashes); - char tmp[512]; - if (test_url_seed) - { - snprintf(tmp, sizeof(tmp), "%s://127.0.0.1:%d/tmp1_web_seed", protocol, port); - t.add_url_seed(tmp); - } - else - { - snprintf(tmp, sizeof(tmp), "http://127.0.0.1:%d/seed", port); - t.add_http_seed(tmp); - } - // calculate the hash for all pieces - set_piece_hashes(t, "./tmp1_web_seed", ec); - - if (ec) - { - fprintf(stderr, "error creating hashes for test torrent: %s\n" - , ec.message().c_str()); - TEST_CHECK(false); - return 0; - } - - std::vector buf; - bencode(std::back_inserter(buf), t.generate()); - boost::intrusive_ptr torrent_file(new torrent_info(&buf[0], buf.size(), ec)); - - // verify that the file hashes are correct - for (int i = 0; i < torrent_file->num_files(); ++i) - { - sha1_hash h1 = torrent_file->files().hash(torrent_file->file_at(i)); - sha1_hash h2 = file_hash(combine_path("./tmp1_web_seed" - , torrent_file->files().file_path(torrent_file->file_at(i)))); - fprintf(stderr, "%s: %s == %s\n" - , torrent_file->files().file_path(torrent_file->file_at(i)).c_str() - , to_hex(h1.to_string()).c_str(), to_hex(h2.to_string()).c_str()); - TEST_EQUAL(h1, h2); - } - - for (int i = 0; i < 6; ++i) - test_transfer(torrent_file, i, port, protocol, test_url_seed, chunked_encoding); - - if (test_url_seed) - { - torrent_file->rename_file(0, "./tmp2_web_seed/test_torrent_dir/renamed_test1"); - test_transfer(torrent_file, 0, port, protocol, test_url_seed, chunked_encoding); - } - - stop_web_server(); - remove_all("./tmp1_web_seed", ec); - return 0; -} - -int test_main() -{ - int ret = 0; - for (int i = 0; i < 2; ++i) - { - for (int j = 0; j < 2; ++j) - { -#ifdef TORRENT_USE_OPENSSL - run_suite("https", i, j); -#endif - run_suite("http", i, j); - } - } - return ret; -} -
-
- - -
-

libtorrent python binding

- --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
- -
-

building

-

Building the libtorrent python bindings will produce a shared library (DLL) -which is a python module that can be imported in a python program.

-
-

building using setup.py

-

There is a setup.py shipped with libtorrent that can be used on windows. -On windows the setup.py will invoke bjam and assume that you have boost -sources at $BOOST_PATH. The resulting executable is self-contained, it does -not depend any boost or libtorrent dlls.

-

On other systems, the setup.py is generated by running -./configure --enable-python-binding.

-

To build the Python bindings do:

-
    -
  1. Run:

    -
    -python setup.py build
    -
    -
  2. -
  3. As root, run:

    -
    -python setup.py install
    -
    -
  4. -
-
-
-

building using boost build

-

To set up your build environment, you need to add some settings to your -$BOOST_BUILD_PATH/user-config.jam.

-

Make sure your user config contains the following line:

-
-using python : 2.3 ;
-
-

Set the version to the version of python you have installed or want to use. If -you've installed python in a non-standard location, you have to add the prefix -path used when you installed python as a second option. Like this:

-
-using python : 2.3 : /usr ;
-
-

The bindings require at least python version 2.2.

-

For more information on how to install and set up boost-build, see the -building libtorrent section.

-

Once you have boost-build set up, you cd to the bindings/python -directory and invoke bjam with the apropriate settings. For the available -build variants, see libtorrent build options.

-

For example:

-
-$ bjam dht-support=on boost=source release link=static
-
-

On Mac OS X, this will produce the following python module:

-
-bin/darwin-4.0/release/dht-support-on/link-static/logging-none/threading-multi/libtorrent.so
-
-
-
-
-

using libtorrent in python

-

The python interface is nearly identical to the C++ interface. Please refer to -the main library reference. The main differences are:

-
-
asio::tcp::endpoint
-
The endpoint type is represented as a tuple of a string (as the address) and an int for -the port number. E.g. ('127.0.0.1', 6881) represents the localhost port 6881.
-
libtorrent::time_duration
-
The time duration is represented as a number of seconds in a regular integer.
-
-

The following functions takes a reference to a container that is filled with -entries by the function. The python equivalent of these functions instead returns -a list of entries.

-
    -
  • torrent_handle::get_peer_info
  • -
  • torrent_handle::file_progress
  • -
  • torrent_handle::get_download_queue
  • -
  • torrent_handle::piece_availability
  • -
-

create_torrent::add_node() takes two arguments, one string and one integer, -instead of a pair. The string is the address and the integer is the port.

-

For an example python program, see client.py in the bindings/python -directory.

-

A very simple example usage of the module would be something like this:

-
-import libtorrent as lt
-import time
-
-ses = lt.session()
-ses.listen_on(6881, 6891)
-
-e = lt.bdecode(open("test.torrent", 'rb').read())
-info = lt.torrent_info(e)
-
-h = ses.add_torrent(info, "./", storage_mode=storage_mode_sparse)
-
-while (not h.is_seed()):
-        s = h.status()
-
-        state_str = ['queued', 'checking', 'downloading metadata', \
-                'downloading', 'finished', 'seeding', 'allocating']
-        print '%.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d) %s' % \
-                (s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
-                s.num_peers, state_str[s.state])
-
-        time.sleep(1)
-
-
-
- -
- - -
-
- - -
- - --- - - - -
Author:Arvid Norberg, arvid@rasterbar.com
-
-

libtorrent plugins

- -

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.
  • -
-
-

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 main 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, use an internal queue, and do the actual sending in tick().

-
-
-
-

plugin interface

-

The plugin interface consists of two base classes that the plugin may -implement. These are called torrent_plugin and peer_plugin. They are -both found in the <libtorrent/extensions.hpp> header.

-

These plugins are instantiated for each torrent and possibly each peer, -respectively.

-

This is done by passing in a 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_plugin> (*)(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<torrent_plugin> 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.

-
-
-

torrent_plugin

-

The synopsis for torrent_plugin follows:

-
-struct torrent_plugin
-{
-        virtual ~torrent_plugin();
-        virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection*);
-
-        virtual void on_piece_pass(int index);
-        virtual void on_piece_failed(int index);
-
-        virtual void tick();
-
-        virtual bool on_pause();
-        virtual bool on_resume();
-
-        virtual void on_files_checked();
-};
-
-

This is the base class for a torrent_plugin. Your derived class is (if added -as an extension) instantiated for each torrent in the session. The callback -hook functions are defined as follows.

-
-

new_connection()

-
-boost::shared_ptr<peer_plugin> new_connection(peer_connection*);
-
-

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.

-
-
-

on_piece_pass() on_piece_fail()

-
-void on_piece_pass(int index);
-void on_piece_failed(int index);
-
-

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.

-
-
-

tick()

-
-void tick();
-
-

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.

-
-
-

on_pause() on_resume()

-
-bool on_pause();
-bool on_resume();
-
-

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.

-
-
-

on_files_checked()

-
-void on_files_checked();
-
-

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.

-
-
-
-

peer_plugin

-
-struct peer_plugin
-{
-        virtual ~peer_plugin();
-
-        virtual void add_handshake(entry&);
-        virtual bool on_handshake(char const* reserved_bits);
-        virtual bool on_extension_handshake(lazy_entry const& h);
-
-        virtual bool on_choke();
-        virtual bool on_unchoke();
-        virtual bool on_interested();
-        virtual bool on_not_interested();
-        virtual bool on_have(int index);
-        virtual bool on_bitfield(bitfield const& bits);
-        virtual bool on_have_all();
-        virtual bool on_have_none();
-        virtual bool on_allowed_fast(int index);
-        virtual bool on_request(peer_request const& req);
-        virtual bool on_piece(peer_request const& piece, disk_buffer_holder& buffer);
-        virtual bool on_cancel(peer_request const& req);
-        virtual bool on_reject(peer_request const& req);
-        virtual bool on_suggest(int index);
-        virtual bool on_extended(int length
-                , int msg, buffer::const_interval body);
-        virtual bool on_unknown_message(int length, int msg
-                , buffer::const_interval body);
-        virtual void on_piece_pass(int index);
-        virtual void on_piece_failed(int index);
-
-        virtual void tick();
-
-        virtual bool write_request(peer_request const& r);
-};
-
-
-
-

disk_buffer_holder

-
-struct disk_buffer_holder
-{
-        disk_buffer_holder(aux::session_impl& s, char* b);
-        ~disk_buffer_holder();
-        char* release();
-        char* buffer();
-};
-
-

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.

-
-
- -
- - -