diff --git a/library/INSTALL b/library/INSTALL new file mode 100644 index 000000000..b2248bcf0 --- /dev/null +++ b/library/INSTALL @@ -0,0 +1,32 @@ +================================= +Installation of python-libtorrent +================================= + +Compile: python setup.py build +Compile&install: sudo python setup.py install + +Dependencies to compile (written as Ubuntu packages): python, libboost-filesystem1.33.1, libboost-date-time1.33.1, libboost-program-options1.33.1, libboost-regex1.33.1, libboost-thread1.33.1, libc6-dev, zlib1g-dev + +May also depend on (not sure): python-dev, and -dev packages for all boost libs + +Note: Makefile.libtorrent-only.* are the makefiles from libtorrent, copies as it. They don't refer to python-libtorrent, and probably won't work in the current directory structure. However, they may help people know what dependencies etc. are needed + +Note: If you find mistakes here, please notify me on the project page, +http://code.google.com/p/python-libtorrent + +Note: skolnick reports that the following are needed on Debian Etch: +apt-get install python +apt-get install libboost-filesystem1.33.1 +apt-get install libboost-date-time1.33.1 +apt-get install libboost-program-options1.33.1 +apt-get install libboost-regex1.33.1 +apt-get install libboost-thread1.33.1 +apt-get install libc6-dev +apt-get install zlib1g-dev +apt-get install libboost-thread-dev +apt-get install libboost-date-time-dev +apt-get install libboost-filesystem-dev +apt-get install libboost-program-options-dev +apt-get install libboost-serialization-dev +apt-get install python-dev +apt-get install libboost-regex-dev diff --git a/library/LICENSE b/library/LICENSE new file mode 100644 index 000000000..c6d326b72 --- /dev/null +++ b/library/LICENSE @@ -0,0 +1,13 @@ +/* +Copyright: A. Zakai ('Kripken') http://6thsenseless.blogspot.com + +2006-15-9 + +This code is licensed under the terms of the GNU General Public License (GPL), +version 2 or above; See /usr/share/common-licenses/GPL , or see +http://www.fsf.org/licensing/licenses/gpl.html + +The libtorrent code is copyrighted by Arvid Norberg; see the notice in +the libtorrent files. However, to ensure no misunderstanding: the entire project +as a whole is licenced as mentioned above, under the GPL. +*/ diff --git a/library/Makefile.am b/library/Makefile.am new file mode 100644 index 000000000..8f650d061 --- /dev/null +++ b/library/Makefile.am @@ -0,0 +1,71 @@ +lib_LTLIBRARIES = libtorrent.la + +libtorrent_la_SOURCES = allocate_resources.cpp \ +entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ +storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp \ +\ +kademlia/closest_nodes.cpp \ +kademlia/dht_tracker.cpp \ +kademlia/find_data.cpp \ +kademlia/node.cpp \ +kademlia/node_id.cpp \ +kademlia/refresh.cpp \ +kademlia/routing_table.cpp \ +kademlia/rpc_manager.cpp \ +kademlia/traversal_algorithm.cpp + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + + +libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ + +AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ diff --git a/library/Makefile.in b/library/Makefile.in new file mode 100644 index 000000000..dfdb17432 --- /dev/null +++ b/library/Makefile.in @@ -0,0 +1,641 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_cxx_namespaces.m4 \ + $(top_srcdir)/m4/acx_pthread.m4 \ + $(top_srcdir)/m4/ax_boost_date-time.m4 \ + $(top_srcdir)/m4/ax_boost_filesystem.m4 \ + $(top_srcdir)/m4/ax_boost_program_options.m4 \ + $(top_srcdir)/m4/ax_boost_regex.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libtorrent_la_DEPENDENCIES = +am_libtorrent_la_OBJECTS = allocate_resources.lo entry.lo \ + escape_string.lo peer_connection.lo bt_peer_connection.lo \ + web_peer_connection.lo piece_picker.lo policy.lo session.lo \ + session_impl.lo sha1.lo stat.lo storage.lo torrent.lo \ + torrent_handle.lo torrent_info.lo tracker_manager.lo \ + http_tracker_connection.lo udp_tracker_connection.lo alert.lo \ + identify_client.lo ip_filter.lo file.lo closest_nodes.lo \ + dht_tracker.lo find_data.lo node.lo node_id.lo refresh.lo \ + routing_table.lo rpc_manager.lo traversal_algorithm.lo +libtorrent_la_OBJECTS = $(am_libtorrent_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libtorrent_la_SOURCES) +DIST_SOURCES = $(libtorrent_la_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@ +BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@ +BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@ +BOOST_REGEX_LIB = @BOOST_REGEX_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLIENT_TEST_BIN = @CLIENT_TEST_BIN@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXAMPLESDIR = @EXAMPLESDIR@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ZLIB = @ZLIB@ +ZLIBDIR = @ZLIBDIR@ +ZLIBINCL = @ZLIBINCL@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +acx_pthread_config = @acx_pthread_config@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +lib_LTLIBRARIES = libtorrent.la +libtorrent_la_SOURCES = allocate_resources.cpp \ +entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ +storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp \ +\ +kademlia/closest_nodes.cpp \ +kademlia/dht_tracker.cpp \ +kademlia/find_data.cpp \ +kademlia/node.cpp \ +kademlia/node_id.cpp \ +kademlia/refresh.cpp \ +kademlia/routing_table.cpp \ +kademlia/rpc_manager.cpp \ +kademlia/traversal_algorithm.cpp + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + +libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +AM_CXXFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS = $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libtorrent.la: $(libtorrent_la_OBJECTS) $(libtorrent_la_DEPENDENCIES) + $(CXXLINK) -rpath $(libdir) $(libtorrent_la_LDFLAGS) $(libtorrent_la_OBJECTS) $(libtorrent_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocate_resources.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closest_nodes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_tracker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routing_table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traversal_algorithm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +closest_nodes.lo: kademlia/closest_nodes.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT closest_nodes.lo -MD -MP -MF "$(DEPDIR)/closest_nodes.Tpo" -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/closest_nodes.Tpo" "$(DEPDIR)/closest_nodes.Plo"; else rm -f "$(DEPDIR)/closest_nodes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/closest_nodes.cpp' object='closest_nodes.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp + +dht_tracker.lo: kademlia/dht_tracker.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT dht_tracker.lo -MD -MP -MF "$(DEPDIR)/dht_tracker.Tpo" -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/dht_tracker.Tpo" "$(DEPDIR)/dht_tracker.Plo"; else rm -f "$(DEPDIR)/dht_tracker.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/dht_tracker.cpp' object='dht_tracker.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp + +find_data.lo: kademlia/find_data.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT find_data.lo -MD -MP -MF "$(DEPDIR)/find_data.Tpo" -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/find_data.Tpo" "$(DEPDIR)/find_data.Plo"; else rm -f "$(DEPDIR)/find_data.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/find_data.cpp' object='find_data.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp + +node.lo: kademlia/node.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node.lo -MD -MP -MF "$(DEPDIR)/node.Tpo" -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node.Tpo" "$(DEPDIR)/node.Plo"; else rm -f "$(DEPDIR)/node.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node.cpp' object='node.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp + +node_id.lo: kademlia/node_id.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node_id.lo -MD -MP -MF "$(DEPDIR)/node_id.Tpo" -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node_id.Tpo" "$(DEPDIR)/node_id.Plo"; else rm -f "$(DEPDIR)/node_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node_id.cpp' object='node_id.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp + +refresh.lo: kademlia/refresh.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT refresh.lo -MD -MP -MF "$(DEPDIR)/refresh.Tpo" -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/refresh.Tpo" "$(DEPDIR)/refresh.Plo"; else rm -f "$(DEPDIR)/refresh.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/refresh.cpp' object='refresh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp + +routing_table.lo: kademlia/routing_table.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT routing_table.lo -MD -MP -MF "$(DEPDIR)/routing_table.Tpo" -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/routing_table.Tpo" "$(DEPDIR)/routing_table.Plo"; else rm -f "$(DEPDIR)/routing_table.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/routing_table.cpp' object='routing_table.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp + +rpc_manager.lo: kademlia/rpc_manager.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rpc_manager.lo -MD -MP -MF "$(DEPDIR)/rpc_manager.Tpo" -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/rpc_manager.Tpo" "$(DEPDIR)/rpc_manager.Plo"; else rm -f "$(DEPDIR)/rpc_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/rpc_manager.cpp' object='rpc_manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp + +traversal_algorithm.lo: kademlia/traversal_algorithm.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT traversal_algorithm.lo -MD -MP -MF "$(DEPDIR)/traversal_algorithm.Tpo" -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/traversal_algorithm.Tpo" "$(DEPDIR)/traversal_algorithm.Plo"; else rm -f "$(DEPDIR)/traversal_algorithm.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/traversal_algorithm.cpp' object='traversal_algorithm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(mkdir_p) $(distdir)/../include/libtorrent $(distdir)/../include/libtorrent/aux_ + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/library/Makefile.libtorrent-only.am b/library/Makefile.libtorrent-only.am new file mode 100644 index 000000000..8f650d061 --- /dev/null +++ b/library/Makefile.libtorrent-only.am @@ -0,0 +1,71 @@ +lib_LTLIBRARIES = libtorrent.la + +libtorrent_la_SOURCES = allocate_resources.cpp \ +entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ +storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp \ +\ +kademlia/closest_nodes.cpp \ +kademlia/dht_tracker.cpp \ +kademlia/find_data.cpp \ +kademlia/node.cpp \ +kademlia/node_id.cpp \ +kademlia/refresh.cpp \ +kademlia/routing_table.cpp \ +kademlia/rpc_manager.cpp \ +kademlia/traversal_algorithm.cpp + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + + +libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ + +AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ diff --git a/library/Makefile.libtorrent-only.in b/library/Makefile.libtorrent-only.in new file mode 100644 index 000000000..dfdb17432 --- /dev/null +++ b/library/Makefile.libtorrent-only.in @@ -0,0 +1,641 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_cxx_namespaces.m4 \ + $(top_srcdir)/m4/acx_pthread.m4 \ + $(top_srcdir)/m4/ax_boost_date-time.m4 \ + $(top_srcdir)/m4/ax_boost_filesystem.m4 \ + $(top_srcdir)/m4/ax_boost_program_options.m4 \ + $(top_srcdir)/m4/ax_boost_regex.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libtorrent_la_DEPENDENCIES = +am_libtorrent_la_OBJECTS = allocate_resources.lo entry.lo \ + escape_string.lo peer_connection.lo bt_peer_connection.lo \ + web_peer_connection.lo piece_picker.lo policy.lo session.lo \ + session_impl.lo sha1.lo stat.lo storage.lo torrent.lo \ + torrent_handle.lo torrent_info.lo tracker_manager.lo \ + http_tracker_connection.lo udp_tracker_connection.lo alert.lo \ + identify_client.lo ip_filter.lo file.lo closest_nodes.lo \ + dht_tracker.lo find_data.lo node.lo node_id.lo refresh.lo \ + routing_table.lo rpc_manager.lo traversal_algorithm.lo +libtorrent_la_OBJECTS = $(am_libtorrent_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libtorrent_la_SOURCES) +DIST_SOURCES = $(libtorrent_la_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@ +BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@ +BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@ +BOOST_REGEX_LIB = @BOOST_REGEX_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLIENT_TEST_BIN = @CLIENT_TEST_BIN@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXAMPLESDIR = @EXAMPLESDIR@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ZLIB = @ZLIB@ +ZLIBDIR = @ZLIBDIR@ +ZLIBINCL = @ZLIBINCL@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +acx_pthread_config = @acx_pthread_config@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +lib_LTLIBRARIES = libtorrent.la +libtorrent_la_SOURCES = allocate_resources.cpp \ +entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ +storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp \ +\ +kademlia/closest_nodes.cpp \ +kademlia/dht_tracker.cpp \ +kademlia/find_data.cpp \ +kademlia/node.cpp \ +kademlia/node_id.cpp \ +kademlia/refresh.cpp \ +kademlia/routing_table.cpp \ +kademlia/rpc_manager.cpp \ +kademlia/traversal_algorithm.cpp + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + +libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +AM_CXXFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS = $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libtorrent.la: $(libtorrent_la_OBJECTS) $(libtorrent_la_DEPENDENCIES) + $(CXXLINK) -rpath $(libdir) $(libtorrent_la_LDFLAGS) $(libtorrent_la_OBJECTS) $(libtorrent_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocate_resources.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closest_nodes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_tracker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routing_table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traversal_algorithm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +closest_nodes.lo: kademlia/closest_nodes.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT closest_nodes.lo -MD -MP -MF "$(DEPDIR)/closest_nodes.Tpo" -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/closest_nodes.Tpo" "$(DEPDIR)/closest_nodes.Plo"; else rm -f "$(DEPDIR)/closest_nodes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/closest_nodes.cpp' object='closest_nodes.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp + +dht_tracker.lo: kademlia/dht_tracker.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT dht_tracker.lo -MD -MP -MF "$(DEPDIR)/dht_tracker.Tpo" -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/dht_tracker.Tpo" "$(DEPDIR)/dht_tracker.Plo"; else rm -f "$(DEPDIR)/dht_tracker.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/dht_tracker.cpp' object='dht_tracker.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp + +find_data.lo: kademlia/find_data.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT find_data.lo -MD -MP -MF "$(DEPDIR)/find_data.Tpo" -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/find_data.Tpo" "$(DEPDIR)/find_data.Plo"; else rm -f "$(DEPDIR)/find_data.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/find_data.cpp' object='find_data.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp + +node.lo: kademlia/node.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node.lo -MD -MP -MF "$(DEPDIR)/node.Tpo" -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node.Tpo" "$(DEPDIR)/node.Plo"; else rm -f "$(DEPDIR)/node.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node.cpp' object='node.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp + +node_id.lo: kademlia/node_id.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node_id.lo -MD -MP -MF "$(DEPDIR)/node_id.Tpo" -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node_id.Tpo" "$(DEPDIR)/node_id.Plo"; else rm -f "$(DEPDIR)/node_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node_id.cpp' object='node_id.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp + +refresh.lo: kademlia/refresh.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT refresh.lo -MD -MP -MF "$(DEPDIR)/refresh.Tpo" -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/refresh.Tpo" "$(DEPDIR)/refresh.Plo"; else rm -f "$(DEPDIR)/refresh.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/refresh.cpp' object='refresh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp + +routing_table.lo: kademlia/routing_table.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT routing_table.lo -MD -MP -MF "$(DEPDIR)/routing_table.Tpo" -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/routing_table.Tpo" "$(DEPDIR)/routing_table.Plo"; else rm -f "$(DEPDIR)/routing_table.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/routing_table.cpp' object='routing_table.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp + +rpc_manager.lo: kademlia/rpc_manager.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rpc_manager.lo -MD -MP -MF "$(DEPDIR)/rpc_manager.Tpo" -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/rpc_manager.Tpo" "$(DEPDIR)/rpc_manager.Plo"; else rm -f "$(DEPDIR)/rpc_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/rpc_manager.cpp' object='rpc_manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp + +traversal_algorithm.lo: kademlia/traversal_algorithm.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT traversal_algorithm.lo -MD -MP -MF "$(DEPDIR)/traversal_algorithm.Tpo" -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/traversal_algorithm.Tpo" "$(DEPDIR)/traversal_algorithm.Plo"; else rm -f "$(DEPDIR)/traversal_algorithm.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/traversal_algorithm.cpp' object='traversal_algorithm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(mkdir_p) $(distdir)/../include/libtorrent $(distdir)/../include/libtorrent/aux_ + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/library/README b/library/README new file mode 100644 index 000000000..1646e47e0 --- /dev/null +++ b/library/README @@ -0,0 +1,30 @@ +================= +python-libtorrent +================= + +The simplest way to use this project is to install the .deb file, +which appears in the "debs" subfolder. This was tested under +Ubuntu 6.06 (Dapper), but may work in other Debian-based systems. + +Otherwise, you may compile the code with the script 'makeit'. The +script 'installit' will install the package, so you can import it as +a module under python. + +After installation of python-libtorrent, you can install and run +Deluge, http://code.google.com/p/deluge-torrent/ + +As a simpler test, you can check whether python-libtorrent works by +by running + +python test.py + +This does a simple torrent download. Note that the torrent is for +the Ubuntu 6.06 release, and was available as of 15.9.2006. You +may need to replace the torrent with another. + +If successful, you will see test.py print the torrent status +to the screen each second. Notice in particular the download speed +and amount downloaded so far, to see if things are working. + +Note: You may need to change the port used, depending on your system. + diff --git a/library/alert.cpp b/library/alert.cpp new file mode 100755 index 000000000..781265b92 --- /dev/null +++ b/library/alert.cpp @@ -0,0 +1,124 @@ +/* + +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/alert.hpp" + +namespace libtorrent { + + alert::alert(severity_t severity, const std::string& msg) + : m_msg(msg) + , m_severity(severity) + , m_timestamp(boost::posix_time::second_clock::universal_time()) + { + } + + alert::~alert() + { + } + + boost::posix_time::ptime alert::timestamp() const + { + return m_timestamp; + } + + const std::string& alert::msg() const + { + return m_msg; + } + + alert::severity_t alert::severity() const + { + return m_severity; + } + + + + alert_manager::alert_manager() + : m_severity(alert::none) + {} + + alert_manager::~alert_manager() + { + while (!m_alerts.empty()) + { + delete m_alerts.front(); + m_alerts.pop(); + } + } + + void alert_manager::post_alert(const alert& alert_) + { + boost::mutex::scoped_lock lock(m_mutex); + if (m_severity > alert_.severity()) return; + + // the internal limit is 100 alerts + if (m_alerts.size() == 100) + { + alert* result = m_alerts.front(); + m_alerts.pop(); + delete result; + } + m_alerts.push(alert_.clone().release()); + } + + std::auto_ptr alert_manager::get() + { + boost::mutex::scoped_lock lock(m_mutex); + + assert(!m_alerts.empty()); + + alert* result = m_alerts.front(); + m_alerts.pop(); + return std::auto_ptr(result); + } + + bool alert_manager::pending() const + { + boost::mutex::scoped_lock lock(m_mutex); + + return !m_alerts.empty(); + } + + void alert_manager::set_severity(alert::severity_t severity) + { + boost::mutex::scoped_lock lock(m_mutex); + + m_severity = severity; + } + + bool alert_manager::should_post(alert::severity_t severity) const + { + return severity >= m_severity; + } + +} // namespace libtorrent + diff --git a/library/allocate_resources.cpp b/library/allocate_resources.cpp new file mode 100644 index 000000000..7d08b036d --- /dev/null +++ b/library/allocate_resources.cpp @@ -0,0 +1,201 @@ +/* + +Copyright (c) 2003, Magnus Jonsson, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +//The Standard Library defines the two template functions std::min() +//and std::max() in the header. In general, you should +//use these template functions for calculating the min and max values +//of a pair. Unfortunately, Visual C++ does not define these function +// templates. This is because the names min and max clash with +//the traditional min and max macros defined in . +//As a workaround, Visual C++ defines two alternative templates with +//identical functionality called _cpp_min() and _cpp_max(). You can +//use them instead of std::min() and std::max().To disable the +//generation of the min and max macros in Visual C++, #define +//NOMINMAX before #including . + +#ifdef _WIN32 + //support boost1.32.0(2004-11-19 18:47) + //now all libs can be compiled and linked with static module + #define NOMINMAX +#endif + +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/aux_/allocate_resources_impl.hpp" + +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1310 +#define for if (false) {} else for +#else +#include +#endif + +namespace libtorrent +{ + int saturated_add(int a, int b) + { + assert(a >= 0); + assert(b >= 0); + assert(a <= resource_request::inf); + assert(b <= resource_request::inf); + assert(resource_request::inf + resource_request::inf < 0); + + unsigned int sum = unsigned(a) + unsigned(b); + if (sum > unsigned(resource_request::inf)) + sum = resource_request::inf; + + assert(sum >= unsigned(a) && sum >= unsigned(b)); + return int(sum); + } + +#if defined(_MSC_VER) && _MSC_VER < 1310 + + namespace detail + { + struct iterator_wrapper + { + typedef std::map >::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper(orig_iter i): iter(i) {} + void operator++() { ++iter; } + torrent& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper& i) const + { return iter != i.iter; } + }; + + struct iterator_wrapper2 + { + typedef std::map::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper2(orig_iter i): iter(i) {} + void operator++() { ++iter; } + peer_connection& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper2& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper2& i) const + { return iter != i.iter; } + }; + + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper(c.begin()) + , detail::iterator_wrapper(c.end()) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper2(c.begin()) + , detail::iterator_wrapper2(c.end()) + , res); + } + +#else + + namespace aux + { + peer_connection& pick_peer( + std::pair + , boost::intrusive_ptr > const& p) + { + return *p.second; + } + + peer_connection& pick_peer2( + std::pair const& p) + { + return *p.second; + } + + torrent& deref(std::pair > const& p) + { + return *p.second; + } + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + typedef std::map >::iterator orig_iter; + typedef std::pair > in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::deref) + , new_iter(c.end(), &aux::deref) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + typedef std::map::iterator orig_iter; + typedef std::pair in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::pick_peer2) + , new_iter(c.end(), &aux::pick_peer2) + , res); + } +#endif + +} // namespace libtorrent diff --git a/library/bt_peer_connection.cpp b/library/bt_peer_connection.cpp new file mode 100755 index 000000000..0cff4b253 --- /dev/null +++ b/library/bt_peer_connection.cpp @@ -0,0 +1,1571 @@ +/* + +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 +#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/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +using namespace boost::posix_time; +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + // the names of the extensions to look for in + // the extensions-message + const char* bt_peer_connection::extension_names[] = + { "", "LT_chat", "LT_metadata", "LT_peer_exchange" }; + + 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, 0, 0, 0, 0, 0, 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) + : peer_connection(ses, tor, s, remote) + , m_state(read_protocol_length) + , m_supports_extensions(false) + , m_supports_dht_port(false) + , m_no_metadata( + boost::gregorian::date(1970, boost::date_time::Jan, 1) + , boost::posix_time::seconds(0)) + , m_metadata_request( + boost::gregorian::date(1970, boost::date_time::Jan, 1) + , boost::posix_time::seconds(0)) + , m_waiting_metadata_request(false) + , m_metadata_progress(0) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** bt_peer_connection\n"; +#endif + + // initialize the extension list to zero, since + // we don't know which extensions the other + // end supports yet + std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0); + + write_handshake(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(1); + + // assume the other end has no pieces + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + if (t->ready_for_connections()) + write_bitfield(t->pieces()); + + setup_send(); + setup_receive(); +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , boost::shared_ptr s) + : peer_connection(ses, s) + , m_state(read_protocol_length) + , m_supports_extensions(false) + , m_supports_dht_port(false) + , m_no_metadata( + boost::gregorian::date(1970, boost::date_time::Jan, 1) + , boost::posix_time::seconds(0)) + , m_metadata_request( + boost::gregorian::date(1970, boost::date_time::Jan, 1) + , boost::posix_time::seconds(0)) + , m_waiting_metadata_request(false) + , m_metadata_progress(0) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { + // initialize the extension list to zero, since + // we don't know which extensions the other + // end supports yet + std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0); + + // 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 + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(1); + setup_receive(); +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::~bt_peer_connection() + { + } + + void bt_peer_connection::write_dht_port(int listen_port) + { + INVARIANT_CHECK; + + buffer::interval packet = allocate_send_buffer(7); + detail::write_uint32(3, packet.begin); + detail::write_uint8(msg_dht_port, packet.begin); + detail::write_uint16(listen_port, packet.begin); + assert(packet.begin == packet.end); + setup_send(); + } + + void bt_peer_connection::get_peer_info(peer_info& p) const + { + assert(!associated_torrent().expired()); + + 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.total_download = statistics().total_payload_download(); + p.total_upload = statistics().total_payload_upload(); + + if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) + p.upload_limit = -1; + else + p.upload_limit = m_ul_bandwidth_quota.given; + + if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) + p.download_limit = -1; + else + p.download_limit = m_dl_bandwidth_quota.given; + + p.load_balancing = total_free_upload(); + + p.download_queue_length = (int)download_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.flags = 0; + 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; + if (!is_connecting() && m_state < read_packet_size) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.pieces = get_bitfield(); + p.seed = is_seed(); + + p.client = m_client_version; + p.connection_type = peer_info::standard_bittorrent; + } + + void bt_peer_connection::write_handshake() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + 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); + // 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 + + // we support extensions + *(i.begin + 5) = 0x10; + + 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; + assert(i.begin == i.end); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> HANDSHAKE\n"; +#endif + setup_send(); + } + + boost::optional bt_peer_connection::downloading_piece_progress() const + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::const_interval recv_buffer = receive_buffer(); + // are we currently receiving a 'piece' message? + if (m_state != read_packet + || (recv_buffer.end - recv_buffer.begin) < 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.end - recv_buffer.begin - 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 + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== KEEPALIVE\n"; +#endif + incoming_keepalive(); + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void bt_peer_connection::on_choke(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'choke' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_choke(); + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void bt_peer_connection::on_unchoke(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'unchoke' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_unchoke(); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void bt_peer_connection::on_interested(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'interested' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_interested(); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void bt_peer_connection::on_not_interested(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'not interested' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_not_interested(); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void bt_peer_connection::on_have(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 5) + throw protocol_error("'have' message size != 5"); + m_statistics.received_bytes(0, received); + 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; + + assert(received > 0); + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8) + throw protocol_error("bitfield with invalid size"); + + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + std::vector bitfield; + + if (!t->valid_metadata()) + bitfield.resize((packet_size() - 1) * 8); + else + bitfield.resize(get_bitfield().size()); + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + for (int i = 0; i < (int)bitfield.size(); ++i) + bitfield[i] = (recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0; + incoming_bitfield(bitfield); + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void bt_peer_connection::on_request(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 13) + throw protocol_error("'request' message size != 13"); + m_statistics.received_bytes(0, received); + 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; + + assert(received > 0); + + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = recv_buffer.end - recv_buffer.begin; + + // classify the received data as protocol chatter + // or data payload for the statistics + 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); + else + { + // received a bit of both + assert(recv_pos - received < 9); + assert(recv_pos > 9); + assert(9 - (recv_pos - received) <= 9); + m_statistics.received_bytes( + recv_pos - 9 + , 9 - (recv_pos - received)); + } + + incoming_piece_fragment(); + if (!packet_finished()) return; + + const char* ptr = recv_buffer.begin + 1; + peer_request p; + p.piece = detail::read_int32(ptr); + p.start = detail::read_int32(ptr); + p.length = packet_size() - 9; + + incoming_piece(p, recv_buffer.begin + 9); + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void bt_peer_connection::on_cancel(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 13) + throw protocol_error("'cancel' message size != 13"); + m_statistics.received_bytes(0, received); + 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; + + assert(received > 0); + if (packet_size() != 3) + throw protocol_error("'dht_port' message size != 3"); + m_statistics.received_bytes(0, received); + 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); + } + + // ----------------------------- + // --------- EXTENDED ---------- + // ----------------------------- + + void bt_peer_connection::on_extended(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() < 2) + throw protocol_error("'extended' message smaller than 2 bytes"); + + if (associated_torrent().expired()) + throw protocol_error("'extended' message sent before proper handshake"); + + buffer::const_interval recv_buffer = receive_buffer(); + if (recv_buffer.end - recv_buffer.begin < 2) return; + + assert(*recv_buffer.begin == msg_extended); + ++recv_buffer.begin; + + int extended_id = detail::read_uint8(recv_buffer.begin); + + if (extended_id > 0 && extended_id < num_supported_extensions + && !m_ses.extension_enabled(extended_id)) + throw protocol_error("'extended' message using disabled extension"); + + switch (extended_id) + { + case extended_handshake: + on_extended_handshake(); break; + case extended_chat_message: + on_chat(); break; + case extended_metadata_message: + on_metadata(); break; + case extended_peer_exchange_message: + on_peer_exchange(); break; + default: + throw protocol_error("unknown extended message id: " + + boost::lexical_cast(extended_id)); + }; + } + + void bt_peer_connection::write_chat_message(const std::string& msg) + { + INVARIANT_CHECK; + + assert(msg.length() <= 1 * 1024); + if (!supports_extension(extended_chat_message)) return; + + entry e(entry::dictionary_t); + e["msg"] = msg; + + std::vector message; + bencode(std::back_inserter(message), e); + + buffer::interval i = allocate_send_buffer(message.size() + 6); + + detail::write_uint32(1 + 1 + (int)message.size(), i.begin); + detail::write_uint8(msg_extended, i.begin); + detail::write_uint8(m_extension_messages[extended_chat_message], i.begin); + + std::copy(message.begin(), message.end(), i.begin); + i.begin += message.size(); + assert(i.begin == i.end); + setup_send(); + } + + + void bt_peer_connection::on_extended_handshake() try + { + if (!packet_finished()) return; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::const_interval recv_buffer = receive_buffer(); + + entry root = bdecode(recv_buffer.begin + 2, recv_buffer.end); + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream ext; + root.print(ext); + (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << ext.str(); +#endif + + if (entry* msgs = root.find_key("m")) + { + if (msgs->type() == entry::dictionary_t) + { + // this must be the initial handshake message + // lets see if any of our extensions are supported + // if not, we will signal no extensions support to the upper layer + for (int i = 1; i < num_supported_extensions; ++i) + { + if (entry* f = msgs->find_key(extension_names[i])) + { + m_extension_messages[i] = (int)f->integer(); + } + else + { + m_extension_messages[i] = 0; + } + } + } + } + + // there is supposed to be a remote listen port + if (entry* listen_port = root.find_key("p")) + { + if (listen_port->type() == entry::int_t) + { + tcp::endpoint adr(remote().address() + , (unsigned short)listen_port->integer()); + t->get_policy().peer_from_tracker(adr, pid()); + } + } + // there should be a version too + // but where do we put that info? + + if (entry* client_info = root.find_key("v")) + { + if (client_info->type() == entry::string_t) + m_client_version = client_info->string(); + } + + if (entry* reqq = root.find_key("reqq")) + { + if (reqq->type() == entry::int_t) + m_max_out_request_queue = reqq->integer(); + if (m_max_out_request_queue < 1) + m_max_out_request_queue = 1; + } + } + catch (std::exception& exc) + { +#ifdef TORRENT_VERBOSE_LOGGIGN + (*m_logger) << "invalid extended handshake: " << exc.what() << "\n"; +#endif + } + + // ----------------------------- + // ----------- CHAT ------------ + // ----------------------------- + + void bt_peer_connection::on_chat() + { + if (packet_size() > 2 * 1024) + throw protocol_error("CHAT message larger than 2 kB"); + + if (!packet_finished()) return; + + try + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::const_interval recv_buffer = receive_buffer(); + entry d = bdecode(recv_buffer.begin + 2, recv_buffer.end); + const std::string& str = d["msg"].string(); + + if (t->alerts().should_post(alert::critical)) + { + t->alerts().post_alert( + chat_message_alert( + t->get_handle() + , remote(), str)); + } + + } + catch (invalid_encoding&) + { + // TODO: post an alert about the invalid chat message + return; +// throw protocol_error("invalid bencoding in CHAT message"); + } + catch (type_error&) + { + // TODO: post an alert about the invalid chat message + return; +// throw protocol_error("invalid types in bencoded CHAT message"); + } + return; + } + + // ----------------------------- + // --------- METADATA ---------- + // ----------------------------- + + void bt_peer_connection::on_metadata() + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + if (packet_size() > 500 * 1024) + throw protocol_error("metadata message larger than 500 kB"); + + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + recv_buffer.begin += 2; + int type = detail::read_uint8(recv_buffer.begin); + + switch (type) + { + case 0: // request + { + int start = detail::read_uint8(recv_buffer.begin); + int size = detail::read_uint8(recv_buffer.begin) + 1; + + if (packet_size() != 5) + { + // invalid metadata request + throw protocol_error("invalid metadata request"); + } + + write_metadata(std::make_pair(start, size)); + } + break; + case 1: // data + { + if (recv_buffer.end - recv_buffer.begin < 8) return; + int total_size = detail::read_int32(recv_buffer.begin); + int offset = detail::read_int32(recv_buffer.begin); + int data_size = packet_size() - 2 - 9; + + if (total_size > 500 * 1024) + throw protocol_error("metadata size larger than 500 kB"); + if (total_size <= 0) + throw protocol_error("invalid metadata size"); + if (offset > total_size || offset < 0) + throw protocol_error("invalid metadata offset"); + if (offset + data_size > total_size) + throw protocol_error("invalid metadata message"); + + t->metadata_progress(total_size + , recv_buffer.left() - m_metadata_progress); + m_metadata_progress = recv_buffer.left(); + if (!packet_finished()) return; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== METADATA [ tot: " << total_size << " offset: " + << offset << " size: " << data_size << " ]\n"; +#endif + + m_waiting_metadata_request = false; + t->received_metadata(recv_buffer.begin, data_size + , offset, total_size); + m_metadata_progress = 0; + } + break; + case 2: // have no data + if (!packet_finished()) return; + + m_no_metadata = second_clock::universal_time(); + if (m_waiting_metadata_request) + t->cancel_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = false; + break; + default: + throw protocol_error("unknown metadata extension message: " + + boost::lexical_cast(type)); + } + + } + + // ----------------------------- + // ------ PEER EXCHANGE -------- + // ----------------------------- + + void bt_peer_connection::on_peer_exchange() + { + + } + + bool bt_peer_connection::has_metadata() const + { + using namespace boost::posix_time; + return second_clock::universal_time() - m_no_metadata > minutes(5); + } + + bool bt_peer_connection::dispatch_message(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + + // this means the connection has been closed already + if (associated_torrent().expired()) return false; + + buffer::const_interval recv_buffer = receive_buffer(); + + int packet_type = recv_buffer[0]; + if (packet_type < 0 + || packet_type >= num_supported_messages + || m_message_handler[packet_type] == 0) + { + throw protocol_error("unknown message id: " + + boost::lexical_cast(packet_type) + + " size: " + boost::lexical_cast(packet_size())); + } + + assert(m_message_handler[packet_type] != 0); + + // call the correct handler for this packet type + (this->*m_message_handler[packet_type])(received); + + if (!packet_finished()) return false; + + return true; + } + + void bt_peer_connection::write_keepalive() + { + INVARIANT_CHECK; + + char buf[] = {0,0,0,0}; + send_buffer(buf, buf + sizeof(buf)); + } + + void bt_peer_connection::write_cancel(peer_request const& r) + { + INVARIANT_CHECK; + + assert(associated_torrent().lock()->valid_metadata()); + + char buf[] = {0,0,0,13, msg_cancel}; + + buffer::interval i = allocate_send_buffer(17); + + std::copy(buf, buf + 5, i.begin); + i.begin += 5; + + // index + detail::write_int32(r.piece, i.begin); + // begin + detail::write_int32(r.start, i.begin); + // length + detail::write_int32(r.length, i.begin); + assert(i.begin == i.end); + + setup_send(); + } + + void bt_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + assert(associated_torrent().lock()->valid_metadata()); + + char buf[] = {0,0,0,13, msg_request}; + + buffer::interval i = allocate_send_buffer(17); + + std::copy(buf, buf + 5, i.begin); + i.begin += 5; + + // index + detail::write_int32(r.piece, i.begin); + // begin + detail::write_int32(r.start, i.begin); + // length + detail::write_int32(r.length, i.begin); + assert(i.begin == i.end); + + setup_send(); + } + + void bt_peer_connection::write_metadata(std::pair req) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.second <= 256); + assert(req.first + req.second <= 256); + assert(!associated_torrent().expired()); + INVARIANT_CHECK; + + // abort if the peer doesn't support the metadata extension + if (!supports_extension(extended_metadata_message)) return; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + if (t->valid_metadata()) + { + std::pair offset + = req_to_offset(req, (int)t->metadata().size()); + + buffer::interval i = allocate_send_buffer(15 + offset.second); + + // yes, we have metadata, send it + detail::write_uint32(11 + offset.second, i.begin); + detail::write_uint8(msg_extended, i.begin); + detail::write_uint8(m_extension_messages[extended_metadata_message] + , i.begin); + // means 'data packet' + detail::write_uint8(1, i.begin); + detail::write_uint32((int)t->metadata().size(), i.begin); + detail::write_uint32(offset.first, i.begin); + std::vector const& metadata = t->metadata(); + std::copy(metadata.begin() + offset.first + , metadata.begin() + offset.first + offset.second, i.begin); + i.begin += offset.second; + assert(i.begin == i.end); + } + else + { + buffer::interval i = 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(msg_extended, i.begin); + detail::write_uint8(m_extension_messages[extended_metadata_message] + , i.begin); + // means 'have no data' + detail::write_uint8(2, i.begin); + assert(i.begin == i.end); + } + setup_send(); + } + + void bt_peer_connection::write_metadata_request(std::pair req) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.first + req.second <= 256); + assert(!associated_torrent().expired()); + assert(!associated_torrent().lock()->valid_metadata()); + INVARIANT_CHECK; + + int start = req.first; + int size = req.second; + + // abort if the peer doesn't support the metadata extension + if (!supports_extension(extended_metadata_message)) return; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> METADATA_REQUEST [ start: " << req.first + << " size: " << req.second << " ]\n"; +#endif + + buffer::interval i = allocate_send_buffer(9); + + detail::write_uint32(1 + 1 + 3, i.begin); + detail::write_uint8(msg_extended, i.begin); + detail::write_uint8(m_extension_messages[extended_metadata_message] + , i.begin); + // means 'request data' + detail::write_uint8(0, i.begin); + detail::write_uint8(start, i.begin); + detail::write_uint8(size - 1, i.begin); + assert(i.begin == i.end); + setup_send(); + } + + void bt_peer_connection::write_bitfield(std::vector const& bitfield) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + if (t->num_pieces() == 0) return; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> BITFIELD "; + + for (int i = 0; i < (int)get_bitfield().size(); ++i) + { + if (bitfield[i]) (*m_logger) << "1"; + else (*m_logger) << "0"; + } + (*m_logger) << "\n"; +#endif + const int packet_size = ((int)bitfield.size() + 7) / 8 + 5; + + buffer::interval i = allocate_send_buffer(packet_size); + + detail::write_int32(packet_size - 4, i.begin); + detail::write_uint8(msg_bitfield, i.begin); + + std::fill(i.begin, i.end, 0); + for (int c = 0; c < (int)bitfield.size(); ++c) + { + if (bitfield[c]) + i.begin[c >> 3] |= 1 << (7 - (c & 7)); + } + assert(i.end - i.begin == ((int)bitfield.size() + 7) / 8); + setup_send(); + } + + void bt_peer_connection::write_extensions() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> EXTENSIONS\n"; +#endif + assert(m_supports_extensions); + + entry handshake(entry::dictionary_t); + entry extension_list(entry::dictionary_t); + + for (int i = 1; i < num_supported_extensions; ++i) + { + // if this specific extension is disabled + // just don't add it to the supported set + if (!m_ses.extension_enabled(i)) continue; + extension_list[extension_names[i]] = i; + } + + handshake["m"] = extension_list; + 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["ip"] = remote_address; + handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; + + std::vector msg; + bencode(std::back_inserter(msg), handshake); + + // make room for message + buffer::interval i = allocate_send_buffer(6 + msg.size()); + + // 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(extended_handshake, i.begin); + + std::copy(msg.begin(), msg.end(), i.begin); + i.begin += msg.size(); + assert(i.begin == i.end); + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream ext; + handshake.print(ext); + (*m_logger) << "==> EXTENDED HANDSHAKE: \n" << ext.str(); +#endif + + setup_send(); + } + + void bt_peer_connection::write_choke() + { + INVARIANT_CHECK; + + if (is_choked()) return; + char msg[] = {0,0,0,1,msg_choke}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_unchoke() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_unchoke}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_interested() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_interested}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_not_interested() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_not_interested}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_have(int index) + { + assert(associated_torrent().lock()->valid_metadata()); + assert(index >= 0); + assert(index < associated_torrent().lock()->torrent_file().num_pieces()); + INVARIANT_CHECK; + + const int packet_size = 9; + char msg[packet_size] = {0,0,0,5,msg_have}; + char* ptr = msg + 5; + detail::write_int32(index, ptr); + send_buffer(msg, msg + packet_size); + } + + void bt_peer_connection::write_piece(peer_request const& r) + { + INVARIANT_CHECK; + + const int packet_size = 4 + 5 + 4 + r.length; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::interval i = allocate_send_buffer(packet_size); + + detail::write_int32(packet_size-4, i.begin); + detail::write_uint8(msg_piece, i.begin); + detail::write_int32(r.piece, i.begin); + detail::write_int32(r.start, i.begin); + + t->filesystem().read( + i.begin, r.piece, r.start, r.length); + + assert(i.begin + r.length == i.end); + + m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); + setup_send(); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + // throws exception when the client should be disconnected + void bt_peer_connection::on_receive(const asio::error& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + boost::shared_ptr t = associated_torrent().lock(); + + switch(m_state) + { + case read_protocol_length: + { + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) break; + + int packet_size = recv_buffer[0]; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " protocol length: " << packet_size << "\n"; +#endif + if (packet_size > 100 || packet_size <= 0) + { + std::stringstream s; + s << "incorrect protocol length (" + << packet_size + << ") should be 19."; + throw std::runtime_error(s.str()); + } + m_state = read_protocol_string; + reset_recv_buffer(packet_size); + } + break; + + case read_protocol_string: + { + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) break; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " protocol: '" << std::string(recv_buffer.begin + , recv_buffer.end) << "'\n"; +#endif + const char protocol_string[] = "BitTorrent protocol"; + if (!std::equal(recv_buffer.begin, recv_buffer.end + , protocol_string)) + { + const char cmd[] = "version"; + if (recv_buffer.end - recv_buffer.begin == 7 && std::equal( + recv_buffer.begin, recv_buffer.end, cmd)) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "sending libtorrent version\n"; +#endif + asio::write(*get_socket(), asio::buffer("libtorrent version " LIBTORRENT_VERSION "\n", 27)); + throw std::runtime_error("closing"); + } +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "incorrect protocol name\n"; +#endif + std::stringstream s; + s << "got invalid protocol name: '" + << std::string(recv_buffer.begin, recv_buffer.end) + << "'"; + throw std::runtime_error(s.str()); + } + + m_state = read_info_hash; + reset_recv_buffer(28); + } + break; + + case read_info_hash: + { + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) break; + +// MassaRoddel +#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] & 0x02) + (*m_logger) << "supports XBT peer exchange message\n"; + if (recv_buffer[5] & 0x10) + (*m_logger) << "supports LT/uT extensions\n"; +#endif + + if ((recv_buffer[5] & 0x10) && m_ses.extensions_enabled()) + m_supports_extensions = true; + if (recv_buffer[7] & 0x01) + m_supports_dht_port = 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); + t = associated_torrent().lock(); + assert(t); + + assert(t->get_policy().has_connection(this)); + + // yes, we found the torrent + // reply with our handshake + write_handshake(); + write_bitfield(t->pieces()); + } + 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) << " received invalid info_hash\n"; +#endif + throw std::runtime_error("invalid info-hash in handshake"); + } + } + +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.kad_settings().service_port); +#endif + + m_state = read_peer_id; + reset_recv_buffer(20); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " info_hash received\n"; +#endif + } + break; + + case read_peer_id: + { + if (!t) return; + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) break; + assert(packet_size() == 20); + +#ifdef TORRENT_VERBOSE_LOGGING + { + peer_id tmp; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)tmp.begin()); + std::stringstream s; + s << "received peer_id: " << tmp << " client: " << identify_client(tmp) << "\n"; + s << "as ascii: "; + for (peer_id::iterator i = tmp.begin(); i != tmp.end(); ++i) + { + if (std::isprint(*i)) s << *i; + else s << "."; + } + s << "\n"; + (*m_logger) << s.str(); + } +#endif + peer_id pid; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin()); + set_pid(pid); + + 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; + } + + // 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()) + throw std::runtime_error("closing connection to ourself"); + + if (m_supports_extensions) write_extensions(); +/* + if (!m_active) + { + m_attached_to_torrent = true; + assert(m_torrent->get_policy().has_connection(this)); + } +*/ + m_state = read_packet_size; + reset_recv_buffer(4); + } + break; + + case read_packet_size: + { + if (!t) return; + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) break; + + 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) + { + // packet too large + throw std::runtime_error("packet > 1 MB (" + + boost::lexical_cast( + (unsigned int)packet_size) + " bytes)"); + } + + if (packet_size == 0) + { + incoming_keepalive(); + // keepalive message + m_state = read_packet_size; + reset_recv_buffer(4); + } + else + { + m_state = read_packet; + reset_recv_buffer(packet_size); + } + } + break; + + case read_packet: + { + if (!t) return; + if (dispatch_message(bytes_transferred)) + { + m_state = read_packet_size; + reset_recv_buffer(4); + } + } + break; + + } + } + + // -------------------------- + // SEND DATA + // -------------------------- + + // throws exception when the client should be disconnected + void bt_peer_connection::on_sent(asio::error const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + + // manage the payload markers + int amount_payload = 0; + if (!m_payloads.empty()) + { + for (std::deque::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()); + + assert(amount_payload <= (int)bytes_transferred); + m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload); + } + + + void bt_peer_connection::on_tick() + { + boost::shared_ptr t = associated_torrent().lock(); + if (!t) return; + + // if we don't have any metadata, and this peer + // supports the request metadata extension + // and we aren't currently waiting for a request + // reply. Then, send a request for some metadata. + if (!t->valid_metadata() + && supports_extension(extended_metadata_message) + && !m_waiting_metadata_request + && has_metadata()) + { + m_last_metadata_request = t->metadata_request(); + write_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = true; + m_metadata_request = second_clock::universal_time(); + } + } + +#ifndef NDEBUG + void bt_peer_connection::check_invariant() const + { + if (!m_in_constructor) + peer_connection::check_invariant(); + + if (!m_payloads.empty()) + { + for (std::deque::const_iterator i = m_payloads.begin(); + i != m_payloads.end() - 1; ++i) + { + assert(i->start + i->length <= (i+1)->start); + } + } + } +#endif + +} + diff --git a/library/control b/library/control new file mode 100644 index 000000000..ac29e820e --- /dev/null +++ b/library/control @@ -0,0 +1,9 @@ +Package: python-libtorrent +Version: 0.2.99-1-i386 +Section: base +Priority: optional +Architecture: all +Depends: python, libboost-filesystem1.33.1, libboost-date-time1.33.1, libboost-program-options1.33.1, libboost-regex1.33.1, libboost-thread1.33.1, libc6-dev, zlib1g-dev +Maintainer: A. Zakai ('Kripken') +Description: A Python wrapper for the (Sourceforge, not Rakshasa!) libtorrent C++ library. + URL: http://code.google.com/p/python-libtorrent/ diff --git a/library/debianit b/library/debianit new file mode 100755 index 000000000..463b266fd --- /dev/null +++ b/library/debianit @@ -0,0 +1,6 @@ +#svn rm ./debs/*.deb +mkdir ./debian/usr/lib/python2.4/site-packages/ +mkdir debs +cp ./build/lib.linux-i686-2.4/* ./debian/usr/lib/python2.4/site-packages/ +dpkg-deb --build debian ./debs/python-libtorrent_0.3.2_i386.deb +#svn add ./debs/*.deb diff --git a/library/entry.cpp b/library/entry.cpp new file mode 100755 index 000000000..02d86a80f --- /dev/null +++ b/library/entry.cpp @@ -0,0 +1,344 @@ +/* + +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 +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" +#include +#include + +#if defined(_MSC_VER) +namespace std +{ + using ::isprint; +} +#define for if (false) {} else for +#endif + +namespace +{ + template + void call_destructor(T* o) + { + assert(o); + o->~T(); + } + + struct compare_string + { + compare_string(char const* s): m_str(s) {} + + bool operator()( + std::pair const& e) const + { + return m_str && e.first == m_str; + } + char const* m_str; + }; +} + +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( + dict().begin() + , std::make_pair(std::string(key), entry())); + return ret->second; + } + + + entry& entry::operator[](std::string const& key) + { + return (*this)[key.c_str()]; + } + + entry* entry::find_key(char const* key) + { + dictionary_type::iterator i = std::find_if( + dict().begin() + , dict().end() + , compare_string(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; + } + + 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()]; + } + + entry::entry(const dictionary_type& v) + { + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + entry::entry(const string_type& v) + { + new(data) string_type(v); + m_type = string_t; + } + + entry::entry(const list_type& v) + { + new(data) list_type(v); + m_type = list_t; + } + + entry::entry(const integer_type& v) + { + new(data) integer_type(v); + m_type = int_t; + } + + void entry::operator=(const dictionary_type& v) + { + destruct(); + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + void entry::operator=(const string_type& v) + { + destruct(); + new(data) string_type(v); + m_type = string_t; + } + + void entry::operator=(const list_type& v) + { + destruct(); + new(data) list_type(v); + m_type = list_t; + } + + void entry::operator=(const integer_type& v) + { + destruct(); + new(data) integer_type(v); + m_type = int_t; + } + + 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: + assert(m_type == undefined_t); + return true; + } + } + + void entry::construct(data_type t) + { + m_type = t; + switch(m_type) + { + 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: + assert(m_type == undefined_t); + m_type = undefined_t; + } + } + + void entry::copy(const entry& e) + { + m_type = e.m_type; + switch(m_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: + m_type = undefined_t; + } + } + + 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: + assert(m_type == undefined_t); + break; + } + } + + void entry::print(std::ostream& os, int indent) const + { + 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 (!std::isprint(static_cast(*i))) + { + binary_string = true; + break; + } + } + if (binary_string) + { + os.unsetf(std::ios_base::dec); + os.setf(std::ios_base::hex); + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + os << std::setfill('0') << std::setw(2) + << static_cast((unsigned char)*i); + os.unsetf(std::ios_base::hex); + os.setf(std::ios_base::dec); + os << "\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) + { + for (int j = 0; j < indent+1; ++j) os << " "; + os << "[" << i->first << "]"; + 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"; + } + } +} + diff --git a/library/errorlog.txt b/library/errorlog.txt new file mode 100644 index 000000000..310f1be0e --- /dev/null +++ b/library/errorlog.txt @@ -0,0 +1,6 @@ +Saturday, November, 04, 2006 11:38 AM: Unable to open state.txt, using defaults. +Saturday, November, 04, 2006 11:38 AM: Cannot find preset.txt, using only standard E tuning +Saturday, November, 04, 2006 11:38 AM: Cannot find file APSmall.bmp +Saturday, November, 04, 2006 11:38 AM: Unable to open state.txt, using defaults. +Saturday, November, 04, 2006 11:38 AM: Cannot find preset.txt, using only standard E tuning +Saturday, November, 04, 2006 11:38 AM: Cannot find file APSmall.bmp diff --git a/library/escape_string.cpp b/library/escape_string.cpp new file mode 100755 index 000000000..80d134ed7 --- /dev/null +++ b/library/escape_string.cpp @@ -0,0 +1,148 @@ +/* + +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 +#include +#include +#include +#include +#include + +namespace libtorrent +{ + std::string unescape_string(std::string const& s) + { + 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()) + throw std::runtime_error("invalid escaped string"); + + 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 throw std::runtime_error("invalid escaped string"); + + ++i; + if (i == s.end()) + throw std::runtime_error("invalid escaped string"); + + 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 throw std::runtime_error("invalid escaped string"); + + ret += char(high * 16 + low); + } + } + return ret; + } + + + std::string escape_string(const char* str, int len) + { + assert(str != 0); + assert(len >= 0); + // http://www.ietf.org/rfc/rfc2396.txt + // section 2.3 + // some trackers seems to require that ' is escaped +// static const char unreserved_chars[] = "-_.!~*'()"; + static const char unreserved_chars[] = "-_.!~*()" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + std::stringstream ret; + ret << std::hex << std::setfill('0'); + for (int i = 0; i < len; ++i) + { + if (std::count( + unreserved_chars + , unreserved_chars+sizeof(unreserved_chars)-1 + , *str)) + { + ret << *str; + } + else + { + ret << '%' + << std::setw(2) + << (int)static_cast(*str); + } + ++str; + } + return ret.str(); + } + + std::string escape_path(const char* str, int len) + { + assert(str != 0); + assert(len >= 0); + static const char unreserved_chars[] = "/-_.!~*()" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + std::stringstream ret; + ret << std::hex << std::setfill('0'); + for (int i = 0; i < len; ++i) + { + if (std::count( + unreserved_chars + , unreserved_chars+sizeof(unreserved_chars)-1 + , *str)) + { + ret << *str; + } + else + { + ret << '%' + << std::setw(2) + << (int)static_cast(*str); + } + ++str; + } + return ret.str(); + } +} diff --git a/library/file.cpp b/library/file.cpp new file mode 100755 index 000000000..af8404660 --- /dev/null +++ b/library/file.cpp @@ -0,0 +1,313 @@ +/* + +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. + +*/ + +#ifdef _WIN32 +// windows part +#include "libtorrent/utf8.hpp" + +#include +#include +#include +#include + +#ifndef _MODE_T_ +typedef int mode_t; +#endif + +#ifdef UNICODE +#include "libtorrent/storage.hpp" +#endif + +#else +// unix part +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include + +#include +// make sure the _FILE_OFFSET_BITS define worked +// on this platform +BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); + +#endif + +#include +#include "libtorrent/file.hpp" +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef O_RANDOM +#define O_RANDOM 0 +#endif + +#ifdef UNICODE +#include "libtorrent/storage.hpp" +#endif + + +namespace fs = boost::filesystem; + +namespace +{ + enum { mode_in = 1, mode_out = 2 }; + + mode_t map_open_mode(int m) + { + if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM; + assert(false); + return 0; + } + +#ifdef WIN32 + std::string utf8_native(std::string const& s) + { + try + { + 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 == wchar_t(-1)) return s; + ret.resize(size); + return ret; + } + catch(std::exception) + { + return s; + } + } +#else + std::string utf8_native(std::string const& s) + { + return s; + } +#endif + +} + +namespace libtorrent +{ + + const file::open_mode file::in(mode_in); + const file::open_mode file::out(mode_out); + + const file::seek_mode file::begin(1); + const file::seek_mode file::end(2); + + struct file::impl + { + impl() + : m_fd(-1) + , m_open_mode(0) + {} + + impl(fs::path const& path, int mode) + : m_fd(-1) + , m_open_mode(0) + { + open(path, mode); + } + + ~impl() + { + close(); + } + + void open(fs::path const& path, int mode) + { + assert(path.is_complete()); + close(); +#if defined(_WIN32) && defined(UNICODE) + std::wstring wpath(safe_convert(path.native_file_string())); + m_fd = ::_wopen( + wpath.c_str() + , map_open_mode(mode) + , S_IREAD | S_IWRITE); +#else + m_fd = ::open( + utf8_native(path.native_file_string()).c_str() + , map_open_mode(mode) +#ifdef _WIN32 + , S_IREAD | S_IWRITE); +#else + , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif +#endif + if (m_fd == -1) + { + std::stringstream msg; + msg << "open failed: '" << path.native_file_string() << "'. " + << strerror(errno); + throw file_error(msg.str()); + } + m_open_mode = mode; + } + + void close() + { + if (m_fd == -1) return; + + ::close(m_fd); + m_fd = -1; + m_open_mode = 0; + } + + size_type read(char* buf, size_type num_bytes) + { + assert(m_open_mode & mode_in); + assert(m_fd != -1); + + size_type ret = ::read(m_fd, buf, num_bytes); + if (ret == -1) + { + std::stringstream msg; + msg << "read failed: " << strerror(errno); + throw file_error(msg.str()); + } + return ret; + } + + size_type write(const char* buf, size_type num_bytes) + { + assert(m_open_mode & mode_out); + assert(m_fd != -1); + + // TODO: Test this a bit more, what happens with random failures in + // the files? +// if ((rand() % 100) > 80) +// throw file_error("debug"); + + size_type ret = ::write(m_fd, buf, num_bytes); + if (ret == -1) + { + std::stringstream msg; + msg << "write failed: " << strerror(errno); + throw file_error(msg.str()); + } + return ret; + } + + size_type seek(size_type offset, int m) + { + assert(m_open_mode); + assert(m_fd != -1); + + int seekdir = (m == 1)?SEEK_SET:SEEK_END; +#ifdef _WIN32 + size_type ret = _lseeki64(m_fd, offset, seekdir); +#else + size_type ret = lseek(m_fd, offset, seekdir); +#endif + + // For some strange reason this fails + // on win32. Use windows specific file + // wrapper instead. + if (ret == -1) + { + std::stringstream msg; + msg << "seek failed: '" << strerror(errno) + << "' fd: " << m_fd + << " offset: " << offset + << " seekdir: " << seekdir; + throw file_error(msg.str()); + } + return ret; + } + + size_type tell() + { + assert(m_open_mode); + assert(m_fd != -1); + +#ifdef _WIN32 + return _telli64(m_fd); +#else + return lseek(m_fd, 0, SEEK_CUR); +#endif + } + + int m_fd; + int m_open_mode; + }; + + // pimpl forwardings + + file::file() : m_impl(new impl()) {} + + file::file(boost::filesystem::path const& p, file::open_mode m) + : m_impl(new impl(p, m.m_mask)) + {} + + file::~file() {} + + void file::open(boost::filesystem::path const& p, file::open_mode m) + { + m_impl->open(p, m.m_mask); + } + + void file::close() + { + m_impl->close(); + } + + size_type file::write(const char* buf, size_type num_bytes) + { + return m_impl->write(buf, num_bytes); + } + + size_type file::read(char* buf, size_type num_bytes) + { + return m_impl->read(buf, num_bytes); + } + + size_type file::seek(size_type pos, file::seek_mode m) + { + return m_impl->seek(pos, m.m_val); + } + + size_type file::tell() + { + return m_impl->tell(); + } + +} diff --git a/library/http_tracker_connection.cpp b/library/http_tracker_connection.cpp new file mode 100755 index 000000000..3299cf5b8 --- /dev/null +++ b/library/http_tracker_connection.cpp @@ -0,0 +1,845 @@ +/* + +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 +#include +#include +#include + +#include "zlib.h" + +#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/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/io.hpp" + +using namespace libtorrent; +using boost::bind; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +using namespace boost::posix_time; + +namespace libtorrent +{ + http_parser::http_parser() + : m_recv_pos(0) + , m_status_code(-1) + , m_content_length(-1) + , m_content_encoding(plain) + , m_state(read_status) + , m_recv_buffer(0, 0) + , m_body_start_pos(0) + , m_finished(false) + {} + + boost::tuple http_parser::incoming(buffer::const_interval recv_buffer) + { + m_recv_buffer = recv_buffer; + boost::tuple ret(0, 0); + + char const* pos = recv_buffer.begin + m_recv_pos; + if (m_state == read_status) + { + 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) return ret; + + if (newline == pos) + throw std::runtime_error("unexpected newline in HTTP response"); + + std::istringstream line(std::string(pos, newline - 1)); + ++newline; + int incoming = (int)std::distance(pos, newline); + m_recv_pos += incoming; + boost::get<1>(ret) += incoming; + pos = newline; + + line >> m_protocol; + if (m_protocol.substr(0, 5) != "HTTP/") + { + throw std::runtime_error("unknown protocol in HTTP response: " + + m_protocol); + } + line >> m_status_code; + std::getline(line, m_server_message); + m_state = read_header; + } + + if (m_state == read_header) + { + 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 (newline == pos) + throw std::runtime_error("unexpected newline in HTTP response"); + + line.assign(pos, newline - 1); + m_recv_pos += newline - pos; + boost::get<1>(ret) += newline - pos; + pos = newline; + + std::string::size_type separator = line.find(": "); + if (separator == std::string::npos) + { + ++pos; + ++m_recv_pos; + boost::get<1>(ret) += 1; + + m_state = read_body; + m_body_start_pos = m_recv_pos; + break; + } + + std::string name = line.substr(0, separator); + std::string value = line.substr(separator + 2, std::string::npos); + m_header.insert(std::make_pair(name, value)); + + if (name == "Content-Length") + { + try + { + m_content_length = boost::lexical_cast(value); + } + catch(boost::bad_lexical_cast&) {} + } + else if (name == "Content-Encoding") + { + if (value == "gzip" || value == "x-gzip") + { + m_content_encoding = gzip; + } + else + { + std::string error_str = "unknown content encoding in response: \""; + error_str += value; + error_str += "\""; + throw std::runtime_error(error_str); + } + } + // TODO: make sure we don't step outside of the buffer + ++pos; + ++m_recv_pos; + assert(m_recv_pos <= (int)recv_buffer.left()); + newline = std::find(pos, recv_buffer.end, '\n'); + } + } + + 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; + + 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; + } + + buffer::const_interval http_parser::get_body() + { + char const* body_begin = m_recv_buffer.begin + m_body_start_pos; + char const* body_end = m_recv_buffer.begin + m_recv_pos; + + m_recv_pos = 0; + m_body_start_pos = 0; + m_status_code = -1; + m_content_length = -1; + m_finished = false; + m_state = read_status; + m_header.clear(); + + return buffer::const_interval(body_begin, body_end); + } + + http_tracker_connection::http_tracker_connection( + demuxer& d + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string request + , boost::weak_ptr c + , session_settings const& stn + , std::string const& auth) + : tracker_connection(man, req, d, c) + , m_man(man) + , m_state(read_status) + , m_content_encoding(plain) + , m_content_length(0) + , m_name_lookup(d) + , m_port(port) + , m_recv_pos(0) + , m_buffer(http_buffer_size) + , m_settings(stn) + , m_password(auth) + , m_code(0) + , m_timed_out(false) + { + const std::string* connect_to_host; + bool using_proxy = false; + + m_send_buffer.assign("GET "); + + // should we use the proxy? + if (!m_settings.proxy_ip.empty()) + { + connect_to_host = &m_settings.proxy_ip; + using_proxy = true; + m_send_buffer += "http://"; + m_send_buffer += hostname; + if (port != 80) + { + m_send_buffer += ":"; + m_send_buffer += boost::lexical_cast(port); + } + m_port = m_settings.proxy_port != 0 + ? m_settings.proxy_port : 80 ; + } + else + { + connect_to_host = &hostname; + } + + if (tracker_req().kind == tracker_request::scrape_request) + { + // find and replace "announce" with "scrape" + // in request + + std::size_t pos = request.find("announce"); + if (pos == std::string::npos) + throw std::runtime_error("scrape is not available on url: '" + + tracker_req().url +"'"); + request.replace(pos, 8, "scrape"); + } + + m_send_buffer += request; + + // if request-string already contains + // some parameters, append an ampersand instead + // of a question mark + if (request.find('?') != std::string::npos) + m_send_buffer += "&"; + else + m_send_buffer += "?"; + + m_send_buffer += "info_hash="; + m_send_buffer += escape_string( + reinterpret_cast(req.info_hash.begin()), 20); + + if (tracker_req().kind == tracker_request::announce_request) + { + m_send_buffer += "&peer_id="; + m_send_buffer += escape_string( + reinterpret_cast(req.pid.begin()), 20); + + m_send_buffer += "&port="; + m_send_buffer += boost::lexical_cast(req.listen_port); + + m_send_buffer += "&uploaded="; + m_send_buffer += boost::lexical_cast(req.uploaded); + + m_send_buffer += "&downloaded="; + m_send_buffer += boost::lexical_cast(req.downloaded); + + m_send_buffer += "&left="; + m_send_buffer += boost::lexical_cast(req.left); + + if (req.event != tracker_request::none) + { + const char* event_string[] = {"completed", "started", "stopped"}; + m_send_buffer += "&event="; + m_send_buffer += event_string[req.event - 1]; + } + m_send_buffer += "&key="; + std::stringstream key_string; + key_string << std::hex << req.key; + m_send_buffer += key_string.str(); + m_send_buffer += "&compact=1"; + m_send_buffer += "&numwant="; + m_send_buffer += boost::lexical_cast( + std::min(req.num_want, 999)); + + // extension that tells the tracker that + // we don't need any peer_id's in the response + m_send_buffer += "&no_peer_id=1"; + } + + m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n" + "User-Agent: "; + m_send_buffer += m_settings.user_agent; + m_send_buffer += "\r\n" + "Host: "; + m_send_buffer += hostname; + if (port != 80) + { + m_send_buffer += ':'; + m_send_buffer += boost::lexical_cast(port); + } + if (using_proxy && !m_settings.proxy_login.empty()) + { + m_send_buffer += "\r\nProxy-Authorization: Basic "; + m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password); + } + if (auth != "") + { + m_send_buffer += "\r\nAuthorization: Basic "; + m_send_buffer += base64encode(auth); + } + m_send_buffer += "\r\n\r\n"; +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]"); + std::stringstream info_hash_str; + info_hash_str << req.info_hash; + requester().debug_log("info_hash: " + info_hash_str.str() + "\n"); + } +#endif + + tcp::resolver::query q(*connect_to_host, "0"); + m_name_lookup.async_resolve(q + , boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)); + set_timeout(m_settings.tracker_completion_timeout + , m_settings.tracker_receive_timeout); + } + + void http_tracker_connection::on_timeout() + { + m_timed_out = true; + m_socket.reset(); + m_name_lookup.cancel(); + fail_timeout(); + } + + void http_tracker_connection::name_lookup(asio::error const& error + , tcp::resolver::iterator i) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + + if (error || i == tcp::resolver::iterator()) + { + fail(-1, error.what()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker name lookup successful"); +#endif + restart_read_timeout(); + m_socket.reset(new stream_socket(m_name_lookup.io_service())); + tcp::endpoint a(i->endpoint().address(), m_port); + if (has_requester()) requester().m_tracker_address = a; + m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1)); + } + catch (std::exception& e) + { + assert(false); + fail(-1, e.what()); + }; + + void http_tracker_connection::connected(asio::error const& error) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + if (error) + { + fail(-1, error.what()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker connection successful"); +#endif + + restart_read_timeout(); + async_write(*m_socket, asio::buffer(m_send_buffer.c_str() + , m_send_buffer.size()), bind(&http_tracker_connection::sent + , self(), _1)); + } + catch (std::exception& e) + { + assert(false); + fail(-1, e.what()); + } + + void http_tracker_connection::sent(asio::error const& error) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + if (error) + { + fail(-1, error.what()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker send data completed"); +#endif + restart_read_timeout(); + assert(m_buffer.size() - m_recv_pos > 0); + m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] + , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive + , self(), _1, _2)); + } + catch (std::exception& e) + { + assert(false); + fail(-1, e.what()); + }; // msvc 7.1 seems to require this semi-colon + + + void http_tracker_connection::receive(asio::error const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + + if (error) + { + if (error == asio::error::eof) + { + on_response(); + close(); + return; + } + + fail(-1, error.what()); + return; + } + + restart_read_timeout(); + assert(bytes_transferred > 0); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker connection reading " + + boost::lexical_cast(bytes_transferred)); +#endif + + m_recv_pos += bytes_transferred; + + // if the receive buffer is full, expand it with http_buffer_size + if ((int)m_buffer.size() == m_recv_pos) + { + if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length) + { + fail(200, "too large tracker response"); + return; + } + assert(http_buffer_size > 0); + if ((int)m_buffer.size() + http_buffer_size + > m_settings.tracker_maximum_response_length) + m_buffer.resize(m_settings.tracker_maximum_response_length); + else + m_buffer.resize(m_buffer.size() + http_buffer_size); + } + + if (m_state == read_status) + { + std::vector::iterator end = m_buffer.begin()+m_recv_pos; + std::vector::iterator newline = std::find(m_buffer.begin(), end, '\n'); + // if we don't have a full line yet, wait. + if (newline != end) + { + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline)); +#endif + + std::istringstream line(std::string(m_buffer.begin(), newline)); + ++newline; + m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); + m_buffer.erase(m_buffer.begin(), newline); + + std::string protocol; + line >> m_server_protocol; + if (m_server_protocol.substr(0, 5) != "HTTP/") + { + std::string error_msg = "unknown protocol in response: " + m_server_protocol; + fail(-1, error_msg.c_str()); + return; + } + line >> m_code; + std::getline(line, m_server_message); + m_state = read_header; + } + } + + if (m_state == read_header) + { + std::vector::iterator end = m_buffer.begin() + m_recv_pos; + std::vector::iterator newline + = std::find(m_buffer.begin(), end, '\n'); + std::string line; + + while (newline != end && m_state == read_header) + { + line.assign(m_buffer.begin(), newline); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log(line); +#endif + + if (line.substr(0, 16) == "Content-Length: ") + { + try + { + m_content_length = boost::lexical_cast( + line.substr(16, line.length() - 17)); + } + catch(boost::bad_lexical_cast&) + { + fail(-1, "invalid content-length in tracker response"); + return; + } + if (m_content_length > m_settings.tracker_maximum_response_length) + { + fail(-1, "content-length is greater than maximum response length"); + return; + } + + if (m_content_length < minimum_tracker_response_length && m_code == 200) + { + fail(-1, "content-length is smaller than minimum response length"); + return; + } + } + else if (line.substr(0, 18) == "Content-Encoding: ") + { + if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip") + { + m_content_encoding = gzip; + } + else + { + std::string error_str = "unknown content encoding in response: \""; + error_str += line.substr(18, line.length() - 18 - 2); + error_str += "\""; + fail(-1, error_str.c_str()); + return; + } + } + else if (line.substr(0, 10) == "Location: ") + { + m_location.assign(line.begin() + 10, line.end()); + } + else if (line.substr(0, 7) == "Server: ") + { + m_server.assign(line.begin() + 7, line.end()); + } + else if (line.size() < 3) + { + m_state = read_body; +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("end of http header"); +#endif + if (m_code >= 300 && m_code < 400) + { + if (m_location.empty()) + { + std::string error_str = "got redirection response ("; + error_str += boost::lexical_cast(m_code); + error_str += ") without 'Location' header"; + fail(-1, error_str.c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\""); +#endif + tracker_request req = tracker_req(); + std::string::size_type i = m_location.find('?'); + if (i == std::string::npos) + req.url = m_location; + else + req.url.assign(m_location.begin(), m_location.begin() + i); + + m_man.queue_request(m_socket->io_service(), req + , m_password, m_requester); + close(); + return; + } + } + + ++newline; + assert(m_recv_pos <= (int)m_buffer.size()); + m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); + m_buffer.erase(m_buffer.begin(), newline); + assert(m_recv_pos <= (int)m_buffer.size()); + end = m_buffer.begin() + m_recv_pos; + newline = std::find(m_buffer.begin(), end, '\n'); + } + + } + + if (m_state == read_body) + { + if (m_recv_pos == m_content_length) + { + on_response(); + close(); + return; + } + } + else if (m_recv_pos > m_content_length && m_content_length > 0) + { + fail(-1, "invalid tracker response (body > content_length)"); + return; + } + + assert(m_buffer.size() - m_recv_pos > 0); + m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] + , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive + , self(), _1, _2)); + } + catch (std::exception& e) + { + assert(false); + fail(-1, e.what()); + }; + + void http_tracker_connection::on_response() + { + // GZIP + if (m_content_encoding == gzip) + { + boost::shared_ptr r = m_requester.lock(); + + if (!r) + { + close(); + return; + } + if (inflate_gzip(m_buffer, tracker_request(), r.get(), + m_settings.tracker_maximum_response_length)) + { + close(); + return; + } + } + + // handle tracker response + try + { + entry e = bdecode(m_buffer.begin(), m_buffer.end()); + parse(e); + } + catch (std::exception& e) + { + std::string error_str(e.what()); + error_str += ": "; + error_str.append(m_buffer.begin(), m_buffer.end()); + fail(m_code, error_str.c_str()); + } + #ifndef NDEBUG + catch (...) + { + assert(false); + } + #endif + } + + peer_entry http_tracker_connection::extract_peer_info(const entry& info) + { + peer_entry ret; + + // extract peer id (if any) + entry const* i = info.find_key("peer id"); + if (i != 0) + { + if (i->string().length() != 20) + throw std::runtime_error("invalid response from tracker"); + std::copy(i->string().begin(), i->string().end(), 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.find_key("ip"); + if (i == 0) throw std::runtime_error("invalid response from tracker"); + ret.ip = i->string(); + + // extract port + i = info.find_key("port"); + if (i == 0) throw std::runtime_error("invalid response from tracker"); + ret.port = (unsigned short)i->integer(); + + return ret; + } + + void http_tracker_connection::parse(entry const& e) + { + if (!has_requester()) return; + + try + { + // parse the response + try + { + entry const& failure = e["failure reason"]; + + fail(m_code, failure.string().c_str()); + return; + } + catch (type_error const&) {} + + try + { + entry const& warning = e["warning message"]; + if (has_requester()) + requester().tracker_warning(warning.string()); + } + catch(type_error const&) {} + + std::vector peer_list; + + if (tracker_req().kind == tracker_request::scrape_request) + { + std::string ih; + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end() + , std::back_inserter(ih)); + entry scrape_data = e["files"][ih]; + int complete = scrape_data["complete"].integer(); + int incomplete = scrape_data["incomplete"].integer(); + requester().tracker_response(tracker_request(), peer_list, 0, complete + , incomplete); + return; + } + + int interval = (int)e["interval"].integer(); + + if (e["peers"].type() == entry::string_t) + { + std::string const& peers = e["peers"].string(); + for (std::string::const_iterator i = peers.begin(); + i != peers.end();) + { + if (std::distance(i, peers.end()) < 6) break; + + peer_entry p; + p.pid.clear(); + std::stringstream ip_str; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i); + p.ip = ip_str.str(); + p.port = detail::read_uint16(i); + peer_list.push_back(p); + } + } + else + { + entry::list_type const& l = e["peers"].list(); + for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i) + { + peer_entry p = extract_peer_info(*i); + peer_list.push_back(p); + } + } + + // look for optional scrape info + int complete = -1; + int incomplete = -1; + + try { complete = e["complete"].integer(); } + catch(type_error&) {} + + try { incomplete = e["incomplete"].integer(); } + catch(type_error&) {} + + requester().tracker_response(tracker_request(), peer_list, interval, complete + , incomplete); + } + catch(type_error& e) + { + requester().tracker_request_error(tracker_request(), m_code, e.what()); + } + catch(std::runtime_error& e) + { + requester().tracker_request_error(tracker_request(), m_code, e.what()); + } + } + +} + diff --git a/library/identify_client.cpp b/library/identify_client.cpp new file mode 100755 index 000000000..96039f2b0 --- /dev/null +++ b/library/identify_client.cpp @@ -0,0 +1,326 @@ +/* + +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 + +#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" + +namespace +{ + + using namespace libtorrent; + + int decode_digit(char c) + { + if (std::isdigit(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] != '-' || !std::isprint(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 (!std::isalnum(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 + || !std::isprint(ret.name[0])) + return boost::optional(); + + return boost::optional(ret); + } + + typedef std::pair map_entry; + + // only support BitTorrentSpecification + // must be ordered alphabetically + map_entry name_map[] = + { + map_entry("A", "ABC") + , map_entry("AR", "Arctic Torrent") + , map_entry("AX", "BitPump") + , map_entry("AZ", "Azureus") + , map_entry("BB", "BitBuddy") + , map_entry("BC", "BitComet") + , map_entry("BS", "BTSlave") + , map_entry("BX", "BittorrentX") + , map_entry("CD", "Enhanced CTorrent") + , map_entry("CT", "CTorrent") + , map_entry("DE", "Deluge") + , map_entry("ES", "electric sheep") + , map_entry("KT", "KTorrent") + , map_entry("LP", "lphant") + , map_entry("LT", "libtorrent") + , map_entry("M", "Mainline") + , map_entry("MP", "MooPolice") + , map_entry("MT", "Moonlight Torrent") + , map_entry("O", "Osprey Permaseed") + , map_entry("R", "Tribler") + , map_entry("S", "Shadow") + , map_entry("SB", "Swiftbit") + , map_entry("SN", "ShareNet") + , map_entry("SS", "SwarmScope") + , map_entry("SZ", "Shareaza") + , map_entry("T", "BitTornado") + , map_entry("TN", "Torrent.NET") + , map_entry("TR", "Transmission") + , map_entry("TS", "TorrentStorm") + , map_entry("U", "UPnP") + , map_entry("UL", "uLeecher") + , map_entry("UT", "MicroTorrent") + , map_entry("XT", "XanTorrent") + , map_entry("ZT", "ZipTorrent") + , map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)") + , map_entry("pX", "pHoeniX") + , map_entry("qB", "qBittorrent") + }; + + bool compare_first_string(map_entry const& lhs, map_entry const& rhs) + { + return lhs.first[0] < rhs.first[0] + || ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1])); + } + + std::string lookup(fingerprint const& f) + { + std::stringstream identity; + + const int size = sizeof(name_map)/sizeof(name_map[0]); + map_entry* i = + std::lower_bound(name_map, name_map + size + , map_entry(f.name, ""), &compare_first_string); + +#ifndef NDEBUG + for (int i = 1; i < size; ++i) + { + assert(compare_first_string(name_map[i-1] + , name_map[i])); + } +#endif + + if (i < name_map + size && std::equal(f.name, f.name + 2, i->first)) + identity << i->second; + else + { + identity << f.name[0]; + if (f.name[1] != 0) identity << f.name[1]; + } + + identity << " " << (int)f.major_version + << "." << (int)f.minor_version + << "." << (int)f.revision_version; + + if (f.name[1] != 0) + identity << "." << (int)f.tag_version; + + return identity.str(); + } + + 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 + // ---------------------- + + if (find_string(PID, "Deadman Walking-")) return "Deadman"; + if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2"; + if (find_string(PID, "DansClient")) return "XanTorrent"; + if (find_string(PID + 4, "btfans")) return "SimpleBT"; + if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II"; + if (find_string(PID, "P87.P---")) return "Bittorrent Plus!"; + if (find_string(PID, "S587Plus")) return "Bittorrent Plus!"; + if (find_string(PID, "martini")) return "Martini Man"; + if (find_string(PID, "Plus---")) return "Bittorrent Plus"; + if (find_string(PID, "turbobt")) return "TurboBT"; + if (find_string(PID, "a00---0")) return "Swarmy"; + if (find_string(PID, "a02---0")) return "Swarmy"; + if (find_string(PID, "T00---0")) return "Teeweety"; + if (find_string(PID, "BTDWV-")) return "Deadman Walking"; + if (find_string(PID + 2, "BS")) return "BitSpirit"; + if (find_string(PID, "btuga")) return "BTugaXP"; + if (find_string(PID, "oernu")) return "BTugaXP"; + if (find_string(PID, "Mbrst")) return "Burst!"; + if (find_string(PID, "Plus")) return "Plus!"; + if (find_string(PID, "-Qt-")) return "Qt"; + if (find_string(PID, "exbc")) return "BitComet"; + if (find_string(PID, "-G3")) return "G3 Torrent"; + if (find_string(PID, "XBT")) return "XBT"; + if (find_string(PID, "OP")) return "Opera"; + + if (find_string(PID, "-BOW") && PID[7] == '-') + return "Bits on Wheels " + std::string(PID + 4, PID + 7); + + + if (find_string(PID, "eX")) + { + std::string user(PID + 2, 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 += std::isprint(*i)?*i:'.'; + } + unknown += "]"; + return unknown; + } + +} diff --git a/library/include/libtorrent/alert.hpp b/library/include/libtorrent/alert.hpp new file mode 100755 index 000000000..a820ef225 --- /dev/null +++ b/library/include/libtorrent/alert.hpp @@ -0,0 +1,174 @@ +/* + +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 +#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" + +#define TORRENT_MAX_ALERT_TYPES 10 + +namespace libtorrent { + + class TORRENT_EXPORT alert + { + public: + enum severity_t { debug, info, warning, critical, fatal, none }; + + alert(severity_t severity, const std::string& msg); + virtual ~alert(); + + // a timestamp is automatically created in the constructor + boost::posix_time::ptime timestamp() const; + + const std::string& msg() const; + + severity_t severity() const; + + virtual std::auto_ptr clone() const = 0; + + private: + std::string m_msg; + severity_t m_severity; + boost::posix_time::ptime m_timestamp; + }; + + class TORRENT_EXPORT alert_manager + { + public: + alert_manager(); + ~alert_manager(); + + void post_alert(const alert& alert_); + bool pending() const; + std::auto_ptr get(); + + void set_severity(alert::severity_t severity); + bool should_post(alert::severity_t severity) const; + + private: + std::queue m_alerts; + alert::severity_t m_severity; + mutable boost::mutex m_mutex; + }; + + struct TORRENT_EXPORT unhandled_alert : std::exception + { + unhandled_alert() {} + }; + + namespace detail { + + struct void_; + + template< + class Handler + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T) + > + void handle_alert_dispatch( + const std::auto_ptr& alert_ + , const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_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& alert_ + , const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) + { + throw unhandled_alert(); + } + + } // namespace detail + + template< + BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_) + > + 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 + } + }; + +} // namespace libtorrent + +#endif // TORRENT_ALERT_HPP_INCLUDED + diff --git a/library/include/libtorrent/alert_types.hpp b/library/include/libtorrent/alert_types.hpp new file mode 100755 index 000000000..7a5cb7fd3 --- /dev/null +++ b/library/include/libtorrent/alert_types.hpp @@ -0,0 +1,300 @@ +/* + +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/peer_connection.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT tracker_alert: alert + { + tracker_alert(torrent_handle const& h + , int times + , int status + , std::string const& msg) + : alert(alert::warning, msg) + , handle(h) + , times_in_row(times) + , status_code(status) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_alert(*this)); } + + torrent_handle handle; + int times_in_row; + int status_code; + }; + + struct TORRENT_EXPORT tracker_warning_alert: alert + { + tracker_warning_alert(torrent_handle const& h + , std::string const& msg) + : alert(alert::warning, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_warning_alert(*this)); } + + torrent_handle handle; + }; + + + + struct TORRENT_EXPORT tracker_reply_alert: alert + { + tracker_reply_alert(torrent_handle const& h + , std::string const& msg) + : alert(alert::info, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_reply_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT tracker_announce_alert: alert + { + tracker_announce_alert(torrent_handle const& h, std::string const& msg) + : alert(alert::info, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_announce_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT hash_failed_alert: alert + { + hash_failed_alert( + torrent_handle const& h + , int index + , std::string const& msg) + : alert(alert::info, msg) + , handle(h) + , piece_index(index) + { assert(index >= 0);} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new hash_failed_alert(*this)); } + + torrent_handle handle; + int piece_index; + }; + + struct TORRENT_EXPORT peer_ban_alert: alert + { + peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg) + : alert(alert::info, msg) + , ip(pip) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_ban_alert(*this)); } + + tcp::endpoint ip; + torrent_handle handle; + }; + + struct TORRENT_EXPORT peer_error_alert: alert + { + peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg) + : alert(alert::debug, msg) + , ip(pip) + , pid(pid_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_error_alert(*this)); } + + tcp::endpoint ip; + peer_id pid; + }; + + struct TORRENT_EXPORT chat_message_alert: alert + { + chat_message_alert( + const torrent_handle& h + , const tcp::endpoint& sender + , const std::string& msg) + : alert(alert::critical, msg) + , handle(h) + , ip(sender) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new chat_message_alert(*this)); } + + torrent_handle handle; + tcp::endpoint ip; + }; + + struct TORRENT_EXPORT invalid_request_alert: alert + { + invalid_request_alert( + peer_request const& r + , torrent_handle const& h + , tcp::endpoint const& sender + , peer_id const& pid_ + , std::string const& msg) + : alert(alert::debug, msg) + , handle(h) + , ip(sender) + , request(r) + , pid(pid_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new invalid_request_alert(*this)); } + + torrent_handle handle; + tcp::endpoint ip; + peer_request request; + peer_id pid; + }; + + struct TORRENT_EXPORT torrent_finished_alert: alert + { + torrent_finished_alert( + const torrent_handle& h + , const std::string& msg) + : alert(alert::warning, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new torrent_finished_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT url_seed_alert: alert + { + url_seed_alert( + const std::string& url_ + , const std::string& msg) + : alert(alert::warning, msg) + , url(url_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new url_seed_alert(*this)); } + + std::string url; + }; + + struct TORRENT_EXPORT file_error_alert: alert + { + file_error_alert( + const torrent_handle& h + , const std::string& msg) + : alert(alert::fatal, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new file_error_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT metadata_failed_alert: alert + { + metadata_failed_alert( + const torrent_handle& h + , const std::string& msg) + : alert(alert::info, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new metadata_failed_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT metadata_received_alert: alert + { + metadata_received_alert( + const torrent_handle& h + , const std::string& msg) + : alert(alert::info, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new metadata_received_alert(*this)); } + + torrent_handle handle; + }; + + struct TORRENT_EXPORT listen_failed_alert: alert + { + listen_failed_alert( + const std::string& msg) + : alert(alert::fatal, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new listen_failed_alert(*this)); } + }; + + struct TORRENT_EXPORT fastresume_rejected_alert: alert + { + fastresume_rejected_alert(torrent_handle const& h + , std::string const& msg) + : alert(alert::warning, msg) + , handle(h) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new fastresume_rejected_alert(*this)); } + + torrent_handle handle; + }; + +} + + +#endif diff --git a/library/include/libtorrent/allocate_resources.hpp b/library/include/libtorrent/allocate_resources.hpp new file mode 100644 index 000000000..b859b6b47 --- /dev/null +++ b/library/include/libtorrent/allocate_resources.hpp @@ -0,0 +1,72 @@ +/* + +Copyright (c) 2003, 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. + +*/ + +#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED +#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED + +#include +#include + +#include + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" + +namespace libtorrent +{ + class peer_connection; + class torrent; + + int saturated_add(int a, int b); + + // Function to allocate a limited resource fairly among many consumers. + // It takes into account the current use, and the consumer's desired use. + // Should be invoked periodically to allow it adjust to the situation (make + // sure "used" is updated between calls!). + // If resources = std::numeric_limits::max() it means there is an infinite + // supply of resources (so everyone can get what they want). + + void allocate_resources( + int resources + , std::map >& torrents + , resource_request torrent::* res); + + void allocate_resources( + int resources + , std::map& connections + , resource_request peer_connection::* res); + +} + + +#endif diff --git a/library/include/libtorrent/asio.hpp b/library/include/libtorrent/asio.hpp new file mode 100644 index 000000000..3bd10de70 --- /dev/null +++ b/library/include/libtorrent/asio.hpp @@ -0,0 +1,71 @@ +// +// asio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_HPP +#define ASIO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_deadline_timer.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/basic_resolver.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_socket_streambuf.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/basic_streambuf.hpp" +#include "asio/buffer.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_stream.hpp" +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/completion_condition.hpp" +#include "asio/datagram_socket_service.hpp" +#include "asio/deadline_timer_service.hpp" +#include "asio/deadline_timer.hpp" +#include "asio/error_handler.hpp" +#include "asio/error.hpp" +#include "asio/handler_alloc_hook.hpp" +#include "asio/handler_invoke_hook.hpp" +#include "asio/io_service.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver_entry.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/host_name.hpp" +#include "asio/ip/multicast.hpp" +#include "asio/ip/resolver_query_base.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/ip/udp.hpp" +#include "asio/is_read_buffered.hpp" +#include "asio/is_write_buffered.hpp" +#include "asio/placeholders.hpp" +#include "asio/read.hpp" +#include "asio/read_until.hpp" +#include "asio/resolver_service.hpp" +#include "asio/socket_acceptor_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/strand.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/streambuf.hpp" +#include "asio/system_exception.hpp" +#include "asio/thread.hpp" +#include "asio/time_traits.hpp" +#include "asio/write.hpp" + +#endif // ASIO_HPP diff --git a/library/include/libtorrent/asio/basic_datagram_socket.hpp b/library/include/libtorrent/asio/basic_datagram_socket.hpp new file mode 100644 index 000000000..f555ae506 --- /dev/null +++ b/library/include/libtorrent/asio/basic_datagram_socket.hpp @@ -0,0 +1,802 @@ +// +// basic_datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_DATAGRAM_SOCKET_HPP +#define ASIO_BASIC_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/datagram_socket_service.hpp" +#include "asio/error_handler.hpp" + +namespace asio { + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + */ +template > +class basic_datagram_socket + : public basic_socket +{ +public: + /// The native representation of a socket. + typedef typename Service::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + */ + explicit basic_datagram_socket(asio::io_service& io_service) + : basic_socket(io_service) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_socket(io_service, protocol) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_socket(io_service, endpoint) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_socket(io_service, protocol, native_socket) + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const Const_Buffers& buffers) + { + return this->service.send(this->implementation, buffers, 0, throw_error()); + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const Const_Buffers& buffers, + socket_base::message_flags flags) + { + return this->service.send(this->implementation, buffers, flags, + throw_error()); + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return this->service.send(this->implementation, buffers, flags, + error_handler); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const Const_Buffers& buffers, Handler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + */ + template + void async_send(const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + this->service.async_send(this->implementation, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const Const_Buffers& buffers, + const endpoint_type& destination) + { + return this->service.send_to(this->implementation, buffers, destination, 0, + throw_error()); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + */ + template + std::size_t send_to(const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + return this->service.send_to(this->implementation, buffers, destination, + flags, throw_error()); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Error_Handler error_handler) + { + return this->service.send_to(this->implementation, buffers, destination, + flags, error_handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send_to(const Const_Buffers& buffers, + const endpoint_type& destination, Handler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, 0, + handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_send_to(const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, + flags, handler); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const Mutable_Buffers& buffers) + { + return this->service.receive(this->implementation, buffers, 0, + throw_error()); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags) + { + return this->service.receive(this->implementation, buffers, flags, + throw_error()); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return this->service.receive(this->implementation, buffers, flags, + error_handler); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const Mutable_Buffers& buffers, Handler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + */ + template + void async_receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + this->service.async_receive(this->implementation, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint) + { + return this->service.receive_from(this->implementation, buffers, + sender_endpoint, 0, throw_error()); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. + */ + template + std::size_t receive_from(const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + return this->service.receive_from(this->implementation, buffers, + sender_endpoint, flags, throw_error()); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Error_Handler error_handler) + { + return this->service.receive_from(this->implementation, buffers, + sender_endpoint, flags, error_handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive_from(const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, Handler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, 0, handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_receive_from(const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Handler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, flags, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP diff --git a/library/include/libtorrent/asio/basic_deadline_timer.hpp b/library/include/libtorrent/asio/basic_deadline_timer.hpp new file mode 100644 index 000000000..7a2765a20 --- /dev/null +++ b/library/include/libtorrent/asio/basic_deadline_timer.hpp @@ -0,0 +1,309 @@ +// +// basic_deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_DEADLINE_TIMER_HPP +#define ASIO_BASIC_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/deadline_timer_service.hpp" +#include "asio/error.hpp" + +namespace asio { + +/// Provides waitable timer functionality. +/** + * The basic_deadline_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * Most applications will use the asio::deadline_timer typedef. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + * + * @sa @ref deadline_timer_reset + * + * @par Examples: + * Performing a blocking wait: + * @code + * // Construct a timer without setting an expiry time. + * asio::deadline_timer timer(io_service); + * + * // Set an expiry time relative to now. + * timer.expires_from_now(boost::posix_time::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait: + * @code + * void handler(const asio::error& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::deadline_timer timer(io_service, + * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + */ +template , + typename Service = deadline_timer_service > +class basic_deadline_timer + : public basic_io_object +{ +public: + /// The type used for reporting errors. + typedef asio::error error_type; + + /// The time traits type. + typedef Time_Traits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + */ + explicit basic_deadline_timer(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_deadline_timer(asio::io_service& io_service, + const time_type& expiry_time) + : basic_io_object(io_service) + { + this->service.expires_at(this->implementation, expiry_time); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_deadline_timer(asio::io_service& io_service, + const duration_type& expiry_time) + : basic_io_object(io_service) + { + this->service.expires_from_now(this->implementation, expiry_time); + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t cancel() + { + return this->service.cancel(this->implementation); + } + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_type expires_at() const + { + return this->service.expires_at(this->implementation); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t expires_at(const time_type& expiry_time) + { + return this->service.expires_at(this->implementation, expiry_time); + } + + /// Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration_type expires_from_now() const + { + return this->service.expires_from_now(this->implementation); + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t expires_from_now(const duration_type& expiry_time) + { + return this->service.expires_from_now(this->implementation, expiry_time); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::error Thrown on failure. + */ + void wait() + { + this->service.wait(this->implementation); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_wait(Handler handler) + { + this->service.async_wait(this->implementation, handler); + } +}; + +/** + * @page deadline_timer_reset Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_from_now(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_deadline_timer::expires_from_now() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error passed to it + * contains the value asio::error::operation_aborted. + * + * @sa asio::basic_deadline_timer + */ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DEADLINE_TIMER_HPP diff --git a/library/include/libtorrent/asio/basic_io_object.hpp b/library/include/libtorrent/asio/basic_io_object.hpp new file mode 100644 index 000000000..99d8e567f --- /dev/null +++ b/library/include/libtorrent/asio/basic_io_object.hpp @@ -0,0 +1,75 @@ +// +// basic_io_object.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_IO_OBJECT_HPP +#define ASIO_BASIC_IO_OBJECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Base class for all I/O objects. +template +class basic_io_object + : private noncopyable +{ +public: + /// The type of the service that will be used to provide I/O operations. + typedef Service service_type; + + /// The underlying implementation type of I/O object. + typedef typename service_type::implementation_type implementation_type; + + /// Get the io_service associated with the object. + /** + * This function may be used to obtain the io_service object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return service.io_service(); + } + +protected: + /// Construct a basic_io_object. + explicit basic_io_object(asio::io_service& io_service) + : service(asio::use_service(io_service)) + { + service.construct(implementation); + } + + /// Protected destructor to prevent deletion through this type. + ~basic_io_object() + { + service.destroy(implementation); + } + + // The backend service implementation. + service_type& service; + + // The underlying native implementation. + implementation_type implementation; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_IO_OBJECT_HPP diff --git a/library/include/libtorrent/asio/basic_resolver.hpp b/library/include/libtorrent/asio/basic_resolver.hpp new file mode 100644 index 000000000..5df89d545 --- /dev/null +++ b/library/include/libtorrent/asio/basic_resolver.hpp @@ -0,0 +1,252 @@ +// +// basic_resolver.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_RESOLVER_HPP +#define ASIO_BASIC_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/resolver_service.hpp" + +namespace asio { + +/// Provides endpoint resolution functionality. +/** + * The basic_resolver class template provides the ability to resolve a query + * to a list of endpoints. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + */ +template > +class basic_resolver + : public basic_io_object +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The query type. + typedef typename Protocol::resolver_query query; + + /// The iterator type. + typedef typename Protocol::resolver_iterator iterator; + + /// The type used for reporting errors. + typedef asio::error error_type; + + /// Constructor. + /** + * This constructor creates a basic_resolver. + * + * @param io_service The io_service object that the resolver will use to + * dispatch handlers for any asynchronous operations performed on the timer. + */ + explicit basic_resolver(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Cancel any asynchronous operations that are waiting on the resolver. + /** + * This function forces the completion of any pending asynchronous + * operations on the host resolver. The handler for each cancelled operation + * will be invoked with the asio::error::operation_aborted error code. + */ + void cancel() + { + return this->service.cancel(this->implementation); + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + iterator resolve(const query& q) + { + return this->service.resolve(this->implementation, q, throw_error()); + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + template + iterator resolve(const query& q, Error_Handler error_handler) + { + return this->service.resolve(this->implementation, q, error_handler); + } + + /// Asynchronously resolve a query to a list of entries. + /** + * This function is used to asynchronously resolve a query into a list of + * endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can be used to + * // traverse the list of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful resolve operation is guaranteed to pass at least one + * entry to the handler. + */ + template + void async_resolve(const query& q, Handler handler) + { + return this->service.async_resolve(this->implementation, q, handler); + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + iterator resolve(const endpoint_type& e) + { + return this->service.resolve(this->implementation, e, throw_error()); + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + template + iterator resolve(const endpoint_type& e, Error_Handler error_handler) + { + return this->service.resolve(this->implementation, e, error_handler); + } + + /// Asynchronously resolve an endpoint to a list of entries. + /** + * This function is used to asynchronously resolve an endpoint into a list of + * endpoint entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can be used to + * // traverse the list of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful resolve operation is guaranteed to pass at least one + * entry to the handler. + */ + template + void async_resolve(const endpoint_type& e, Handler handler) + { + return this->service.async_resolve(this->implementation, e, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_RESOLVER_HPP diff --git a/library/include/libtorrent/asio/basic_socket.hpp b/library/include/libtorrent/asio/basic_socket.hpp new file mode 100644 index 000000000..2cc31404c --- /dev/null +++ b/library/include/libtorrent/asio/basic_socket.hpp @@ -0,0 +1,919 @@ +// +// basic_socket.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_SOCKET_HPP +#define ASIO_BASIC_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/socket_base.hpp" + +namespace asio { + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Error_Source, IO_Object. + */ +template +class basic_socket + : public basic_io_object, + public socket_base +{ +public: + /// The native representation of a socket. + typedef typename Service::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The type used for reporting errors. + typedef asio::error error_type; + + /// A basic_socket is always the lowest layer. + typedef basic_socket lowest_layer_type; + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_io_object(io_service) + { + this->service.open(this->implementation, protocol, throw_error()); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_io_object(io_service) + { + this->service.open(this->implementation, endpoint.protocol(), + throw_error()); + this->service.bind(this->implementation, endpoint, throw_error()); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_io_object(io_service) + { + this->service.assign(this->implementation, protocol, native_socket, + throw_error()); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + this->service.open(this->implementation, protocol, throw_error()); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::error error; + * socket.open(asio::ip::tcp::v4(), asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void open(const protocol_type& protocol, Error_Handler error_handler) + { + this->service.open(this->implementation, protocol, error_handler); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws asio::error Thrown on failure. + */ + void assign(const protocol_type& protocol, const native_type& native_socket) + { + this->service.assign(this->implementation, protocol, native_socket, + throw_error()); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void assign(const protocol_type& protocol, const native_type& native_socket, + Error_Handler error_handler) + { + this->service.assign(this->implementation, protocol, native_socket, + error_handler); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @throws asio::error Thrown on failure. + */ + void close() + { + this->service.close(this->implementation, throw_error()); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error error; + * socket.close(asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void close(Error_Handler error_handler) + { + this->service.close(this->implementation, error_handler); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::error Thrown on failure. + */ + void cancel() + { + this->service.cancel(this->implementation, throw_error()); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void cancel(Error_Handler error_handler) + { + this->service.cancel(this->implementation, error_handler); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + this->service.bind(this->implementation, endpoint, throw_error()); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * asio::error error; + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345), + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void bind(const endpoint_type& endpoint, Error_Handler error_handler) + { + this->service.bind(this->implementation, endpoint, error_handler); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + this->service.connect(this->implementation, peer_endpoint, throw_error()); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * asio::error error; + * socket.connect(endpoint, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void connect(const endpoint_type& peer_endpoint, Error_Handler error_handler) + { + this->service.connect(this->implementation, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * @code + * void connect_handler(const asio::error& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template + void async_connect(const endpoint_type& peer_endpoint, Handler handler) + { + this->service.async_connect(this->implementation, peer_endpoint, handler); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws asio::error Thrown on failure. + * + * @sa Socket_Option @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example: + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const Socket_Option& option) + { + this->service.set_option(this->implementation, option, throw_error()); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example: + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option(true); + * asio::error error; + * socket.set_option(option, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void set_option(const Socket_Option& option, Error_Handler error_handler) + { + this->service.set_option(this->implementation, option, error_handler); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws asio::error Thrown on failure. + * + * @sa Socket_Option @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option) const + { + this->service.get_option(this->implementation, option, throw_error()); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::keep_alive option; + * asio::error error; + * socket.get_option(option, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Socket_Option& option, Error_Handler error_handler) const + { + this->service.get_option(this->implementation, option, error_handler); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws asio::error Thrown on failure. + * + * @sa IO_Control_Command @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command) + { + this->service.io_control(this->implementation, command, throw_error()); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @sa IO_Control_Command @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example: + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * asio::error error; + * socket.io_control(command, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IO_Control_Command& command, Error_Handler error_handler) + { + this->service.io_control(this->implementation, command, error_handler); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + return this->service.local_endpoint(this->implementation, throw_error()); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error error; + * asio::ip::tcp::endpoint endpoint + * = socket.local_endpoint(asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + endpoint_type local_endpoint(Error_Handler error_handler) const + { + return this->service.local_endpoint(this->implementation, error_handler); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + return this->service.remote_endpoint(this->implementation, throw_error()); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error error; + * asio::ip::tcp::endpoint endpoint + * = socket.remote_endpoint(asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + endpoint_type remote_endpoint(Error_Handler error_handler) const + { + return this->service.remote_endpoint(this->implementation, error_handler); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * socket.shutdown(asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + this->service.shutdown(this->implementation, what, throw_error()); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error error; + * socket.shutdown(asio::ip::tcp::socket::shutdown_send, + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void shutdown(shutdown_type what, Error_Handler error_handler) + { + this->service.shutdown(this->implementation, what, error_handler); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_socket() + { + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_HPP diff --git a/library/include/libtorrent/asio/basic_socket_acceptor.hpp b/library/include/libtorrent/asio/basic_socket_acceptor.hpp new file mode 100644 index 000000000..c646dc9d2 --- /dev/null +++ b/library/include/libtorrent/asio/basic_socket_acceptor.hpp @@ -0,0 +1,854 @@ +// +// basic_socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_SOCKET_ACCEPTOR_HPP +#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/basic_socket.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/socket_acceptor_service.hpp" +#include "asio/socket_base.hpp" + +namespace asio { + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor class template is used for accepting new socket + * connections. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + * + * @par Example: + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template > +class basic_socket_acceptor + : public basic_io_object, + public socket_base +{ +public: + /// The native representation of an acceptor. + typedef typename Service::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The type used for reporting errors. + typedef asio::error error_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + */ + explicit basic_socket_acceptor(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::error Thrown on failure. + */ + basic_socket_acceptor(asio::io_service& io_service, + const protocol_type& protocol) + : basic_io_object(io_service) + { + this->service.open(this->implementation, protocol, throw_error()); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(io_service); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(listen_backlog); + * @endcode + */ + basic_socket_acceptor(asio::io_service& io_service, + const endpoint_type& endpoint, bool reuse_addr = true) + : basic_io_object(io_service) + { + this->service.open(this->implementation, endpoint.protocol(), + throw_error()); + if (reuse_addr) + { + this->service.set_option(this->implementation, + socket_base::reuse_address(true), throw_error()); + } + this->service.bind(this->implementation, endpoint, throw_error()); + this->service.listen(this->implementation, + socket_base::max_connections, throw_error()); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::error Thrown on failure. + */ + basic_socket_acceptor(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_acceptor) + : basic_io_object(io_service) + { + this->service.assign(this->implementation, protocol, native_acceptor, + throw_error()); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + this->service.open(this->implementation, protocol, throw_error()); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * asio::error error; + * acceptor.open(asio::ip::tcp::v4(), + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void open(const protocol_type& protocol, Error_Handler error_handler) + { + this->service.open(this->implementation, protocol, error_handler); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::error Thrown on failure. + */ + void assign(const protocol_type& protocol, const native_type& native_acceptor) + { + this->service.assign(this->implementation, protocol, native_acceptor, + throw_error()); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void assign(const protocol_type& protocol, const native_type& native_acceptor, + Error_Handler error_handler) + { + this->service.assign(this->implementation, protocol, native_acceptor, + error_handler); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * acceptor.bind(asio::ip::tcp::endpoint(12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + this->service.bind(this->implementation, endpoint, throw_error()); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * asio::error error; + * acceptor.bind(asio::ip::tcp::endpoint(12345), + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void bind(const endpoint_type& endpoint, Error_Handler error_handler) + { + this->service.bind(this->implementation, endpoint, error_handler); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + */ + void listen(int backlog = socket_base::max_connections) + { + this->service.listen(this->implementation, backlog, throw_error()); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error error; + * acceptor.listen(asio::socket_base::max_connections, + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void listen(int backlog, Error_Handler error_handler) + { + this->service.listen(this->implementation, backlog, error_handler); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws asio::error Thrown on failure. + */ + void close() + { + this->service.close(this->implementation, throw_error()); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error error; + * acceptor.close(asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void close(Error_Handler error_handler) + { + this->service.close(this->implementation, error_handler); + } + + /// Get the native acceptor representation. + /** + * This function may be used to obtain the underlying representation of the + * acceptor. This is intended to allow access to native acceptor functionality + * that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::error Thrown on failure. + */ + void cancel() + { + this->service.cancel(this->implementation, throw_error()); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void cancel(Error_Handler error_handler) + { + this->service.cancel(this->implementation, error_handler); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws asio::error Thrown on failure. + * + * @sa Socket_Option @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example: + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template + void set_option(const Option& option) + { + this->service.set_option(this->implementation, option, throw_error()); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example: + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * asio::error error; + * acceptor.set_option(option, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void set_option(const Option& option, Error_Handler error_handler) + { + this->service.set_option(this->implementation, option, error_handler); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws asio::error Thrown on failure. + * + * @sa Socket_Option @n + * asio::socket_base::reuse_address + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Option& option) + { + this->service.get_option(this->implementation, option, throw_error()); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @sa Socket_Option @n + * asio::socket_base::reuse_address + * + * @par Example: + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * asio::error error; + * acceptor.get_option(option, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(Option& option, Error_Handler error_handler) + { + this->service.get_option(this->implementation, option, error_handler); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @returns An object that represents the local endpoint of the acceptor. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + return this->service.local_endpoint(this->implementation, throw_error()); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns An object that represents the local endpoint of the acceptor. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error error; + * asio::ip::tcp::endpoint endpoint + * = acceptor.local_endpoint(asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + endpoint_type local_endpoint(Error_Handler error_handler) const + { + return this->service.local_endpoint(this->implementation, error_handler); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * acceptor.accept(socket); + * @endcode + */ + template + void accept(basic_socket& peer) + { + this->service.accept(this->implementation, peer, throw_error()); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::soocket socket(io_service); + * asio::error error; + * acceptor.accept(socket, asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void accept(basic_socket& peer, + Error_Handler error_handler) + { + this->service.accept(this->implementation, peer, error_handler); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * @code + * void accept_handler(const asio::error& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ + template + void async_accept(basic_socket& peer, + Handler handler) + { + this->service.async_accept(this->implementation, peer, handler); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint; + * acceptor.accept_endpoint(socket, endpoint); + * @endcode + */ + template + void accept_endpoint(basic_socket& peer, + endpoint_type& peer_endpoint) + { + this->service.accept_endpoint(this->implementation, peer, peer_endpoint, + throw_error()); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @par Example: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint; + * asio::error error; + * acceptor.accept_endpoint(socket, endpoint, + * asio::assign_error(error)); + * if (error) + * { + * // An error occurred. + * } + * @endcode + */ + template + void accept_endpoint(basic_socket& peer, + endpoint_type& peer_endpoint, Error_Handler error_handler) + { + this->service.accept_endpoint(this->implementation, peer, peer_endpoint, + error_handler); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_accept_endpoint(basic_socket& peer, + endpoint_type& peer_endpoint, Handler handler) + { + this->service.async_accept_endpoint(this->implementation, peer, + peer_endpoint, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP diff --git a/library/include/libtorrent/asio/basic_socket_iostream.hpp b/library/include/libtorrent/asio/basic_socket_iostream.hpp new file mode 100644 index 000000000..052b6e603 --- /dev/null +++ b/library/include/libtorrent/asio/basic_socket_iostream.hpp @@ -0,0 +1,176 @@ +// +// basic_socket_iostream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_SOCKET_IOSTREAM_HPP +#define ASIO_BASIC_SOCKET_IOSTREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket_streambuf.hpp" +#include "asio/stream_socket_service.hpp" + +#if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) +#define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5 +#endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) + +// A macro that should expand to: +// template < typename T1, ..., typename Tn > +// explicit basic_socket_iostream( T1 x1, ..., Tn xn ) +// : basic_iostream(&this->boost::base_from_member< +// basic_socket_streambuf >::member) +// { +// try +// { +// rdbuf()->connect ( x1, ..., xn ); +// } +// catch (asio::error&) +// { +// this->setstate(std::ios_base::failbit); +// if (this->exceptions() & std::ios_base::failbit) +// throw; +// } +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \ + template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ + explicit basic_socket_iostream( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ + : std::basic_iostream(&this->boost::base_from_member< \ + basic_socket_streambuf >::member) \ + { \ + try \ + { \ + rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \ + } \ + catch (asio::error&) \ + { \ + this->setstate(std::ios_base::failbit); \ + if (this->exceptions() & std::ios_base::failbit) \ + throw; \ + } \ + } \ + /**/ + +// A macro that should expand to: +// template < typename T1, ..., typename Tn > +// void connect( T1 x1, ..., Tn xn ) +// { +// try +// { +// rdbuf()->connect ( x1, ..., xn ); +// } +// catch (asio::error&) +// { +// this->setstate(std::ios_base::failbit); +// if (this->exceptions() & std::ios_base::failbit) +// throw; +// } +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \ + template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ + void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ + { \ + try \ + { \ + rdbuf()->connect( BOOST_PP_ENUM_PARAMS(n, x) ); \ + } \ + catch (asio::error&) \ + { \ + this->setstate(std::ios_base::failbit); \ + if (this->exceptions() & std::ios_base::failbit) \ + throw; \ + } \ + } \ + /**/ + +namespace asio { + +/// Iostream interface for a socket. +template > +class basic_socket_iostream + : public boost::base_from_member >, + public std::basic_iostream +{ +public: + /// Construct a basic_socket_iostream without establishing a connection. + basic_socket_iostream() + : std::basic_iostream(&this->boost::base_from_member< + basic_socket_streambuf >::member) + { + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This constructor automatically establishes a connection based on the + * supplied resolver query parameters. The arguments are used to construct + * a resolver query object. + */ + template + explicit basic_socket_iostream(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), + ASIO_PRIVATE_CTR_DEF, _ ) +#endif + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + */ + template + void connect(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), + ASIO_PRIVATE_CONNECT_DEF, _ ) +#endif + + /// Close the connection. + void close() + { + rdbuf()->close(); + } + + /// Return a pointer to the underlying streambuf. + basic_socket_streambuf* rdbuf() const + { + return const_cast*>( + &this->boost::base_from_member< + basic_socket_streambuf >::member); + } +}; + +} // namespace asio + +#undef ASIO_PRIVATE_CTR_DEF +#undef ASIO_PRIVATE_CONNECT_DEF + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP diff --git a/library/include/libtorrent/asio/basic_socket_streambuf.hpp b/library/include/libtorrent/asio/basic_socket_streambuf.hpp new file mode 100644 index 000000000..c24e00140 --- /dev/null +++ b/library/include/libtorrent/asio/basic_socket_streambuf.hpp @@ -0,0 +1,280 @@ +// +// basic_socket_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_SOCKET_STREAMBUF_HPP +#define ASIO_BASIC_SOCKET_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/error_handler.hpp" +#include "asio/io_service.hpp" +#include "asio/stream_socket_service.hpp" + +#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) +#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 +#endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) + +// A macro that should expand to: +// template < typename T1, ..., typename Tn > +// explicit basic_socket_streambuf( T1 x1, ..., Tn xn ) +// : basic_socket( +// boost::base_from_member::member) +// { +// init_buffers(); +// typedef typename Protocol::resolver_query resolver_query; +// resolver_query query( x1, ..., xn ); +// resolve_and_connect(query); +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CTR_DEF( z, n, data ) \ + template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ + explicit basic_socket_streambuf( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ + : basic_socket( \ + boost::base_from_member::member) \ + { \ + init_buffers(); \ + typedef typename Protocol::resolver_query resolver_query; \ + resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \ + resolve_and_connect(query); \ + } \ + /**/ + +// A macro that should expand to: +// template < typename T1, ..., typename Tn > +// void connect( T1 x1, ..., Tn xn ) +// { +// this->basic_socket::close(); +// init_buffers(); +// typedef typename Protocol::resolver_query resolver_query; +// resolver_query query( x1, ..., xn ); +// resolve_and_connect(query); +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \ + template < BOOST_PP_ENUM_PARAMS(n, typename T) > \ + void connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, x) ) \ + { \ + this->basic_socket::close(); \ + init_buffers(); \ + typedef typename Protocol::resolver_query resolver_query; \ + resolver_query query( BOOST_PP_ENUM_PARAMS(n, x) ); \ + resolve_and_connect(query); \ + } \ + /**/ + +namespace asio { + +/// Iostream streambuf for a socket. +template > +class basic_socket_streambuf + : public std::streambuf, + private boost::base_from_member, + public basic_socket +{ +public: + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_socket_streambuf without establishing a connection. + basic_socket_streambuf() + : basic_socket( + boost::base_from_member::member) + { + init_buffers(); + } + + /// Establish a connection to the specified endpoint. + explicit basic_socket_streambuf(const endpoint_type& endpoint) + : basic_socket( + boost::base_from_member::member) + { + init_buffers(); + this->basic_socket::connect(endpoint); + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This constructor automatically establishes a connection based on the + * supplied resolver query parameters. The arguments are used to construct + * a resolver query object. + */ + template + explicit basic_socket_streambuf(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY), + ASIO_PRIVATE_CTR_DEF, _ ) +#endif + + /// Destructor flushes buffered data. + ~basic_socket_streambuf() + { + sync(); + } + + /// Establish a connection to the specified endpoint. + void connect(const endpoint_type& endpoint) + { + this->basic_socket::close(); + init_buffers(); + this->basic_socket::connect(endpoint); + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + */ + template + void connect(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY), + ASIO_PRIVATE_CONNECT_DEF, _ ) +#endif + + /// Close the connection. + void close() + { + sync(); + this->basic_socket::close(); + init_buffers(); + } + +protected: + int_type underflow() + { + if (gptr() == egptr()) + { + asio::error error; + std::size_t bytes_transferred = this->service.receive( + this->implementation, + asio::buffer(asio::buffer(get_buffer_) + putback_max), + 0, asio::assign_error(error)); + if (error) + { + if (error != asio::error::eof) + throw error; + return traits_type::eof(); + } + setg(get_buffer_.begin(), get_buffer_.begin() + putback_max, + get_buffer_.begin() + putback_max + bytes_transferred); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + int_type overflow(int_type c) + { + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + if (pptr() == epptr()) + { + asio::const_buffer buffer = + asio::buffer(pbase(), pptr() - pbase()); + while (asio::buffer_size(buffer) > 0) + { + std::size_t bytes_transferred = this->service.send( + this->implementation, asio::buffer(buffer), + 0, asio::throw_error()); + buffer = buffer + bytes_transferred; + } + setp(put_buffer_.begin(), put_buffer_.end()); + } + + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + + return traits_type::not_eof(c); + } + + int sync() + { + asio::const_buffer buffer = + asio::buffer(pbase(), pptr() - pbase()); + while (asio::buffer_size(buffer) > 0) + { + std::size_t bytes_transferred = this->service.send( + this->implementation, asio::buffer(buffer), + 0, asio::throw_error()); + buffer = buffer + bytes_transferred; + } + setp(put_buffer_.begin(), put_buffer_.end()); + return 0; + } + +private: + void init_buffers() + { + setg(get_buffer_.begin(), + get_buffer_.begin() + putback_max, + get_buffer_.begin() + putback_max); + setp(put_buffer_.begin(), put_buffer_.end()); + } + + void resolve_and_connect(const typename Protocol::resolver_query& query) + { + typedef typename Protocol::resolver resolver_type; + typedef typename Protocol::resolver_iterator iterator_type; + resolver_type resolver( + boost::base_from_member::member); + iterator_type iterator = resolver.resolve(query); + asio::error error(asio::error::host_not_found); + while (error && iterator != iterator_type()) + { + this->basic_socket::close(); + this->basic_socket::connect( + *iterator, asio::assign_error(error)); + ++iterator; + } + if (error) + throw error; + } + + enum { putback_max = 8 }; + enum { buffer_size = 512 }; + boost::array get_buffer_; + boost::array put_buffer_; +}; + +} // namespace asio + +#undef ASIO_PRIVATE_CTR_DEF +#undef ASIO_PRIVATE_CONNECT_DEF + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/library/include/libtorrent/asio/basic_stream_socket.hpp b/library/include/libtorrent/asio/basic_stream_socket.hpp new file mode 100644 index 000000000..c2cf3c3c7 --- /dev/null +++ b/library/include/libtorrent/asio/basic_stream_socket.hpp @@ -0,0 +1,816 @@ +// +// basic_stream_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_STREAM_SOCKET_HPP +#define ASIO_BASIC_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/error_handler.hpp" +#include "asio/stream_socket_service.hpp" + +namespace asio { + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Read_Stream, Async_Write_Stream, Error_Source, IO_Object, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template > +class basic_stream_socket + : public basic_socket +{ +public: + /// The native representation of a socket. + typedef typename Service::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket(asio::io_service& io_service) + : basic_socket(io_service) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_socket(io_service, protocol) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws asio::error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_socket(io_service, endpoint) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_socket(io_service, protocol, native_socket) + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const Const_Buffers& buffers) + { + return this->service.send(this->implementation, buffers, 0, throw_error()); + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const Const_Buffers& buffers, + socket_base::message_flags flags) + { + return this->service.send(this->implementation, buffers, flags, + throw_error()); + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes sent. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const Const_Buffers& buffers, + socket_base::message_flags flags, + Error_Handler error_handler) + { + return this->service.send(this->implementation, buffers, flags, + error_handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const Const_Buffers& buffers, Handler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example: + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + this->service.async_send(this->implementation, buffers, flags, handler); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const Mutable_Buffers& buffers) + { + return this->service.receive(this->implementation, buffers, 0, + throw_error()); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags) + { + return this->service.receive(this->implementation, buffers, flags, + throw_error()); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes received. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return this->service.receive(this->implementation, buffers, flags, + error_handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const Mutable_Buffers& buffers, Handler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example: + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + this->service.async_receive(this->implementation, buffers, flags, handler); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws asio::error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const Const_Buffers& buffers) + { + return this->service.send(this->implementation, buffers, 0, throw_error()); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return this->service.send(this->implementation, buffers, 0, error_handler); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return this->service.receive(this->implementation, buffers, 0, + throw_error()); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return this->service.receive(this->implementation, buffers, 0, + error_handler); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Peek at the incoming data on the stream socket. + /** + * This function is used to peek at the incoming data on the stream socket, + * without removing it from the input queue. The function call will block + * until data has been read successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::error Thrown on failure. + * + * @par Example: + * To peek using a single data buffer use the @ref buffer function as + * follows: + * @code socket.peek(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on using multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return this->service.receive(this->implementation, buffers, + socket_base::message_peek, throw_error()); + } + + /// Peek at the incoming data on the stream socket. + /** + * This function is used to peek at the incoming data on the stream socket, + * without removing it from the input queue. The function call will block + * until data has been read successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + */ + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return this->service.receive(this->implementation, buffers, + socket_base::message_peek, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream socket without blocking. + * + * @returns The number of bytes of data that can be read without blocking. + * + * @throws asio::error Thrown on failure. + */ + std::size_t in_avail() + { + socket_base::bytes_readable command; + this->service.io_control(this->implementation, command, throw_error()); + return command.get(); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream socket without blocking. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes of data that can be read without blocking. + */ + template + std::size_t in_avail(Error_Handler error_handler) + { + socket_base::bytes_readable command; + this->service.io_control(this->implementation, command, error_handler); + return command.get(); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAM_SOCKET_HPP diff --git a/library/include/libtorrent/asio/basic_streambuf.hpp b/library/include/libtorrent/asio/basic_streambuf.hpp new file mode 100644 index 000000000..49ce11a89 --- /dev/null +++ b/library/include/libtorrent/asio/basic_streambuf.hpp @@ -0,0 +1,200 @@ +// +// basic_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BASIC_STREAMBUF_HPP +#define ASIO_BASIC_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Automatically resizable buffer class based on std::streambuf. +template > +class basic_streambuf + : public std::streambuf, + private noncopyable +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The type used to represent the get area as a list of buffers. + typedef implementation_defined const_buffers_type; + + /// The type used to represent the put area as a list of buffers. + typedef implementation_defined mutable_buffers_type; +#else + typedef asio::const_buffer_container_1 const_buffers_type; + typedef asio::mutable_buffer_container_1 mutable_buffers_type; +#endif + + /// Construct a buffer with a specified maximum size. + explicit basic_streambuf( + std::size_t max_size = (std::numeric_limits::max)(), + const Allocator& allocator = Allocator()) + : max_size_(max_size), + buffer_(allocator) + { + std::size_t pend = (std::min)(max_size_, buffer_delta); + buffer_.resize((std::max)(pend, 1)); + setg(&buffer_[0], &buffer_[0], &buffer_[0]); + setp(&buffer_[0], &buffer_[0] + pend); + } + + /// Return the size of the get area in characters. + std::size_t size() const + { + return pptr() - gptr(); + } + + /// Return the maximum size of the buffer. + std::size_t max_size() const + { + return max_size_; + } + + /// Get a list of buffers that represents the get area. + const_buffers_type data() const + { + return asio::buffer(asio::const_buffer(gptr(), + (pptr() - gptr()) * sizeof(char_type))); + } + + /// Get a list of buffers that represents the put area, with the given size. + mutable_buffers_type prepare(std::size_t size) + { + reserve(size); + return asio::buffer(asio::mutable_buffer( + pptr(), size * sizeof(char_type))); + } + + /// Move the start of the put area by the specified number of characters. + void commit(std::size_t n) + { + if (pptr() + n > epptr()) + n = epptr() - pptr(); + pbump(static_cast(n)); + } + + /// Move the start of the get area by the specified number of characters. + void consume(std::size_t n) + { + while (n > 0) + { + sbumpc(); + --n; + } + } + +protected: + enum { buffer_delta = 128 }; + + int_type underflow() + { + if (gptr() < pptr()) + { + setg(&buffer_[0], gptr(), pptr()); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + int_type overflow(int_type c) + { + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + if (pptr() == epptr()) + { + std::size_t buffer_size = pptr() - gptr(); + if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) + { + reserve(max_size_ - buffer_size); + } + else + { + reserve(buffer_delta); + } + } + + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + + return traits_type::not_eof(c); + } + + void reserve(std::size_t n) + { + // Get current stream positions as offsets. + std::size_t gnext = gptr() - &buffer_[0]; + std::size_t gend = egptr() - &buffer_[0]; + std::size_t pnext = pptr() - &buffer_[0]; + std::size_t pend = epptr() - &buffer_[0]; + + // Check if there is already enough space in the put area. + if (n <= pend - pnext) + { + return; + } + + // Shift existing contents of get area to start of buffer. + if (gnext > 0) + { + std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend); + gend -= gnext; + pnext -= gnext; + } + + // Ensure buffer is large enough to hold at least the specified size. + if (n > pend - pnext) + { + if (n <= max_size_ && pnext <= max_size_ - n) + { + buffer_.resize((std::max)(pnext + n, 1)); + } + else + { + throw std::length_error("asio::streambuf too long"); + } + } + + // Update stream positions. + setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend); + setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n); + } + +private: + std::size_t max_size_; + std::vector buffer_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAMBUF_HPP diff --git a/library/include/libtorrent/asio/buffer.hpp b/library/include/libtorrent/asio/buffer.hpp new file mode 100644 index 000000000..bd6442432 --- /dev/null +++ b/library/include/libtorrent/asio/buffer.hpp @@ -0,0 +1,639 @@ +// +// buffer.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFER_HPP +#define ASIO_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +class mutable_buffer; +class const_buffer; + +namespace detail { +void* buffer_cast_helper(const mutable_buffer&); +const void* buffer_cast_helper(const const_buffer&); +std::size_t buffer_size_helper(const mutable_buffer&); +std::size_t buffer_size_helper(const const_buffer&); +} // namespace detail + +/// Holds a buffer that can be modified. +/** + * The mutable_buffer class provides a safe representation of a buffer that can + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class mutable_buffer +{ +public: + /// Construct an empty buffer. + mutable_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + mutable_buffer(void* data, std::size_t size) + : data_(data), + size_(size) + { + } + +private: + friend void* asio::detail::buffer_cast_helper( + const mutable_buffer& b); + friend std::size_t asio::detail::buffer_size_helper( + const mutable_buffer& b); + + void* data_; + std::size_t size_; +}; + +namespace detail { + +inline void* buffer_cast_helper(const mutable_buffer& b) +{ + return b.data_; +} + +inline std::size_t buffer_size_helper(const mutable_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates mutable_buffer + */ +template +inline Pointer_To_Pod_Type buffer_cast(const mutable_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates mutable_buffer + */ +inline std::size_t buffer_size(const mutable_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size); +} + +/// Adapts a single modifiable buffer so that it meets the requirements of the +/// Mutable_Buffers concept. +class mutable_buffer_container_1 + : public mutable_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Construct to represent a single modifiable buffer. + explicit mutable_buffer_container_1(const mutable_buffer& b) + : mutable_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +/// Holds a buffer that cannot be modified. +/** + * The const_buffer class provides a safe representation of a buffer that cannot + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class const_buffer +{ +public: + /// Construct an empty buffer. + const_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + const_buffer(const void* data, std::size_t size) + : data_(data), + size_(size) + { + } + + /// Construct a non-modifiable buffer from a modifiable one. + const_buffer(const mutable_buffer& b) + : data_(asio::detail::buffer_cast_helper(b)), + size_(asio::detail::buffer_size_helper(b)) + { + } + +private: + friend const void* asio::detail::buffer_cast_helper( + const const_buffer& b); + friend std::size_t asio::detail::buffer_size_helper( + const const_buffer& b); + + const void* data_; + std::size_t size_; +}; + +namespace detail { + +inline const void* buffer_cast_helper(const const_buffer& b) +{ + return b.data_; +} + +inline std::size_t buffer_size_helper(const const_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates const_buffer + */ +template +inline Pointer_To_Pod_Type buffer_cast(const const_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates const_buffer + */ +inline std::size_t buffer_size(const const_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(const const_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(std::size_t start, const const_buffer& b) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size); +} + +/// Adapts a single non-modifiable buffer so that it meets the requirements of +/// the Const_Buffers concept. +class const_buffer_container_1 + : public const_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef const_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const const_buffer* const_iterator; + + /// Construct to represent a single non-modifiable buffer. + explicit const_buffer_container_1(const const_buffer& b) + : const_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +/** @defgroup buffer asio::buffer + * + * @brief The asio::buffer function is used to create a buffer object to + * represent raw memory, an array of POD elements, or a vector of POD elements. + * + * The simplest use case involves reading or writing a single buffer of a + * specified size: + * + * @code sock.write(asio::buffer(data, size)); @endcode + * + * In the above example, the return value of asio::buffer meets the + * requirements of the Const_Buffers concept so that it may be directly passed + * to the socket's write function. A buffer created for modifiable memory also + * meets the requirements of the Mutable_Buffers concept. + * + * An individual buffer may be created from a builtin array, std::vector or + * boost::array of POD elements. This helps prevent buffer overruns by + * automatically determining the size of the buffer: + * + * @code char d1[128]; + * size_t bytes_transferred = sock.read(asio::buffer(d1)); + * + * std::vector d2(128); + * bytes_transferred = sock.read(asio::buffer(d2)); + * + * boost::array d3; + * bytes_transferred = sock.read(asio::buffer(d3)); @endcode + * + * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple + * buffer objects may be assigned into a container that supports the + * Mutable_Buffers (for read) or Const_Buffers (for write) concepts: + * + * @code + * char d1[128]; + * std::vector d2(128); + * boost::array d3; + * + * boost::array bufs1 = { + * asio::buffer(d1), + * asio::buffer(d2), + * asio::buffer(d3) }; + * bytes_transferred = sock.read(bufs1); + * + * std::vector bufs2; + * bufs2.push_back(asio::buffer(d1)); + * bufs2.push_back(asio::buffer(d2)); + * bufs2.push_back(asio::buffer(d3)); + * bytes_transferred = sock.write(bufs2); @endcode + */ +/*@{*/ + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffer_container_1 buffer(const mutable_buffer& b) +{ + return mutable_buffer_container_1(b); +} + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffer_container_1 buffer(const mutable_buffer& b, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffer_container_1 buffer(const const_buffer& b) +{ + return const_buffer_container_1(b); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffer_container_1 buffer(const const_buffer& b, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given memory range. +inline mutable_buffer_container_1 buffer(void* data, std::size_t size_in_bytes) +{ + return mutable_buffer_container_1(mutable_buffer(data, size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given memory range. +inline const_buffer_container_1 buffer(const void* data, + std::size_t size_in_bytes) +{ + return const_buffer_container_1(const_buffer(data, size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N]) +{ + return mutable_buffer_container_1(mutable_buffer(data, N * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(Pod_Type (&data)[N], + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(data, + N * sizeof(Pod_Type) < max_size_in_bytes + ? N * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const Pod_Type (&data)[N]) +{ + return const_buffer_container_1(const_buffer(data, N * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const Pod_Type (&data)[N], + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data, + N * sizeof(Pod_Type) < max_size_in_bytes + ? N * sizeof(Pod_Type) : max_size_in_bytes)); +} + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +// Borland C++ thinks the overloads: +// +// unspecified buffer(boost::array& array ...); +// +// and +// +// unspecified buffer(boost::array& array ...); +// +// are ambiguous. This will be worked around by using a buffer_types traits +// class that contains typedefs for the appropriate buffer and container +// classes, based on whether Pod_Type is const or non-const. + +namespace detail { + +template +struct buffer_types_base; + +template <> +struct buffer_types_base +{ + typedef mutable_buffer buffer_type; + typedef mutable_buffer_container_1 container_type; +}; + +template <> +struct buffer_types_base +{ + typedef const_buffer buffer_type; + typedef const_buffer_container_1 container_type; +}; + +template +struct buffer_types + : public buffer_types_base::value> +{ +}; + +} // namespace detail + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data) +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), data.size() * sizeof(Pod_Type))); +} + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data, std::size_t max_size_in_bytes) +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(boost::array& data) +{ + return mutable_buffer_container_1( + mutable_buffer(data.c_array(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffer_container_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(data.c_array(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(boost::array& data) +{ + return const_buffer_container_1( + const_buffer(data.data(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const boost::array& data) +{ + return const_buffer_container_1( + const_buffer(data.data(), data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffer_container_1 buffer(const boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffer_container_1 buffer(std::vector& data) +{ + return mutable_buffer_container_1( + mutable_buffer(&data[0], data.size() * sizeof(Pod_Type))); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffer_container_1 buffer(std::vector& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffer_container_1( + mutable_buffer(&data[0], + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer( + const std::vector& data) +{ + return const_buffer_container_1( + const_buffer(&data[0], data.size() * sizeof(Pod_Type))); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffer_container_1 buffer( + const std::vector& data, std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(&data[0], + data.size() * sizeof(Pod_Type) < max_size_in_bytes + ? data.size() * sizeof(Pod_Type) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffer_container_1 buffer(const std::string& data) +{ + return const_buffer_container_1(const_buffer(data.data(), data.size())); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffer_container_1 buffer(const std::string& data, + std::size_t max_size_in_bytes) +{ + return const_buffer_container_1( + const_buffer(data.data(), + data.size() < max_size_in_bytes + ? data.size() : max_size_in_bytes)); +} + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFER_HPP diff --git a/library/include/libtorrent/asio/buffered_read_stream.hpp b/library/include/libtorrent/asio/buffered_read_stream.hpp new file mode 100644 index 000000000..6260d22a0 --- /dev/null +++ b/library/include/libtorrent/asio/buffered_read_stream.hpp @@ -0,0 +1,407 @@ +// +// buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_READ_STREAM_HPP +#define ASIO_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_resize_guard.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the read-related operations of a stream. +/** + * The buffered_read_stream class template can be used to add buffering to the + * synchronous and asynchronous read operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_read_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_read_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_read_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + next_layer_.close(error_handler); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return next_layer_.write_some(buffers, error_handler); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + next_layer_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size))); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred and the + /// error handler did not throw. + template + std::size_t fill(Error_Handler error_handler) + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + error_handler)); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + template + class fill_handler + { + public: + fill_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + std::size_t previous_size, Handler handler) + : io_service_(io_service), + storage_(storage), + previous_size_(previous_size), + handler_(handler) + { + } + + template + void operator()(const Error& e, std::size_t bytes_transferred) + { + storage_.resize(previous_size_ + bytes_transferred); + io_service_.dispatch(detail::bind_handler( + handler_, e, bytes_transferred)); + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + std::size_t previous_size_; + Handler handler_; + }; + + /// Start an asynchronous fill. + template + void async_fill(Handler handler) + { + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + next_layer_.async_read_some( + buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + fill_handler(io_service(), storage_, previous_size, handler)); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + if (storage_.empty()) + fill(); + return copy(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + if (storage_.empty() && !fill(error_handler)) + return 0; + return copy(buffers); + } + + template + class read_some_handler + { + public: + read_some_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + const Mutable_Buffers& buffers, Handler handler) + : io_service_(io_service), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t) + { + if (e || storage_.empty()) + { + std::size_t length = 0; + io_service_.dispatch(detail::bind_handler(handler_, e, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), + storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied)); + } + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + Mutable_Buffers buffers_; + Handler handler_; + }; + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + if (storage_.empty()) + { + async_fill(read_some_handler( + io_service(), storage_, buffers, handler)); + } + else + { + std::size_t length = copy(buffers); + io_service().post(detail::bind_handler(handler, 0, length)); + } + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + if (storage_.empty()) + fill(); + return peek_copy(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + if (storage_.empty() && !fill(error_handler)) + return 0; + return peek_copy(buffers); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return storage_.size(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return storage_.size(); + } + +private: + /// Copy data out of the internal buffer to the specified target buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const Mutable_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + return bytes_copied; + } + + /// Copy data from the internal buffer to the specified target buffer, without + /// removing the data from the internal buffer. Returns the number of bytes + /// copied. + template + std::size_t peek_copy(const Mutable_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_HPP diff --git a/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp new file mode 100644 index 000000000..7cdf04bcf --- /dev/null +++ b/library/include/libtorrent/asio/buffered_read_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_read_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_READ_STREAM_FWD_HPP +#define ASIO_BUFFERED_READ_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_read_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/library/include/libtorrent/asio/buffered_stream.hpp b/library/include/libtorrent/asio/buffered_stream.hpp new file mode 100644 index 000000000..1948fef09 --- /dev/null +++ b/library/include/libtorrent/asio/buffered_stream.hpp @@ -0,0 +1,249 @@ +// +// buffered_stream.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_STREAM_HPP +#define ASIO_BUFFERED_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the read- and write-related operations of a stream. +/** + * The buffered_stream class template can be used to add buffering to the + * synchronous and asynchronous read and write operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a) + : inner_stream_impl_(a), + stream_impl_(inner_stream_impl_) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a, std::size_t read_buffer_size, + std::size_t write_buffer_size) + : inner_stream_impl_(a, write_buffer_size), + stream_impl_(inner_stream_impl_, read_buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return stream_impl_.next_layer().next_layer(); + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return stream_impl_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return stream_impl_.io_service(); + } + + /// Close the stream. + void close() + { + stream_impl_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + stream_impl_.close(error_handler); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + return stream_impl_.next_layer().flush(); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred and the error handler did not throw. + template + std::size_t flush(Error_Handler error_handler) + { + return stream_impl_.next_layer().flush(error_handler); + } + + /// Start an asynchronous flush. + template + void async_flush(Handler handler) + { + return stream_impl_.next_layer().async_flush(handler); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + return stream_impl_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return stream_impl_.write_some(buffers, error_handler); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + stream_impl_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + return stream_impl_.fill(); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred and the + /// error handler did not throw. + template + std::size_t fill(Error_Handler error_handler) + { + return stream_impl_.fill(error_handler); + } + + /// Start an asynchronous fill. + template + void async_fill(Handler handler) + { + stream_impl_.async_fill(handler); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return stream_impl_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return stream_impl_.read_some(buffers, error_handler); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + stream_impl_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return stream_impl_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return stream_impl_.peek(buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return stream_impl_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return stream_impl_.in_avail(error_handler); + } + +private: + // The buffered write stream. + typedef buffered_write_stream write_stream_type; + write_stream_type inner_stream_impl_; + + // The buffered read stream. + typedef buffered_read_stream read_stream_type; + read_stream_type stream_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_HPP diff --git a/library/include/libtorrent/asio/buffered_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_stream_fwd.hpp new file mode 100644 index 000000000..4590ae8e3 --- /dev/null +++ b/library/include/libtorrent/asio/buffered_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_STREAM_FWD_HPP +#define ASIO_BUFFERED_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/library/include/libtorrent/asio/buffered_write_stream.hpp b/library/include/libtorrent/asio/buffered_write_stream.hpp new file mode 100644 index 000000000..d4bd49b92 --- /dev/null +++ b/library/include/libtorrent/asio/buffered_write_stream.hpp @@ -0,0 +1,362 @@ +// +// buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_WRITE_STREAM_HPP +#define ASIO_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/write.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the write-related operations of a stream. +/** + * The buffered_write_stream class template can be used to add buffering to the + * synchronous and asynchronous write operations of a stream. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class buffered_write_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_write_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_write_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + template + void close(Error_Handler error_handler) + { + next_layer_.close(error_handler); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size())); + storage_.consume(bytes_written); + return bytes_written; + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred and the error handler did not throw. + template + std::size_t flush(Error_Handler error_handler) + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size()), + transfer_all(), error_handler); + storage_.consume(bytes_written); + return bytes_written; + } + + template + class flush_handler + { + public: + flush_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, Handler handler) + : io_service_(io_service), + storage_(storage), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t bytes_written) + { + storage_.consume(bytes_written); + io_service_.dispatch(detail::bind_handler(handler_, e, bytes_written)); + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + Handler handler_; + }; + + /// Start an asynchronous flush. + template + void async_flush(Handler handler) + { + async_write(next_layer_, buffer(storage_.data(), storage_.size()), + flush_handler(io_service(), storage_, handler)); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const Const_Buffers& buffers) + { + if (storage_.size() == storage_.capacity()) + flush(); + return copy(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + if (storage_.size() == storage_.capacity() && !flush(error_handler)) + return 0; + return copy(buffers); + } + + template + class write_some_handler + { + public: + write_some_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + const Const_Buffers& buffers, Handler handler) + : io_service_(io_service), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const error_type& e, std::size_t) + { + if (e) + { + std::size_t length = 0; + io_service_.dispatch(detail::bind_handler(handler_, e, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + io_service_.dispatch(detail::bind_handler(handler_, e, bytes_copied)); + } + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + Const_Buffers buffers_; + Handler handler_; + }; + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + if (storage_.size() == storage_.capacity()) + { + async_flush(write_some_handler( + io_service(), storage_, buffers, handler)); + } + else + { + std::size_t bytes_copied = copy(buffers); + io_service().post(detail::bind_handler(handler, 0, bytes_copied)); + } + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return next_layer_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred and the error handler did not throw an exception. + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return next_layer_.read_some(buffers, error_handler); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + next_layer_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return next_layer_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return next_layer_.peek(buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return next_layer_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(Error_Handler error_handler) + { + return next_layer_.in_avail(error_handler); + } + +private: + /// Copy data into the internal buffer from the specified source buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const Const_Buffers& buffers) + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_HPP diff --git a/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp b/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp new file mode 100644 index 000000000..6cabef816 --- /dev/null +++ b/library/include/libtorrent/asio/buffered_write_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_write_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_BUFFERED_WRITE_STREAM_FWD_HPP +#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_write_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/library/include/libtorrent/asio/completion_condition.hpp b/library/include/libtorrent/asio/completion_condition.hpp new file mode 100644 index 000000000..3f8f7b611 --- /dev/null +++ b/library/include/libtorrent/asio/completion_condition.hpp @@ -0,0 +1,101 @@ +// +// completion_condition.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_COMPLETION_CONDITION_HPP +#define ASIO_COMPLETION_CONDITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +namespace detail { + +class transfer_all_t +{ +public: + typedef bool result_type; + + template + bool operator()(const Error& err, std::size_t) + { + return !!err; + } +}; + +class transfer_at_least_t +{ +public: + typedef bool result_type; + + explicit transfer_at_least_t(std::size_t minimum) + : minimum_(minimum) + { + } + + template + bool operator()(const Error& err, std::size_t bytes_transferred) + { + return !!err || bytes_transferred >= minimum_; + } + +private: + std::size_t minimum_; +}; + +} // namespace detail + +/** + * @defgroup completion_condition Completion Condition Function Objects + * + * Function objects used for determining when a read or write operation should + * complete. + */ +/*@{*/ + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until all of the data has been transferred, +/// or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_all(); +#else +inline detail::transfer_all_t transfer_all() +{ + return detail::transfer_all_t(); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until a minimum number of bytes has been +/// transferred, or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_at_least(std::size_t minimum); +#else +inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) +{ + return detail::transfer_at_least_t(minimum); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_COMPLETION_CONDITION_HPP diff --git a/library/include/libtorrent/asio/datagram_socket_service.hpp b/library/include/libtorrent/asio/datagram_socket_service.hpp new file mode 100644 index 000000000..77a3af42d --- /dev/null +++ b/library/include/libtorrent/asio/datagram_socket_service.hpp @@ -0,0 +1,295 @@ +// +// datagram_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DATAGRAM_SOCKET_SERVICE_HPP +#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/reactive_socket_service.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a datagram socket. +template +class datagram_socket_service + : public asio::io_service::service +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a datagram socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new datagram socket service for the specified io_service. + explicit datagram_socket_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new datagram socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a datagram socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + // Open a new datagram socket implementation. + template + void open(implementation_type& impl, const protocol_type& protocol, + Error_Handler error_handler) + { + if (protocol.type() == SOCK_DGRAM) + service_impl_.open(impl, protocol, error_handler); + else + error_handler(asio::error(asio::error::invalid_argument)); + } + + /// Assign an existing native socket to a datagram socket. + template + void assign(implementation_type& impl, const protocol_type& protocol, + const native_type& native_socket, Error_Handler error_handler) + { + service_impl_.assign(impl, protocol, native_socket, error_handler); + } + + /// Close a datagram socket implementation. + template + void close(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + /// Get the native socket implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + + // Bind the datagram socket to the specified local endpoint. + template + void bind(implementation_type& impl, const endpoint_type& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Connect the datagram socket to the specified endpoint. + template + void connect(implementation_type& impl, const endpoint_type& peer_endpoint, + Error_Handler error_handler) + { + service_impl_.connect(impl, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + void set_option(implementation_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Get a socket option. + template + void get_option(const implementation_type& impl, Option& option, + Error_Handler error_handler) const + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Perform an IO control command on the socket. + template + void io_control(implementation_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + service_impl_.io_control(impl, command, error_handler); + } + + /// Get the local endpoint. + template + endpoint_type local_endpoint(const implementation_type& impl, + Error_Handler error_handler) const + { + endpoint_type endpoint; + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + return endpoint; + } + + /// Get the remote endpoint. + template + endpoint_type remote_endpoint(const implementation_type& impl, + Error_Handler error_handler) const + { + endpoint_type endpoint; + service_impl_.get_remote_endpoint(impl, endpoint, error_handler); + return endpoint; + } + + /// Disable sends or receives on the socket. + template + void shutdown(implementation_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, what, error_handler); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.send(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous send. + template + void async_send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + template + std::size_t send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Error_Handler error_handler) + { + return service_impl_.send_to(impl, buffers, destination, flags, + error_handler); + } + + /// Start an asynchronous send. + template + void async_send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + service_impl_.async_send_to(impl, buffers, destination, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.receive(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous receive. + template + void async_receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + template + std::size_t receive_from(implementation_type& impl, + const Mutable_Buffers& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, + error_handler); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + void async_receive_from(implementation_type& impl, + const Mutable_Buffers& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags, + handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP diff --git a/library/include/libtorrent/asio/deadline_timer.hpp b/library/include/libtorrent/asio/deadline_timer.hpp new file mode 100644 index 000000000..7deafa60b --- /dev/null +++ b/library/include/libtorrent/asio/deadline_timer.hpp @@ -0,0 +1,37 @@ +// +// deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DEADLINE_TIMER_HPP +#define ASIO_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_deadline_timer.hpp" + +namespace asio { + +/// Typedef for the typical usage of timer. +typedef basic_deadline_timer deadline_timer; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DEADLINE_TIMER_HPP diff --git a/library/include/libtorrent/asio/deadline_timer_service.hpp b/library/include/libtorrent/asio/deadline_timer_service.hpp new file mode 100644 index 000000000..2415d9be6 --- /dev/null +++ b/library/include/libtorrent/asio/deadline_timer_service.hpp @@ -0,0 +1,152 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/time_traits.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" + +namespace asio { + +/// Default service implementation for a timer. +template > +class deadline_timer_service + : public asio::io_service::service +{ +public: + /// The time traits type. + typedef Time_Traits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::deadline_timer_service< + traits_type, detail::select_reactor > service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::deadline_timer_service< + traits_type, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::deadline_timer_service< + traits_type, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::deadline_timer_service< + traits_type, detail::select_reactor > service_impl_type; +#endif + +public: + /// The implementation type of the deadline timer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new timer service for the specified io_service. + explicit deadline_timer_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new timer implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a timer implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl) + { + return service_impl_.cancel(impl); + } + + /// Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return service_impl_.expires_at(impl); + } + + /// Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time) + { + return service_impl_.expires_at(impl, expiry_time); + } + + /// Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return service_impl_.expires_from_now(impl); + } + + /// Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time) + { + return service_impl_.expires_from_now(impl, expiry_time); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl) + { + service_impl_.wait(impl); + } + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, Handler handler) + { + service_impl_.async_wait(impl, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/bind_handler.hpp b/library/include/libtorrent/asio/detail/bind_handler.hpp new file mode 100644 index 000000000..59c2bcef2 --- /dev/null +++ b/library/include/libtorrent/asio/detail/bind_handler.hpp @@ -0,0 +1,349 @@ +// +// bind_handler.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_BIND_HANDLER_HPP +#define ASIO_DETAIL_BIND_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template +class binder1 +{ +public: + binder1(const Handler& handler, const Arg1& arg1) + : handler_(handler), + arg1_(arg1) + { + } + + void operator()() + { + handler_(arg1_); + } + + void operator()() const + { + handler_(arg1_); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder1* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder1 bind_handler(const Handler& handler, + const Arg1& arg1) +{ + return binder1(handler, arg1); +} + +template +class binder2 +{ +public: + binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2) + : handler_(handler), + arg1_(arg1), + arg2_(arg2) + { + } + + void operator()() + { + handler_(arg1_, arg2_); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder2* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder2 bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2) +{ + return binder2(handler, arg1, arg2); +} + +template +class binder3 +{ +public: + binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder3* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder3* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder3 bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) +{ + return binder3(handler, arg1, arg2, arg3); +} + +template +class binder4 +{ +public: + binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder4* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder4* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder4 bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) +{ + return binder4(handler, arg1, arg2, arg3, + arg4); +} + +template +class binder5 +{ +public: + binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder5* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder5* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder5 bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) +{ + return binder5(handler, arg1, arg2, + arg3, arg4, arg5); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BIND_HANDLER_HPP diff --git a/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp b/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 000000000..d72753010 --- /dev/null +++ b/library/include/libtorrent/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,70 @@ +// +// buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ + != std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ + = std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp b/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 000000000..17517f9b5 --- /dev/null +++ b/library/include/libtorrent/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,127 @@ +// +// buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + byte_type* data() + { + return &buffer_[0] + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const byte_type* data() const + { + return &buffer_[0] + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + assert(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + assert(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector buffer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/library/include/libtorrent/asio/detail/call_stack.hpp b/library/include/libtorrent/asio/detail/call_stack.hpp new file mode 100644 index 000000000..37256ee59 --- /dev/null +++ b/library/include/libtorrent/asio/detail/call_stack.hpp @@ -0,0 +1,90 @@ +// +// call_stack.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_CALL_STACK_HPP +#define ASIO_DETAIL_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/tss_ptr.hpp" + +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of io_service::run() for a specified io_service object. +template +class call_stack +{ +public: + // Context class automatically pushes an owner on to the stack. + class context + : private noncopyable + { + public: + // Push the owner on to the stack. + explicit context(Owner* d) + : owner_(d), + next_(call_stack::top_) + { + call_stack::top_ = this; + } + + // Pop the owner from the stack. + ~context() + { + call_stack::top_ = next_; + } + + private: + friend class call_stack; + + // The owner associated with the context. + Owner* owner_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified owner is on the stack. + static bool contains(Owner* d) + { + context* elem = top_; + while (elem) + { + if (elem->owner_ == d) + return true; + elem = elem->next_; + } + return false; + } + +private: + // The top of the stack of calls for the current thread. + static tss_ptr top_; +}; + +template +tss_ptr::context> +call_stack::top_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CALL_STACK_HPP diff --git a/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp b/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp new file mode 100644 index 000000000..f48d2df85 --- /dev/null +++ b/library/include/libtorrent/asio/detail/const_buffers_iterator.hpp @@ -0,0 +1,150 @@ +// +// const_buffers_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP +#define ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" + +namespace asio { +namespace detail { + +// A proxy iterator for a sub-range in a list of buffers. +template +class const_buffers_iterator + : public boost::iterator_facade, + const char, boost::bidirectional_traversal_tag> +{ +public: + // Default constructor creates an iterator in an undefined state. + const_buffers_iterator() + { + } + + // Create an iterator for the specified position. + const_buffers_iterator(const Const_Buffers& buffers, std::size_t position) + : begin_(buffers.begin()), + current_(buffers.begin()), + end_(buffers.end()), + position_(0) + { + while (current_ != end_) + { + current_buffer_ = *current_; + std::size_t buffer_size = asio::buffer_size(current_buffer_); + if (position - position_ < buffer_size) + { + current_buffer_position_ = position - position_; + position_ = position; + return; + } + position_ += buffer_size; + ++current_; + } + current_buffer_ = asio::const_buffer(); + current_buffer_position_ = 0; + } + + std::size_t position() const + { + return position_; + } + +private: + friend class boost::iterator_core_access; + + void increment() + { + if (current_ == end_) + return; + + ++position_; + + ++current_buffer_position_; + if (current_buffer_position_ != asio::buffer_size(current_buffer_)) + return; + + ++current_; + current_buffer_position_ = 0; + while (current_ != end_) + { + current_buffer_ = *current_; + if (asio::buffer_size(current_buffer_) > 0) + return; + ++current_; + } + } + + void decrement() + { + if (position_ == 0) + return; + + --position_; + + if (current_buffer_position_ != 0) + { + --current_buffer_position_; + return; + } + + typename Const_Buffers::const_iterator iter = current_; + while (iter != begin_) + { + --iter; + asio::const_buffer buffer = *iter; + std::size_t buffer_size = asio::buffer_size(buffer); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size - 1; + return; + } + } + } + + bool equal(const const_buffers_iterator& other) const + { + return position_ == other.position_; + } + + const char& dereference() const + { + return asio::buffer_cast( + current_buffer_)[current_buffer_position_]; + } + + asio::const_buffer current_buffer_; + std::size_t current_buffer_position_; + typename Const_Buffers::const_iterator begin_; + typename Const_Buffers::const_iterator current_; + typename Const_Buffers::const_iterator end_; + std::size_t position_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP diff --git a/library/include/libtorrent/asio/detail/consuming_buffers.hpp b/library/include/libtorrent/asio/detail/consuming_buffers.hpp new file mode 100644 index 000000000..7877f95f2 --- /dev/null +++ b/library/include/libtorrent/asio/detail/consuming_buffers.hpp @@ -0,0 +1,196 @@ +// +// consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// A proxy iterator for a sub-range in a list of buffers. +template +class consuming_buffers_iterator + : public boost::iterator_facade< + consuming_buffers_iterator, + const Buffer, + boost::forward_traversal_tag> +{ +public: + // Default constructor creates an end iterator. + consuming_buffers_iterator() + : at_end_(true) + { + } + + // Construct with a buffer for the first entry and an iterator + // range for the remaining entries. + consuming_buffers_iterator(bool at_end, const Buffer& first, + Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder) + : at_end_(at_end), + first_(first), + begin_remainder_(begin_remainder), + end_remainder_(end_remainder) + { + } + +private: + friend class boost::iterator_core_access; + + void increment() + { + if (!at_end_) + { + if (begin_remainder_ == end_remainder_) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + } + + bool equal(const consuming_buffers_iterator& other) const + { + if (at_end_ && other.at_end_) + return true; + return !at_end_ && !other.at_end_ + && buffer_cast(first_) + == buffer_cast(other.first_) + && buffer_size(first_) == buffer_size(other.first_) + && begin_remainder_ == other.begin_remainder_ + && end_remainder_ == other.end_remainder_; + } + + const Buffer& dereference() const + { + return first_; + } + + bool at_end_; + Buffer first_; + Buffer_Iterator begin_remainder_; + Buffer_Iterator end_remainder_; +}; + +// A proxy for a sub-range in a list of buffers. +template +class consuming_buffers +{ +public: + // The type for each element in the list of buffers. + typedef Buffer value_type; + + // A forward-only iterator type that may be used to read elements. + typedef consuming_buffers_iterator + const_iterator; + + // Construct to represent the entire list of buffers. + consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + at_end_(buffers_.begin() == buffers_.end()), + first_(*buffers_.begin()), + begin_remainder_(buffers_.begin()) + { + if (!at_end_) + ++begin_remainder_; + } + + // Copy constructor. + consuming_buffers(const consuming_buffers& other) + : buffers_(other.buffers_), + at_end_(other.at_end_), + first_(other.first_), + begin_remainder_(buffers_.begin()) + { + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + } + + // Assignment operator. + consuming_buffers& operator=(const consuming_buffers& other) + { + buffers_ = other.buffers_; + at_end_ = other.at_end_; + first_ = other.first_; + begin_remainder_ = buffers_.begin(); + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + return *this; + } + + // Get a forward-only iterator to the first element. + const_iterator begin() const + { + return const_iterator(at_end_, first_, begin_remainder_, buffers_.end()); + } + + // Get a forward-only iterator for one past the last element. + const_iterator end() const + { + return const_iterator(); + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + // Remove buffers from the start until the specified size is reached. + while (size > 0 && !at_end_) + { + if (buffer_size(first_) <= size) + { + size -= buffer_size(first_); + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + else + { + first_ = first_ + size; + size = 0; + } + } + + // Remove any more empty buffers at the start. + while (!at_end_ && buffer_size(first_) == 0) + { + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + } + +private: + Buffers buffers_; + bool at_end_; + Buffer first_; + typename Buffers::const_iterator begin_remainder_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/library/include/libtorrent/asio/detail/deadline_timer_service.hpp b/library/include/libtorrent/asio/detail/deadline_timer_service.hpp new file mode 100644 index 000000000..c5d15a5df --- /dev/null +++ b/library/include/libtorrent/asio/detail/deadline_timer_service.hpp @@ -0,0 +1,182 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class deadline_timer_service + : public asio::io_service::service +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // The implementation type of the timer. This type is dependent on the + // underlying implementation of the timer service. + struct implementation_type + : private asio::detail::noncopyable + { + time_type expiry; + bool might_have_pending_waits; + }; + + // Constructor. + deadline_timer_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + scheduler_(asio::use_service(io_service)) + { + scheduler_.add_timer_queue(timer_queue_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new timer implementation. + void construct(implementation_type& impl) + { + impl.expiry = time_type(); + impl.might_have_pending_waits = false; + } + + // Destroy a timer implementation. + void destroy(implementation_type& impl) + { + cancel(impl); + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl) + { + if (!impl.might_have_pending_waits) + return 0; + std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl); + impl.might_have_pending_waits = false; + return count; + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return impl.expiry; + } + + // Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time) + { + std::size_t count = cancel(impl); + impl.expiry = expiry_time; + return count; + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return Time_Traits::subtract(expires_at(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time) + { + return expires_at(impl, Time_Traits::add(Time_Traits::now(), expiry_time)); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl) + { + time_type now = Time_Traits::now(); + while (Time_Traits::less_than(now, impl.expiry)) + { + boost::posix_time::time_duration timeout = + Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now)); + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + socket_ops::select(0, 0, 0, 0, &tv); + now = Time_Traits::now(); + } + } + + template + class wait_handler + { + public: + wait_handler(asio::io_service& io_service, Handler handler) + : io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()(int result) + { + asio::error e(result); + io_service_.post(detail::bind_handler(handler_, e)); + } + + private: + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, Handler handler) + { + impl.might_have_pending_waits = true; + scheduler_.schedule_timer(timer_queue_, impl.expiry, + wait_handler(io_service(), handler), &impl); + } + +private: + // The queue of timers. + timer_queue timer_queue_; + + // The object that schedules and executes timers. Usually a reactor. + Timer_Scheduler& scheduler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/epoll_reactor.hpp b/library/include/libtorrent/asio/detail/epoll_reactor.hpp new file mode 100644 index 000000000..5f46a93ba --- /dev/null +++ b/library/include/libtorrent/asio/detail/epoll_reactor.hpp @@ -0,0 +1,593 @@ +// +// epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/epoll_reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/system_exception.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class epoll_reactor + : public asio::io_service::service +{ +public: + // Constructor. + epoll_reactor(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + epoll_fd_(do_epoll_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&epoll_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR; + ev.data.fd = interrupter_.read_descriptor(); + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + } + + // Destructor. + ~epoll_reactor() + { + shutdown_service(); + close(epoll_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type descriptor) + { + // No need to lock according to epoll documentation. + + epoll_event ev = { 0, { 0 } }; + ev.events = 0; + ev.data.fd = descriptor; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!read_op_queue_.has_operation(descriptor)) + if (handler(0)) + return; + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!write_op_queue_.has_operation(descriptor)) + if (handler(0)) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler); + need_mod = except_op_queue_.enqueue_operation(descriptor, handler) + && need_mod; + if (need_mod) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // epoll_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from epoll. + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run epoll once until interrupted or events are ready to be dispatched. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + int timeout = block ? get_timeout() : 0; + wait_in_progress_ = true; + lock.unlock(); + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].data.fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + if (events[i].events & (EPOLLERR | EPOLLHUP)) + { + except_op_queue_.dispatch_all_operations(descriptor, 0); + read_op_queue_.dispatch_all_operations(descriptor, 0); + write_op_queue_.dispatch_all_operations(descriptor, 0); + + epoll_event ev = { 0, { 0 } }; + ev.events = 0; + ev.data.fd = descriptor; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & EPOLLPRI) + more_except = except_op_queue_.dispatch_operation(descriptor, 0); + else + more_except = except_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLIN) + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + else + more_reads = read_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLOUT) + more_writes = write_op_queue_.dispatch_operation(descriptor, 0); + else + more_writes = write_op_queue_.has_operation(descriptor); + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLERR | EPOLLHUP; + if (more_reads) + ev.events |= EPOLLIN; + if (more_writes) + ev.events |= EPOLLOUT; + if (more_except) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.dispatch_all_operations(descriptor, error); + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + } + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(epoll_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // The hint to pass to epoll_create to size its data structures. + enum { epoll_size = 20000 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_epoll_create() + { + int fd = epoll_create(epoll_size); + if (fd == -1) + { + system_exception e("epoll", errno); + boost::throw_exception(e); + } + return fd; + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the epoll_wait call. The timeout value is + // returned as a number of milliseconds. A return value of -1 indicates + // that epoll_wait should block indefinitely. + int get_timeout() + { + if (all_timer_queues_are_empty()) + return -1; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + return minimum_wait_duration.total_milliseconds(); + } + else + { + return 0; + } + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the epoll_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The epoll file descriptor. + int epoll_fd_; + + // Whether the epoll_wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp new file mode 100644 index 000000000..5a60b759b --- /dev/null +++ b/library/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp @@ -0,0 +1,47 @@ +// +// epoll_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_EPOLL) +#if defined(__linux__) // This service is only supported on Linux. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45. + +// Define this to indicate that epoll is supported on the target platform. +#define ASIO_HAS_EPOLL 1 + +namespace asio { +namespace detail { + +template +class epoll_reactor; + +} // namespace detail +} // namespace asio + +#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) +#endif // defined(__linux__) +#endif // !defined(ASIO_DISABLE_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP diff --git a/library/include/libtorrent/asio/detail/event.hpp b/library/include/libtorrent/asio/detail/event.hpp new file mode 100644 index 000000000..8f8b753e7 --- /dev/null +++ b/library/include/libtorrent/asio/detail/event.hpp @@ -0,0 +1,50 @@ +// +// event.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_EVENT_HPP +#define ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_event.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_event.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_event.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_event event; +#elif defined(BOOST_WINDOWS) +typedef win_event event; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_event event; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EVENT_HPP diff --git a/library/include/libtorrent/asio/detail/fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/fd_set_adapter.hpp new file mode 100644 index 000000000..5266dd68c --- /dev/null +++ b/library/include/libtorrent/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,41 @@ +// +// fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/posix_fd_set_adapter.hpp" +#include "asio/detail/win_fd_set_adapter.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp b/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp new file mode 100644 index 000000000..d78c7e6af --- /dev/null +++ b/library/include/libtorrent/asio/detail/handler_alloc_helpers.hpp @@ -0,0 +1,256 @@ +// +// handler_alloc_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_alloc_hook.hpp" +#include "asio/detail/noncopyable.hpp" + +// Calls to asio_handler_allocate and asio_handler_deallocate must be made from +// a namespace that does not contain any overloads of these functions. The +// asio_handler_alloc_helpers namespace is defined here for that purpose. +namespace asio_handler_alloc_helpers { + +template +inline void* allocate(std::size_t s, Handler* h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + return ::operator new(s); +#else + using namespace asio; + return asio_handler_allocate(s, h); +#endif +} + +template +inline void deallocate(void* p, std::size_t s, Handler* h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + ::operator delete(p); +#else + using namespace asio; + asio_handler_deallocate(p, s, h); +#endif +} + +} // namespace asio_handler_alloc_helpers + +namespace asio { +namespace detail { + +// Traits for handler allocation. +template +struct handler_alloc_traits +{ + typedef Handler handler_type; + typedef Object value_type; + typedef Object* pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object)); +}; + +template +class handler_ptr; + +// Helper class to provide RAII on uninitialised handler memory. +template +class raw_handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + + // Constructor allocates the memory. + raw_handler_ptr(handler_type& handler) + : handler_(handler), + pointer_(static_cast( + asio_handler_alloc_helpers::allocate(value_size, &handler_))) + { + } + + // Destructor automatically deallocates memory, unless it has been stolen by + // a handler_ptr object. + ~raw_handler_ptr() + { + if (pointer_) + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, &handler_); + } + +private: + friend class handler_ptr; + handler_type& handler_; + pointer_type pointer_; +}; + +// Helper class to provide RAII on uninitialised handler memory. +template +class handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + typedef raw_handler_ptr raw_ptr_type; + + // Take ownership of existing memory. + handler_ptr(handler_type& handler, pointer_type pointer) + : handler_(handler), + pointer_(pointer) + { + } + + // Construct object in raw memory and take ownership if construction succeeds. + handler_ptr(raw_ptr_type& raw_ptr) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type( + a1, a2, a3, a4, a5, a6, a7, a8)) + { + raw_ptr.pointer_ = 0; + } + + // Destructor automatically deallocates memory, unless it has been released. + ~handler_ptr() + { + reset(); + } + + // Get the memory. + pointer_type get() const + { + return pointer_; + } + + // Release ownership of the memory. + pointer_type release() + { + pointer_type tmp = pointer_; + pointer_ = 0; + return tmp; + } + + // Explicitly destroy and deallocate the memory. + void reset() + { + if (pointer_) + { + pointer_->value_type::~value_type(); + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, &handler_); + pointer_ = 0; + } + } + +private: + handler_type& handler_; + pointer_type pointer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP diff --git a/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp b/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp new file mode 100644 index 000000000..6045122c4 --- /dev/null +++ b/library/include/libtorrent/asio/detail/handler_invoke_helpers.hpp @@ -0,0 +1,47 @@ +// +// handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_invoke_hook.hpp" + +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers +// namespace is defined here for that purpose. +namespace asio_handler_invoke_helpers { + +template +inline void invoke(const Function& function, Context* context) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + Function tmp(function); + tmp(); +#else + using namespace asio; + asio_handler_invoke(function, context); +#endif +} + +} // namespace asio_handler_invoke_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/library/include/libtorrent/asio/detail/hash_map.hpp b/library/include/libtorrent/asio/detail/hash_map.hpp new file mode 100644 index 000000000..c68d78d34 --- /dev/null +++ b/library/include/libtorrent/asio/detail/hash_map.hpp @@ -0,0 +1,197 @@ +// +// hash_map.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_HASH_MAP_HPP +#define ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +using boost::hash_value; + +template +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list::const_iterator const_iterator; + + // Constructor. + hash_map() + { + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Check whether the map is empty. + bool empty() const + { + return values_.empty(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + size_t bucket = hash_value(k) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + size_t bucket = hash_value(k) % num_buckets; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair insert(const value_type& v) + { + size_t bucket = hash_value(v.first) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_.insert(values_.end(), v); + return std::pair(buckets_[bucket].last, true); + } + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == v.first) + return std::pair(it, false); + ++it; + } + buckets_[bucket].last = values_.insert(end, v); + return std::pair(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + assert(it != values_.end()); + + size_t bucket = hash_value(it->first) % num_buckets; + bool is_first = (it == buckets_[bucket].first); + bool is_last = (it == buckets_[bucket].last); + if (is_first && is_last) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_.erase(it); + } + + // Remove all entries from the map. + void clear() + { + // Clear the values. + values_.clear(); + + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + } + +private: + // The list of all values in the hash map. + std::list values_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The number of buckets in the hash. + enum { num_buckets = 1021 }; + + // The buckets in the hash. + bucket_type buckets_[num_buckets]; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HASH_MAP_HPP diff --git a/library/include/libtorrent/asio/detail/io_control.hpp b/library/include/libtorrent/asio/detail/io_control.hpp new file mode 100644 index 000000000..ee9e8ac9f --- /dev/null +++ b/library/include/libtorrent/asio/detail/io_control.hpp @@ -0,0 +1,137 @@ +// +// io_control.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_IO_CONTROL_HPP +#define ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace io_control { + +// IO control command for non-blocking I/O. +class non_blocking_io +{ +public: + // Default constructor. + non_blocking_io() + : value_(0) + { + } + + // Construct with a specific command value. + non_blocking_io(bool value) + : value_(value ? 1 : 0) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONBIO; + } + + // Set the value of the I/O control command. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the I/O control command. + bool get() const + { + return value_ != 0; + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +// I/O control command for getting number of bytes available. +class bytes_readable +{ +public: + // Default constructor. + bytes_readable() + : value_(0) + { + } + + // Construct with a specific command value. + bytes_readable(std::size_t value) + : value_(value) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONREAD; + } + + // Set the value of the I/O control command. + void set(std::size_t value) + { + value_ = static_cast(value); + } + + // Get the current value of the I/O control command. + std::size_t get() const + { + return static_cast(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_CONTROL_HPP diff --git a/library/include/libtorrent/asio/detail/kqueue_reactor.hpp b/library/include/libtorrent/asio/detail/kqueue_reactor.hpp new file mode 100644 index 000000000..51cabbc0a --- /dev/null +++ b/library/include/libtorrent/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,596 @@ +// +// kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under 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 ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/kqueue_reactor_fwd.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/system_exception.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +// Older versions of Mac OS X may not define EV_OOBAND. +#if !defined(EV_OOBAND) +# define EV_OOBAND EV_FLAG1 +#endif // !defined(EV_OOBAND) + +namespace asio { +namespace detail { + +template +class kqueue_reactor + : public asio::io_service::service +{ +public: + // Constructor. + kqueue_reactor(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + kqueue_fd_(do_kqueue_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&kqueue_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to the kqueue. + struct kevent event; + EV_SET(&event, interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, 0); + ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); + } + + // Destructor. + ~kqueue_reactor() + { + shutdown_service(); + close(kqueue_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type) + { + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!read_op_queue_.has_operation(descriptor)) + if (handler(0)) + return; + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!write_op_queue_.has_operation(descriptor)) + if (handler(0)) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // kqueue_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from kqueue. + struct kevent event[2]; + EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, event, 2, 0, 0, 0); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run the kqueue loop. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // Determine how long to block while waiting for events. + timespec timeout_buf = { 0, 0 }; + timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; + + wait_in_progress_ = true; + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].ident; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else if (events[i].filter == EVFILT_READ) + { + // Dispatch operations associated with the descriptor. + bool more_reads = false; + bool more_except = false; + if (events[i].flags & EV_ERROR) + { + int error = events[i].data; + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + else if (events[i].flags & EV_OOBAND) + { + more_except = except_op_queue_.dispatch_operation(descriptor, 0); + if (events[i].data > 0) + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + else + more_reads = read_op_queue_.has_operation(descriptor); + } + else + { + more_reads = read_op_queue_.dispatch_operation(descriptor, 0); + more_except = except_op_queue_.has_operation(descriptor); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_reads) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else if (more_except) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + else if (events[i].filter == EVFILT_WRITE) + { + // Dispatch operations associated with the descriptor. + bool more_writes = false; + if (events[i].flags & EV_ERROR) + { + int error = events[i].data; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + else + { + more_writes = write_op_queue_.dispatch_operation(descriptor, 0); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_writes) + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + int error = errno; + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (std::size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(kqueue_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Create the kqueue file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_kqueue_create() + { + int fd = kqueue(); + if (fd == -1) + { + system_exception e("kqueue", errno); + boost::throw_exception(e); + } + return fd; + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the kevent call. + timespec* get_timeout(timespec& ts) + { + if (all_timer_queues_are_empty()) + return 0; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + ts.tv_sec = minimum_wait_duration.total_seconds(); + ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000; + } + else + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + + return &ts; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the epoll_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The epoll file descriptor. + int kqueue_fd_; + + // Whether the kqueue wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp new file mode 100644 index 000000000..b9bc5a02d --- /dev/null +++ b/library/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp @@ -0,0 +1,41 @@ +// +// kqueue_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under 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 ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_KQUEUE) +#if defined(__MACH__) && defined(__APPLE__) + +// Define this to indicate that epoll is supported on the target platform. +#define ASIO_HAS_KQUEUE 1 + +namespace asio { +namespace detail { + +template +class kqueue_reactor; + +} // namespace detail +} // namespace asio + +#endif // defined(__MACH__) && defined(__APPLE__) +#endif // !defined(ASIO_DISABLE_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP diff --git a/library/include/libtorrent/asio/detail/mutex.hpp b/library/include/libtorrent/asio/detail/mutex.hpp new file mode 100644 index 000000000..e861556d0 --- /dev/null +++ b/library/include/libtorrent/asio/detail/mutex.hpp @@ -0,0 +1,50 @@ +// +// mutex.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_MUTEX_HPP +#define ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_mutex.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_mutex.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_mutex.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_mutex mutex; +#elif defined(BOOST_WINDOWS) +typedef win_mutex mutex; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_mutex mutex; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_MUTEX_HPP diff --git a/library/include/libtorrent/asio/detail/noncopyable.hpp b/library/include/libtorrent/asio/detail/noncopyable.hpp new file mode 100644 index 000000000..004a68bc6 --- /dev/null +++ b/library/include/libtorrent/asio/detail/noncopyable.hpp @@ -0,0 +1,55 @@ +// +// noncopyable.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NONCOPYABLE_HPP +#define ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +// Redefine the noncopyable class for Borland C++ since that compiler does not +// apply the empty base optimisation unless the base class contains a dummy +// char data member. +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + char dummy_; +}; +#else +using boost::noncopyable; +#endif + +} // namespace detail + +using asio::detail::noncopyable; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/library/include/libtorrent/asio/detail/null_event.hpp b/library/include/libtorrent/asio/detail/null_event.hpp new file mode 100644 index 000000000..91bfc329e --- /dev/null +++ b/library/include/libtorrent/asio/detail/null_event.hpp @@ -0,0 +1,68 @@ +// +// null_event.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NULL_EVENT_HPP +#define ASIO_DETAIL_NULL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_event + : private noncopyable +{ +public: + // Constructor. + null_event() + { + } + + // Destructor. + ~null_event() + { + } + + // Signal the event. + void signal() + { + } + + // Reset the event. + void clear() + { + } + + // Wait for the event to become signalled. + void wait() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/library/include/libtorrent/asio/detail/null_mutex.hpp b/library/include/libtorrent/asio/detail/null_mutex.hpp new file mode 100644 index 000000000..814a18f5f --- /dev/null +++ b/library/include/libtorrent/asio/detail/null_mutex.hpp @@ -0,0 +1,66 @@ +// +// null_mutex.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NULL_MUTEX_HPP +#define ASIO_DETAIL_NULL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class null_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + null_mutex() + { + } + + // Destructor. + ~null_mutex() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/library/include/libtorrent/asio/detail/null_signal_blocker.hpp b/library/include/libtorrent/asio/detail/null_signal_blocker.hpp new file mode 100644 index 000000000..ff7cfcaf0 --- /dev/null +++ b/library/include/libtorrent/asio/detail/null_signal_blocker.hpp @@ -0,0 +1,63 @@ +// +// null_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + null_signal_blocker() + { + } + + // Destructor restores the previous signal mask. + ~null_signal_blocker() + { + } + + // Block all signals for the calling thread. + void block() + { + } + + // Restore the previous signal mask. + void unblock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/library/include/libtorrent/asio/detail/null_thread.hpp b/library/include/libtorrent/asio/detail/null_thread.hpp new file mode 100644 index 000000000..653caa651 --- /dev/null +++ b/library/include/libtorrent/asio/detail/null_thread.hpp @@ -0,0 +1,67 @@ +// +// null_thread.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NULL_THREAD_HPP +#define ASIO_DETAIL_NULL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_thread + : private noncopyable +{ +public: + // Constructor. + template + null_thread(Function f) + { + system_exception e("thread", asio::error::not_supported); + boost::throw_exception(e); + } + + // Destructor. + ~null_thread() + { + } + + // Wait for the thread to exit. + void join() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/library/include/libtorrent/asio/detail/null_tss_ptr.hpp b/library/include/libtorrent/asio/detail/null_tss_ptr.hpp new file mode 100644 index 000000000..707e31e79 --- /dev/null +++ b/library/include/libtorrent/asio/detail/null_tss_ptr.hpp @@ -0,0 +1,70 @@ +// +// null_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_NULL_TSS_PTR_HPP +#define ASIO_DETAIL_NULL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class null_tss_ptr + : private noncopyable +{ +public: + // Constructor. + null_tss_ptr() + : value_(0) + { + } + + // Destructor. + ~null_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + T* value_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp b/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp new file mode 100644 index 000000000..24c42ad67 --- /dev/null +++ b/library/include/libtorrent/asio/detail/old_win_sdk_compat.hpp @@ -0,0 +1,312 @@ +// +// old_win_sdk_compat.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP +#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Guess whether we are building against on old Platform SDK. +#if !defined(IPPROTO_IPV6) +#define ASIO_HAS_OLD_WIN_SDK 1 +#endif // !defined(IPPROTO_IPV6) + +#if defined(ASIO_HAS_OLD_WIN_SDK) + +// Emulation of types that are missing from old Platform SDKs. + +namespace asio { +namespace detail { + +enum +{ + sockaddr_storage_maxsize = 128, // Maximum size. + sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. + sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), + sockaddr_storage_pad2size = (sockaddr_storage_maxsize - + (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) +}; + +struct sockaddr_storage_emulation +{ + short ss_family; + char __ss_pad1[sockaddr_storage_pad1size]; + __int64 __ss_align; + char __ss_pad2[sockaddr_storage_pad2size]; +}; + +struct in6_addr_emulation +{ + u_char s6_addr[16]; +}; + +struct sockaddr_in6_emulation +{ + short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + in6_addr_emulation sin6_addr; + u_long sin6_scope_id; +}; + +struct ipv6_mreq_emulation +{ + in6_addr_emulation ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +}; + +#if !defined(IN6ADDR_ANY_INIT) +# define IN6ADDR_ANY_INIT { 0 } +#endif + +#if !defined(IN6ADDR_LOOPBACK_INIT) +# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } +#endif + +struct addrinfo_emulation +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo_emulation* ai_next; +}; + +#if !defined(AI_PASSIVE) +# define AI_PASSIVE 0x1 +#endif + +#if !defined(AI_CANONNAME) +# define AI_CANONNAME 0x2 +#endif + +#if !defined(AI_NUMERICHOST) +# define AI_NUMERICHOST 0x4 +#endif + +#if !defined(EAI_AGAIN) +# define EAI_AGAIN WSATRY_AGAIN +#endif + +#if !defined(EAI_BADFLAGS) +# define EAI_BADFLAGS WSAEINVAL +#endif + +#if !defined(EAI_FAIL) +# define EAI_FAIL WSANO_RECOVERY +#endif + +#if !defined(EAI_FAMILY) +# define EAI_FAMILY WSAEAFNOSUPPORT +#endif + +#if !defined(EAI_MEMORY) +# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#endif + +#if !defined(EAI_NODATA) +# define EAI_NODATA WSANO_DATA +#endif + +#if !defined(EAI_NONAME) +# define EAI_NONAME WSAHOST_NOT_FOUND +#endif + +#if !defined(EAI_SERVICE) +# define EAI_SERVICE WSATYPE_NOT_FOUND +#endif + +#if !defined(EAI_SOCKTYPE) +# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif + +#if !defined(NI_NOFQDN) +# define NI_NOFQDN 0x01 +#endif + +#if !defined(NI_NUMERICHOST) +# define NI_NUMERICHOST 0x02 +#endif + +#if !defined(NI_NAMEREQD) +# define NI_NAMEREQD 0x04 +#endif + +#if !defined(NI_NUMERICSERV) +# define NI_NUMERICSERV 0x08 +#endif + +#if !defined(NI_DGRAM) +# define NI_DGRAM 0x10 +#endif + +#if !defined(IPPROTO_IPV6) +# define IPPROTO_IPV6 41 +#endif + +#if !defined(IPV6_MULTICAST_IF) +# define IPV6_MULTICAST_IF 9 +#endif + +#if !defined(IPV6_MULTICAST_HOPS) +# define IPV6_MULTICAST_HOPS 10 +#endif + +#if !defined(IPV6_MULTICAST_LOOP) +# define IPV6_MULTICAST_LOOP 11 +#endif + +#if !defined(IPV6_JOIN_GROUP) +# define IPV6_JOIN_GROUP 12 +#endif + +#if !defined(IPV6_LEAVE_GROUP) +# define IPV6_LEAVE_GROUP 13 +#endif + +inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 0)); +} + +inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 1)); +} + +inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a) +{ + return (a->s6_addr[0] == 0xff); +} + +inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); +} + +inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0)); +} + +inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff)); +} + +inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff) + && !((a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1)))); +} + +inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1); +} + +inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2); +} + +inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5); +} + +inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8); +} + +inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe); +} + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_OLD_WIN_SDK) + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp b/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 000000000..b73fb3f2e --- /dev/null +++ b/library/include/libtorrent/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,104 @@ +// +// pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + pipe_select_interrupter() + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + } + + // Destructor. + ~pipe_select_interrupter() + { + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + ::write(write_descriptor_, &byte, 1); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/library/include/libtorrent/asio/detail/pop_options.hpp b/library/include/libtorrent/asio/detail/pop_options.hpp new file mode 100644 index 000000000..4c942e8d3 --- /dev/null +++ b/library/include/libtorrent/asio/detail/pop_options.hpp @@ -0,0 +1,88 @@ +// +// pop_options.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (pop) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +#endif diff --git a/library/include/libtorrent/asio/detail/posix_event.hpp b/library/include/libtorrent/asio/detail/posix_event.hpp new file mode 100644 index 000000000..9d84bfa58 --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_event.hpp @@ -0,0 +1,107 @@ +// +// posix_event.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_EVENT_HPP +#define ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + posix_event() + : signalled_(false) + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + system_exception e("event", error); + boost::throw_exception(e); + } + + error = ::pthread_cond_init(&cond_, 0); + if (error != 0) + { + ::pthread_mutex_destroy(&mutex_); + system_exception e("event", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + ::pthread_mutex_destroy(&mutex_); + } + + // Signal the event. + void signal() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = true; + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Reset the event. + void clear() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = false; + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Wait for the event to become signalled. + void wait() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + while (!signalled_) + ::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + +private: + ::pthread_mutex_t mutex_; + ::pthread_cond_t cond_; + bool signalled_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 000000000..56d8ff3dc --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,71 @@ +// +// posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + FD_ZERO(&fd_set_); + } + + void set(socket_type descriptor) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/library/include/libtorrent/asio/detail/posix_mutex.hpp b/library/include/libtorrent/asio/detail/posix_mutex.hpp new file mode 100644 index 000000000..4e9df4a75 --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_mutex.hpp @@ -0,0 +1,94 @@ +// +// posix_mutex.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_MUTEX_HPP +#define ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class posix_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + posix_mutex() + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); + } + + // Lock the mutex. + void lock() + { + int error = ::pthread_mutex_lock(&mutex_); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + int error = ::pthread_mutex_unlock(&mutex_); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + +private: + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp b/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 000000000..c11fc5ef6 --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,89 @@ +// +// posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/library/include/libtorrent/asio/detail/posix_thread.hpp b/library/include/libtorrent/asio/detail/posix_thread.hpp new file mode 100644 index 000000000..7d738fa4b --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_thread.hpp @@ -0,0 +1,125 @@ +// +// posix_thread.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_THREAD_HPP +#define ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +extern "C" void* asio_detail_posix_thread_function(void* arg); + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template + posix_thread(Function f) + : joined_(false) + { + std::auto_ptr arg(new func(f)); + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg.get()); + if (error != 0) + { + system_exception e("thread", error); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~posix_thread() + { + if (!joined_) + ::pthread_detach(thread_); + } + + // Wait for the thread to exit. + void join() + { + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } + } + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::pthread_t thread_; + bool joined_; +}; + +inline void* asio_detail_posix_thread_function(void* arg) +{ + std::auto_ptr f( + static_cast(arg)); + f->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp b/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 000000000..961120ee9 --- /dev/null +++ b/library/include/libtorrent/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,84 @@ +// +// posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + int error = ::pthread_key_create(&tss_key_, 0); + if (error != 0) + { + system_exception e("tss", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/library/include/libtorrent/asio/detail/push_options.hpp b/library/include/libtorrent/asio/detail/push_options.hpp new file mode 100644 index 000000000..45493ed29 --- /dev/null +++ b/library/include/libtorrent/asio/detail/push_options.hpp @@ -0,0 +1,106 @@ +// +// push_options.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (push, 8) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma warning (disable:4675) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +#endif diff --git a/library/include/libtorrent/asio/detail/reactive_socket_service.hpp b/library/include/libtorrent/asio/detail/reactive_socket_service.hpp new file mode 100644 index 000000000..361f978af --- /dev/null +++ b/library/include/libtorrent/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,1640 @@ +// +// reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_service + : public asio::io_service::service +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_type; + + // The implementation type of the socket. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + protocol_(endpoint_type().protocol()) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_socket_service; + + // The native socket representation. + socket_type socket_; + + enum + { + user_set_non_blocking = 1, // The user wants a non-blocking socket. + internal_non_blocking = 2, // The socket has been set non-blocking. + enable_connection_aborted = 4, // User wants connection_aborted errors. + user_set_linger = 8 // The user set the linger option. + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. + reactive_socket_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + reactor_(asio::use_service(io_service)) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.flags_ = 0; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)); + } + + socket_ops::close(impl.socket_); + impl.socket_ = invalid_socket; + } + } + + // Open a new socket implementation. + template + void open(implementation_type& impl, const protocol_type& protocol, + Error_Handler error_handler) + { + close(impl, asio::ignore_error()); + + socket_holder sock(socket_ops::socket(protocol.family(), + protocol.type(), protocol.protocol())); + if (sock.get() == invalid_socket) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + if (int err = reactor_.register_descriptor(sock.get())) + { + error_handler(asio::error(err)); + return; + } + + impl.socket_ = sock.release(); + impl.flags_ = 0; + impl.protocol_ = protocol; + + error_handler(asio::error(0)); + } + + // Assign a native socket to a socket implementation. + template + void assign(implementation_type& impl, const protocol_type& protocol, + const native_type& native_socket, Error_Handler error_handler) + { + close(impl, asio::ignore_error()); + + if (int err = reactor_.register_descriptor(native_socket)) + { + error_handler(asio::error(err)); + return; + } + + impl.socket_ = native_socket; + impl.flags_ = 0; + impl.protocol_ = protocol; + + error_handler(asio::error(0)); + } + + // Destroy a socket implementation. + template + void close(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (socket_ops::close(impl.socket_) == socket_error_retval) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + impl.socket_ = invalid_socket; + } + + error_handler(asio::error(0)); + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + error_handler(error); + } + else + { + reactor_.cancel_ops(impl.socket_); + error_handler(asio::error(0)); + } + } + + // Bind the socket to the specified local endpoint. + template + void bind(implementation_type& impl, const endpoint_type& endpoint, + Error_Handler error_handler) + { + if (socket_ops::bind(impl.socket_, endpoint.data(), + endpoint.size()) == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Place the socket into the state where it will listen for new connections. + template + void listen(implementation_type& impl, int backlog, + Error_Handler error_handler) + { + if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Set a socket option. + template + void set_option(implementation_type& impl, const Option& option, + Error_Handler error_handler) + { + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + error_handler(asio::error(asio::error::invalid_argument)); + } + else + { + if (*reinterpret_cast(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + error_handler(asio::error(0)); + } + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + + if (socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_))) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + } + + // Set a socket option. + template + void get_option(const implementation_type& impl, Option& option, + Error_Handler error_handler) const + { + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + error_handler(asio::error(asio::error::invalid_argument)); + } + else + { + int* target = reinterpret_cast(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + error_handler(asio::error(0)); + } + } + else + { + size_t size = option.size(impl.protocol_); + if (socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size)) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + } + + // Perform an IO control command on the socket. + template + void io_control(implementation_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + if (command.name() == static_cast(FIONBIO)) + { + if (command.get()) + impl.flags_ |= implementation_type::user_set_non_blocking; + else + impl.flags_ &= ~implementation_type::user_set_non_blocking; + error_handler(asio::error(0)); + } + else + { + if (socket_ops::ioctl(impl.socket_, command.name(), + static_cast(command.data()))) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + } + + // Get the local endpoint. + template + void get_local_endpoint(const implementation_type& impl, + endpoint_type& endpoint, Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len)) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + endpoint.resize(addr_len); + error_handler(asio::error(0)); + } + + // Get the remote endpoint. + template + void get_remote_endpoint(const implementation_type& impl, + endpoint_type& endpoint, Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len)) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + endpoint.resize(addr_len); + error_handler(asio::error(0)); + } + + /// Disable sends or receives on the socket. + template + void shutdown(implementation_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + if (socket_ops::shutdown(impl.socket_, what) != 0) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Send the given data to the peer. + template + size_t send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(asio::error(0)); + return 0; + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (bytes_sent >= 0) + { + error_handler(asio::error(0)); + return bytes_sent; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (error != asio::error::would_block + && error != asio::error::try_again)) + { + error_handler(asio::error(error)); + return 0; + } + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return 0; + } + } + } + + template + class send_handler + { + public: + send_handler(socket_type socket, asio::io_service& io_service, + const Const_Buffers& buffers, socket_base::message_flags flags, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + int bytes = socket_ops::send(socket_, bufs, i, flags_); + asio::error error(bytes < 0 + ? socket_ops::get_error() : asio::error::success); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Const_Buffers buffers_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error, 0)); + } + else + { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + asio::error error(asio::error::success); + io_service().post(bind_handler(handler, error, 0)); + return; + } + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_write_op(impl.socket_, + send_handler( + impl.socket_, io_service(), buffers, flags, handler)); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = socket_ops::sendto(impl.socket_, bufs, i, flags, + destination.data(), destination.size()); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (bytes_sent >= 0) + { + error_handler(asio::error(0)); + return bytes_sent; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (error != asio::error::would_block + && error != asio::error::try_again)) + { + error_handler(asio::error(error)); + return 0; + } + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return 0; + } + } + } + + template + class send_to_handler + { + public: + send_to_handler(socket_type socket, asio::io_service& io_service, + const Const_Buffers& buffers, const endpoint_type& endpoint, + socket_base::message_flags flags, Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + destination_(endpoint), + flags_(flags), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers_.begin(); + typename Const_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + int bytes = socket_ops::sendto(socket_, bufs, i, flags_, + destination_.data(), destination_.size()); + asio::error error(bytes < 0 + ? socket_ops::get_error() : asio::error::success); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Const_Buffers buffers_; + endpoint_type destination_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error, 0)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_write_op(impl.socket_, + send_to_handler( + impl.socket_, io_service(), buffers, destination, flags, handler)); + } + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(asio::error(0)); + return 0; + } + + // Receive some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_recvd = socket_ops::recv(impl.socket_, bufs, i, flags); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (bytes_recvd > 0) + { + error_handler(asio::error(0)); + return bytes_recvd; + } + + // Check for EOF. + if (bytes_recvd == 0) + { + error_handler(asio::error(asio::error::eof)); + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (error != asio::error::would_block + && error != asio::error::try_again)) + { + error_handler(asio::error(error)); + return 0; + } + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return 0; + } + } + } + + template + class receive_handler + { + public: + receive_handler(socket_type socket, asio::io_service& io_service, + const Mutable_Buffers& buffers, socket_base::message_flags flags, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + int bytes = socket_ops::recv(socket_, bufs, i, flags_); + int error_code = asio::error::success; + if (bytes < 0) + error_code = socket_ops::get_error(); + else if (bytes == 0) + error_code = asio::error::eof; + asio::error error(error_code); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Mutable_Buffers buffers_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error, 0)); + } + else + { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + asio::error error(asio::error::success); + io_service().post(bind_handler(handler, error, 0)); + return; + } + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + if (flags & socket_base::message_out_of_band) + { + reactor_.start_except_op(impl.socket_, + receive_handler( + impl.socket_, io_service(), buffers, flags, handler)); + } + else + { + reactor_.start_read_op(impl.socket_, + receive_handler( + impl.socket_, io_service(), buffers, flags, handler)); + } + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Error_Handler error_handler) + { + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + for (;;) + { + // Try to complete the operation without blocking. + socket_addr_len_type addr_len = sender_endpoint.capacity(); + int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs, i, flags, + sender_endpoint.data(), &addr_len); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (bytes_recvd > 0) + { + sender_endpoint.resize(addr_len); + error_handler(asio::error(0)); + return bytes_recvd; + } + + // Check for EOF. + if (bytes_recvd == 0) + { + error_handler(asio::error(asio::error::eof)); + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (error != asio::error::would_block + && error != asio::error::try_again)) + { + error_handler(asio::error(error)); + return 0; + } + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return 0; + } + } + } + + template + class receive_from_handler + { + public: + receive_from_handler(socket_type socket, + asio::io_service& io_service, const Mutable_Buffers& buffers, + endpoint_type& endpoint, socket_base::message_flags flags, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers_.begin(); + typename Mutable_Buffers::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + socket_addr_len_type addr_len = sender_endpoint_.capacity(); + int bytes = socket_ops::recvfrom(socket_, bufs, i, flags_, + sender_endpoint_.data(), &addr_len); + int error_code = asio::error::success; + if (bytes < 0) + error_code = socket_ops::get_error(); + else if (bytes == 0) + error_code = asio::error::eof; + asio::error error(error_code); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + + sender_endpoint_.resize(addr_len); + io_service_.post(bind_handler(handler_, error, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Mutable_Buffers buffers_; + endpoint_type& sender_endpoint_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const Mutable_Buffers& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error, 0)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.socket_, + receive_from_handler( + impl.socket_, io_service(), buffers, + sender_endpoint, flags, handler)); + } + } + + // Accept a new connection. + template + void accept(implementation_type& impl, Socket& peer, + Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.native() != invalid_socket) + { + error_handler(asio::error(asio::error::already_connected)); + return; + } + + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_holder new_socket(socket_ops::accept(impl.socket_, 0, 0)); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (new_socket.get() >= 0) + { + asio::error temp_error; + peer.assign(impl.protocol_, new_socket.get(), + asio::assign_error(temp_error)); + if (temp_error) + { + error_handler(temp_error); + } + else + { + new_socket.release(); + error_handler(asio::error(0)); + } + return; + } + + // Operation failed. + if (error == asio::error::would_block + || error == asio::error::try_again) + { + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + error_handler(asio::error(error)); + return; + } + // Fall through to retry operation. + } + else if (error == asio::error::connection_aborted) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + { + error_handler(asio::error(error)); + return; + } + // Fall through to retry operation. + } + else + { + error_handler(asio::error(error)); + return; + } + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + } + } + + // Accept a new connection. + template + void accept_endpoint(implementation_type& impl, Socket& peer, + endpoint_type& peer_endpoint, Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.native() != invalid_socket) + { + error_handler(asio::error(asio::error::already_connected)); + return; + } + + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_addr_len_type addr_len = peer_endpoint.capacity(); + socket_holder new_socket(socket_ops::accept( + impl.socket_, peer_endpoint.data(), &addr_len)); + int error = socket_ops::get_error(); + + // Check if operation succeeded. + if (new_socket.get() >= 0) + { + peer_endpoint.resize(addr_len); + asio::error temp_error; + peer.assign(impl.protocol_, new_socket.get(), + asio::assign_error(temp_error)); + if (temp_error) + { + error_handler(temp_error); + } + else + { + new_socket.release(); + error_handler(asio::error(0)); + } + return; + } + + // Operation failed. + if (error == asio::error::would_block + || error == asio::error::try_again) + { + if (impl.flags_ & implementation_type::user_set_non_blocking) + { + error_handler(asio::error(error)); + return; + } + // Fall through to retry operation. + } + else if (error == asio::error::connection_aborted) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + { + error_handler(asio::error(error)); + return; + } + // Fall through to retry operation. + } + else + { + error_handler(asio::error(error)); + return; + } + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_) < 0) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + } + } + + template + class accept_handler + { + public: + accept_handler(socket_type socket, asio::io_service& io_service, + Socket& peer, const protocol_type& protocol, + bool enable_connection_aborted, Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + peer_(peer), + protocol_(protocol), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Accept the waiting connection. + socket_holder new_socket(socket_ops::accept(socket_, 0, 0)); + asio::error error(new_socket.get() == invalid_socket + ? socket_ops::get_error() : asio::error::success); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + if (error == asio::error::connection_aborted + && !enable_connection_aborted_) + return false; + + // Transfer ownership of the new socket to the peer object. + if (!error) + { + peer_.assign(protocol_, new_socket.get(), + asio::assign_error(error)); + if (!error) + new_socket.release(); + } + + io_service_.post(bind_handler(handler_, error)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Socket& peer_; + protocol_type protocol_; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer object must be valid until the + // accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error)); + } + else if (peer.native() != invalid_socket) + { + asio::error error(asio::error::already_connected); + io_service().post(bind_handler(handler, error)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.socket_, + accept_handler( + impl.socket_, io_service(), peer, impl.protocol_, + (impl.flags_ & implementation_type::enable_connection_aborted) != 0, + handler)); + } + } + + template + class accept_endp_handler + { + public: + accept_endp_handler(socket_type socket, asio::io_service& io_service, + Socket& peer, endpoint_type& peer_endpoint, + bool enable_connection_aborted, Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + peer_(peer), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Accept the waiting connection. + socket_addr_len_type addr_len = peer_endpoint_.capacity(); + socket_holder new_socket(socket_ops::accept( + socket_, peer_endpoint_.data(), &addr_len)); + asio::error error(new_socket.get() == invalid_socket + ? socket_ops::get_error() : asio::error::success); + + // Check if we need to run the operation again. + if (error == asio::error::would_block + || error == asio::error::try_again) + return false; + if (error == asio::error::connection_aborted + && !enable_connection_aborted_) + return false; + + // Transfer ownership of the new socket to the peer object. + if (!error) + { + peer_endpoint_.resize(addr_len); + peer_.assign(peer_endpoint_.protocol(), new_socket.get(), + asio::assign_error(error)); + if (!error) + new_socket.release(); + } + + io_service_.post(bind_handler(handler_, error)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Socket& peer_; + endpoint_type& peer_endpoint_; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept_endpoint(implementation_type& impl, Socket& peer, + endpoint_type& peer_endpoint, Handler handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error)); + } + else if (peer.native() != invalid_socket) + { + asio::error error(asio::error::already_connected); + io_service().post(bind_handler(handler, error)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.socket_, + accept_endp_handler( + impl.socket_, io_service(), peer, peer_endpoint, + (impl.flags_ & implementation_type::enable_connection_aborted) != 0, + handler)); + } + } + + // Connect the socket to the specified endpoint. + template + void connect(implementation_type& impl, const endpoint_type& peer_endpoint, + Error_Handler error_handler) + { + // Open the socket if it is not already open. + if (impl.socket_ == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl.socket_ = socket_ops::socket(family, type, proto); + if (impl.socket_ == invalid_socket) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + // Register the socket with the reactor. + if (int err = reactor_.register_descriptor(impl.socket_)) + { + socket_ops::close(impl.socket_); + error_handler(asio::error(err)); + return; + } + } + else if (impl.flags_ & implementation_type::internal_non_blocking) + { + // Mark the socket as blocking while we perform the connect. + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + // Perform the connect operation. + int result = socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size()); + if (result == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + template + class connect_handler + { + public: + connect_handler(socket_type socket, boost::shared_ptr completed, + asio::io_service& io_service, Reactor& reactor, Handler handler) + : socket_(socket), + completed_(completed), + io_service_(io_service), + work_(io_service), + reactor_(reactor), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return true; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(socket_); + + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len) == socket_error_retval) + { + asio::error error(socket_ops::get_error()); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + asio::error error(connect_error); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Post the result of the successful connection operation. + asio::error error(asio::error::success); + io_service_.post(bind_handler(handler_, error)); + + return true; + } + + private: + socket_type socket_; + boost::shared_ptr completed_; + asio::io_service& io_service_; + asio::io_service::work work_; + Reactor& reactor_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + // Open the socket if it is not already open. + if (impl.socket_ == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl.socket_ = socket_ops::socket(family, type, proto); + if (impl.socket_ == invalid_socket) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + + // Register the socket with the reactor. + if (int err = reactor_.register_descriptor(impl.socket_)) + { + socket_ops::close(impl.socket_); + asio::error error(err); + io_service().post(bind_handler(handler, error)); + return; + } + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + // Start the connect operation. The socket is already marked as non-blocking + // so the connection will take place asynchronously. + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size()) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + asio::error error(asio::error::success); + io_service().post(bind_handler(handler, error)); + } + else if (socket_ops::get_error() == asio::error::in_progress + || socket_ops::get_error() == asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor_.start_write_and_except_ops(impl.socket_, + connect_handler( + impl.socket_, completed, io_service(), reactor_, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + } + } + +private: + // The selector that performs event demultiplexing for the provider. + Reactor& reactor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/reactor_op_queue.hpp b/library/include/libtorrent/asio/detail/reactor_op_queue.hpp new file mode 100644 index 000000000..dbf86c2e5 --- /dev/null +++ b/library/include/libtorrent/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,379 @@ +// +// reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class reactor_op_queue + : private noncopyable +{ +public: + // Constructor. + reactor_op_queue() + : operations_(), + cancelled_operations_(0), + cleanup_operations_(0) + { + } + + // Add a new operation to the queue. Returns true if this is the only + // operation for the given descriptor, in which case the reactor's event + // demultiplexing function call may need to be interrupted and restarted. + template + bool enqueue_operation(Descriptor descriptor, Handler handler) + { + op_base* new_op = new op(descriptor, handler); + + typedef typename operation_map::iterator iterator; + typedef typename operation_map::value_type value_type; + std::pair entry = + operations_.insert(value_type(descriptor, new_op)); + if (entry.second) + return true; + + op_base* current_op = entry.first->second; + while (current_op->next_) + current_op = current_op->next_; + current_op->next_ = new_op; + + return false; + } + + // Cancel all operations associated with the descriptor. Any operations + // pending for the descriptor will be notified that they have been cancelled + // next time dispatch_cancellations is called. Returns true if any operations + // were cancelled, in which case the reactor's event demultiplexing function + // may need to be interrupted and restarted. + bool cancel_operations(Descriptor descriptor) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* last_op = i->second; + while (last_op->next_) + last_op = last_op->next_; + last_op->next_ = cancelled_operations_; + cancelled_operations_ = i->second; + operations_.erase(i); + return true; + } + + return false; + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // Dispatch the first operation corresponding to the descriptor. Returns true + // if there are more operations queued for the descriptor. + bool dispatch_operation(Descriptor descriptor, int result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* this_op = i->second; + i->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (done) + { + // Operation has finished. + if (i->second) + { + return true; + } + else + { + operations_.erase(i); + return false; + } + } + else + { + // Operation wants to be called again. Leave it at the front of the + // queue for this descriptor, and remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = i->second; + i->second = this_op; + return true; + } + } + return false; + } + + // Dispatch all operations corresponding to the descriptor. + void dispatch_all_operations(Descriptor descriptor, int result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + while (i->second) + { + op_base* this_op = i->second; + i->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (!done) + { + // Operation has not finished yet, so leave at front of queue, and + // remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = i->second; + i->second = this_op; + return; + } + operations_.erase(i); + } + } + } + + // Fill a descriptor set with the descriptors corresponding to each active + // operation. + template + void get_descriptors(Descriptor_Set& descriptors) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + descriptors.set(i->first); + ++i; + } + } + + // Dispatch the operations corresponding to the ready file descriptors + // contained in the given descriptor set. + template + void dispatch_descriptors(const Descriptor_Set& descriptors, int result) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operation_map::iterator op_iter = i++; + if (descriptors.is_set(op_iter->first)) + { + op_base* this_op = op_iter->second; + op_iter->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (done) + { + if (!op_iter->second) + operations_.erase(op_iter); + } + else + { + // Operation has not finished yet, so leave at front of queue, and + // remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = op_iter->second; + op_iter->second = this_op; + } + } + } + } + + // Dispatch any pending cancels for operations. + void dispatch_cancellations() + { + while (cancelled_operations_) + { + op_base* this_op = cancelled_operations_; + cancelled_operations_ = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + this_op->invoke(asio::error::operation_aborted); + } + } + + // Destroy operations that are waiting to be cleaned up. + void cleanup_operations() + { + while (cleanup_operations_) + { + op_base* next_op = cleanup_operations_->next_; + cleanup_operations_->next_ = 0; + cleanup_operations_->destroy(); + cleanup_operations_ = next_op; + } + } + + // Destroy all operations owned by the queue. + void destroy_operations() + { + while (cancelled_operations_) + { + op_base* next_op = cancelled_operations_->next_; + cancelled_operations_->next_ = 0; + cancelled_operations_->destroy(); + cancelled_operations_ = next_op; + } + + while (cleanup_operations_) + { + op_base* next_op = cleanup_operations_->next_; + cleanup_operations_->next_ = 0; + cleanup_operations_->destroy(); + cleanup_operations_ = next_op; + } + + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operation_map::iterator op_iter = i++; + op_base* curr_op = op_iter->second; + operations_.erase(op_iter); + while (curr_op) + { + op_base* next_op = curr_op->next_; + curr_op->next_ = 0; + curr_op->destroy(); + curr_op = next_op; + } + } + } + +private: + // Base class for reactor operations. A function pointer is used instead of + // virtual functions to avoid the associated overhead. + class op_base + { + public: + // Get the descriptor associated with the operation. + Descriptor descriptor() const + { + return descriptor_; + } + + // Perform the operation. + bool invoke(int result) + { + return invoke_func_(this, result); + } + + // Destroy the operation. + void destroy() + { + return destroy_func_(this); + } + + protected: + typedef bool (*invoke_func_type)(op_base*, int); + typedef void (*destroy_func_type)(op_base*); + + // Construct an operation for the given descriptor. + op_base(invoke_func_type invoke_func, + destroy_func_type destroy_func, Descriptor descriptor) + : invoke_func_(invoke_func), + destroy_func_(destroy_func), + descriptor_(descriptor), + next_(0) + { + } + + // Prevent deletion through this type. + ~op_base() + { + } + + private: + friend class reactor_op_queue; + + // The function to be called to dispatch the handler. + invoke_func_type invoke_func_; + + // The function to be called to delete the handler. + destroy_func_type destroy_func_; + + // The descriptor associated with the operation. + Descriptor descriptor_; + + // The next operation for the same file descriptor. + op_base* next_; + }; + + // Adaptor class template for using handlers in operations. + template + class op + : public op_base + { + public: + // Constructor. + op(Descriptor descriptor, Handler handler) + : op_base(&op::invoke_handler, + &op::destroy_handler, descriptor), + handler_(handler) + { + } + + // Invoke the handler. + static bool invoke_handler(op_base* base, int result) + { + return static_cast*>(base)->handler_(result); + } + + // Delete the handler. + static void destroy_handler(op_base* base) + { + delete static_cast*>(base); + } + + private: + Handler handler_; + }; + + // The type for a map of operations. + typedef hash_map operation_map; + + // The operations that are currently executing asynchronously. + operation_map operations_; + + // The list of operations that have been cancelled. + op_base* cancelled_operations_; + + // The list of operations to be destroyed. + op_base* cleanup_operations_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/library/include/libtorrent/asio/detail/resolver_service.hpp b/library/include/libtorrent/asio/detail/resolver_service.hpp new file mode 100644 index 000000000..31c87e58e --- /dev/null +++ b/library/include/libtorrent/asio/detail/resolver_service.hpp @@ -0,0 +1,361 @@ +// +// resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { +namespace detail { + +template +class resolver_service + : public asio::io_service::service +{ +private: + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; + +public: + // The implementation type of the resolver. The shared pointer is used as a + // cancellation token to indicate to the background thread that the operation + // has been cancelled. + typedef boost::shared_ptr implementation_type; + struct noop_deleter { void operator()(void*) {} }; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef typename Protocol::resolver_query query_type; + + // The iterator type. + typedef typename Protocol::resolver_iterator iterator_type; + + // Constructor. + resolver_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + work_io_service_(new asio::io_service), + work_(new asio::io_service::work(*work_io_service_)), + work_thread_(0) + { + } + + // Destructor. + ~resolver_service() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + work_.reset(); + if (work_io_service_) + { + work_io_service_->interrupt(); + if (work_thread_) + { + work_thread_->join(); + work_thread_.reset(); + } + work_io_service_.reset(); + } + } + + // Construct a new resolver implementation. + void construct(implementation_type& impl) + { + impl.reset(static_cast(0), noop_deleter()); + } + + // Destroy a resolver implementation. + void destroy(implementation_type&) + { + } + + // Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + impl.reset(static_cast(0), noop_deleter()); + } + + // Resolve a query to a list of entries. + template + iterator_type resolve(implementation_type&, const query_type& query, + Error_Handler error_handler) + { + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = query.host_name(); + std::string service_name = query.service_name(); + asio::detail::addrinfo_type hints = query.hints(); + + int result = socket_ops::getaddrinfo( + host_name.length() ? host_name.c_str() : 0, + service_name.c_str(), &hints, &address_info); + auto_addrinfo auto_address_info(address_info); + + error_handler(asio::error(result)); + if (result != 0) + return iterator_type(); + return iterator_type::create(address_info, host_name, service_name); + } + + template + class resolve_query_handler + { + public: + resolve_query_handler(implementation_type impl, const query_type& query, + asio::io_service& io_service, Handler handler) + : impl_(impl), + query_(query), + io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + iterator_type iterator; + io_service_.post(asio::detail::bind_handler(handler_, + asio::error(asio::error::operation_aborted), + iterator)); + return; + } + + // Perform the blocking host resolution operation. + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = query_.host_name(); + std::string service_name = query_.service_name(); + asio::detail::addrinfo_type hints = query_.hints(); + int result = socket_ops::getaddrinfo( + host_name.length() ? host_name.c_str() : 0, + service_name.c_str(), &hints, &address_info); + auto_addrinfo auto_address_info(address_info); + + // Invoke the handler and pass the result. + asio::error e(result); + iterator_type iterator; + if (result == 0) + iterator = iterator_type::create(address_info, host_name, service_name); + io_service_.post(asio::detail::bind_handler( + handler_, e, iterator)); + } + + private: + boost::weak_ptr impl_; + query_type query_; + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler handler) + { + if (work_io_service_) + { + start_work_thread(); + work_io_service_->post( + resolve_query_handler( + impl, query, io_service(), handler)); + } + } + + // Resolve an endpoint to a list of entries. + template + iterator_type resolve(implementation_type&, + const endpoint_type& endpoint, Error_Handler error_handler) + { + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; + int result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags); + if (result) + { + flags |= NI_NUMERICSERV; + result = socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags); + } + + error_handler(asio::error(result)); + if (result != 0) + return iterator_type(); + return iterator_type::create(endpoint, host_name, service_name); + } + + template + class resolve_endpoint_handler + { + public: + resolve_endpoint_handler(implementation_type impl, + const endpoint_type& endpoint, asio::io_service& io_service, + Handler handler) + : impl_(impl), + endpoint_(endpoint), + io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + iterator_type iterator; + io_service_.post(asio::detail::bind_handler(handler_, + asio::error(asio::error::operation_aborted), + iterator)); + return; + } + + + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; + int result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags); + if (result) + { + flags |= NI_NUMERICSERV; + result = socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags); + } + + // Invoke the handler and pass the result. + asio::error e(result); + iterator_type iterator; + if (result == 0) + iterator = iterator_type::create(endpoint_, host_name, service_name); + io_service_.post(asio::detail::bind_handler( + handler_, e, iterator)); + } + + private: + boost::weak_ptr impl_; + endpoint_type endpoint_; + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + Handler handler) + { + if (work_io_service_) + { + start_work_thread(); + work_io_service_->post( + resolve_endpoint_handler( + impl, endpoint, io_service(), handler)); + } + } + +private: + // Helper class to run the work io_service in a thread. + class work_io_service_runner + { + public: + work_io_service_runner(asio::io_service& io_service) + : io_service_(io_service) {} + void operator()() { io_service_.run(); } + private: + asio::io_service& io_service_; + }; + + // Start the work thread if it's not already running. + void start_work_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (work_thread_ == 0) + { + work_thread_.reset(new asio::detail::thread( + work_io_service_runner(*work_io_service_))); + } + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Private io_service used for performing asynchronous host resolution. + boost::scoped_ptr work_io_service_; + + // Work for the private io_service to perform. + boost::scoped_ptr work_; + + // Thread used for running the work io_service's run loop. + boost::scoped_ptr work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/scoped_lock.hpp b/library/include/libtorrent/asio/detail/scoped_lock.hpp new file mode 100644 index 000000000..40ad27d2d --- /dev/null +++ b/library/include/libtorrent/asio/detail/scoped_lock.hpp @@ -0,0 +1,79 @@ +// +// scoped_lock.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SCOPED_LOCK_HPP +#define ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template +class scoped_lock + : private noncopyable +{ +public: + // Constructor acquires the lock. + scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/library/include/libtorrent/asio/detail/select_interrupter.hpp b/library/include/libtorrent/asio/detail/select_interrupter.hpp new file mode 100644 index 000000000..6b4d51316 --- /dev/null +++ b/library/include/libtorrent/asio/detail/select_interrupter.hpp @@ -0,0 +1,41 @@ +// +// select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/pipe_select_interrupter.hpp" +#include "asio/detail/socket_select_interrupter.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef socket_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/library/include/libtorrent/asio/detail/select_reactor.hpp b/library/include/libtorrent/asio/detail/select_reactor.hpp new file mode 100644 index 000000000..e6335aef7 --- /dev/null +++ b/library/include/libtorrent/asio/detail/select_reactor.hpp @@ -0,0 +1,435 @@ +// +// select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class select_reactor + : public asio::io_service::service +{ +public: + // Constructor. + select_reactor(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + select_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&select_reactor::call_run_thread, this)); + } + } + + // Destructor. + ~select_reactor() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type descriptor) + { + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (read_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (write_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (except_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler); + interrupt = except_op_queue_.enqueue_operation(descriptor, handler) + || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // select_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run select once until interrupted or events are ready to be dispatched. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // Set up the descriptor sets. + fd_set_adapter read_fds; + read_fds.set(interrupter_.read_descriptor()); + read_op_queue_.get_descriptors(read_fds); + fd_set_adapter write_fds; + write_op_queue_.get_descriptors(write_fds); + fd_set_adapter except_fds; + except_op_queue_.get_descriptors(except_fds); + socket_type max_fd = read_fds.max_descriptor(); + if (write_fds.max_descriptor() > max_fd) + max_fd = write_fds.max_descriptor(); + if (except_fds.max_descriptor() > max_fd) + max_fd = except_fds.max_descriptor(); + + // Block on the select call without holding the lock so that new + // operations can be started while the call is executing. + timeval tv_buf = { 0, 0 }; + timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; + select_in_progress_ = true; + lock.unlock(); + int retval = socket_ops::select(static_cast(max_fd + 1), + read_fds, write_fds, except_fds, tv); + lock.lock(); + select_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Reset the interrupter. + if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor())) + interrupter_.reset(); + + // Dispatch all ready operations. + if (retval > 0) + { + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + except_op_queue_.dispatch_descriptors(except_fds, 0); + read_op_queue_.dispatch_descriptors(read_fds, 0); + write_op_queue_.dispatch_descriptors(write_fds, 0); + except_op_queue_.dispatch_cancellations(); + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + } + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(select_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the select call. + timeval* get_timeout(timeval& tv) + { + if (all_timer_queues_are_empty()) + return 0; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + tv.tv_sec = minimum_wait_duration.total_seconds(); + tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000; + } + else + { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + + return &tv; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the select_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Whether the select loop is currently running or not. + bool select_in_progress_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of exception operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp b/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp new file mode 100644 index 000000000..35c524c7e --- /dev/null +++ b/library/include/libtorrent/asio/detail/select_reactor_fwd.hpp @@ -0,0 +1,31 @@ +// +// select_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SELECT_REACTOR_FWD_HPP +#define ASIO_DETAIL_SELECT_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class select_reactor; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP diff --git a/library/include/libtorrent/asio/detail/service_registry.hpp b/library/include/libtorrent/asio/detail/service_registry.hpp new file mode 100644 index 000000000..d7164d088 --- /dev/null +++ b/library/include/libtorrent/asio/detail/service_registry.hpp @@ -0,0 +1,163 @@ +// +// service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class service_registry + : private noncopyable +{ +public: + // Constructor. + service_registry(Owner& o) + : owner_(o), + first_service_(0) + { + } + + // Destructor. + ~service_registry() + { + // Shutdown all services. This must be done in a separate loop before the + // services are destroyed since the destructors of user-defined handler + // objects may try to access other service objects. + typename Owner::service* service = first_service_; + while (service) + { + service->shutdown_service(); + service = service->next_; + } + + // Destroy all services. + while (first_service_) + { + typename Owner::service* next_service = first_service_->next_; + delete first_service_; + first_service_ = next_service; + } + } + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template + Service& use_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object for the given type. + typename Owner::service* service = first_service_; + while (service) + { + if (*service->type_info_ == typeid(Service)) + return *static_cast(service); + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + std::auto_ptr new_service(new Service(owner_)); + new_service->type_info_ = &typeid(Service); + Service& new_service_ref = *new_service; + lock.lock(); + + // Check that nobody else created another service object of the same type + // while the lock was released. + service = first_service_; + while (service) + { + if (*service->type_info_ == typeid(Service)) + return *static_cast(service); + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + new_service->next_ = first_service_; + first_service_ = new_service.release(); + + return new_service_ref; + } + + // Add a service object. Returns false on error, in which case ownership of + // the object is retained by the caller. + template + bool add_service(Service* new_service) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object for the given type. + typename Owner::service* service = first_service_; + while (service) + { + if (*service->type_info_ == typeid(Service)) + return false; + service = service->next_; + } + + // Take ownership of the service object. + new_service->type_info_ = &typeid(Service); + new_service->next_ = first_service_; + first_service_ = new_service; + } + + // Check whether a service object of the specified type already exists. + template + bool has_service() const + { + asio::detail::mutex::scoped_lock lock(mutex_); + + typename Owner::service* service = first_service_; + while (service) + { + if (*service->type_info_ == typeid(Service)) + return true; + service = service->next_; + } + + return false; + } + +private: + // Mutex to protect access to internal data. + mutable asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + Owner& owner_; + + // The first service in the list of contained services. + typename Owner::service* first_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/library/include/libtorrent/asio/detail/signal_blocker.hpp b/library/include/libtorrent/asio/detail/signal_blocker.hpp new file mode 100644 index 000000000..92299e3e7 --- /dev/null +++ b/library/include/libtorrent/asio/detail/signal_blocker.hpp @@ -0,0 +1,50 @@ +// +// signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_signal_blocker.hpp" +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/win_signal_blocker.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_signal_blocker.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_signal_blocker signal_blocker; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_signal_blocker signal_blocker; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/library/include/libtorrent/asio/detail/signal_init.hpp b/library/include/libtorrent/asio/detail/signal_init.hpp new file mode 100644 index 000000000..95c05e852 --- /dev/null +++ b/library/include/libtorrent/asio/detail/signal_init.hpp @@ -0,0 +1,51 @@ +// +// signal_init.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SIGNAL_INIT_HPP +#define ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/library/include/libtorrent/asio/detail/socket_holder.hpp b/library/include/libtorrent/asio/detail/socket_holder.hpp new file mode 100644 index 000000000..a94e01e2c --- /dev/null +++ b/library/include/libtorrent/asio/detail/socket_holder.hpp @@ -0,0 +1,91 @@ +// +// socket_holder.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SOCKET_HOLDER_HPP +#define ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" + +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + socket_ops::close(socket_); + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + socket_ops::close(socket_); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/library/include/libtorrent/asio/detail/socket_ops.hpp b/library/include/libtorrent/asio/detail/socket_ops.hpp new file mode 100644 index 000000000..c3f4a59b2 --- /dev/null +++ b/library/include/libtorrent/asio/detail/socket_ops.hpp @@ -0,0 +1,1534 @@ +// +// socket_ops.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SOCKET_OPS_HPP +#define ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#if defined(__MACH__) && defined(__APPLE__) +# include +#endif // defined(__MACH__) && defined(__APPLE__) +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +inline int get_error() +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return WSAGetLastError(); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return errno; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void set_error(int error) +{ + errno = error; +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(error); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +template +inline ReturnType error_wrapper(ReturnType return_value) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + errno = WSAGetLastError(); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return return_value; +} + +inline socket_type accept(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); +#if defined(__MACH__) && defined(__APPLE__) + socket_type new_s = error_wrapper(::accept(s, addr, addrlen)); + if (new_s == invalid_socket) + return new_s; + + int optval = 1; + int result = error_wrapper(::setsockopt(new_s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval))); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } + + return new_s; +#else + return error_wrapper(::accept(s, addr, addrlen)); +#endif +} + +inline int bind(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen) +{ + set_error(0); + return error_wrapper(::bind(s, addr, addrlen)); +} + +inline int close(socket_type s) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::closesocket(s)); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::close(s)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int shutdown(socket_type s, int what) +{ + set_error(0); + return error_wrapper(::shutdown(s, what)); +} + +inline int connect(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen) +{ + set_error(0); + return error_wrapper(::connect(s, addr, addrlen)); +} + +inline int listen(socket_type s, int backlog) +{ + set_error(0); + return error_wrapper(::listen(s, backlog)); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +inline void init_buf(buf& b, void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(data); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.iov_base = data; + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(const_cast(data)); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.iov_base = const_cast(data); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int recv(socket_type s, buf* bufs, size_t count, int flags) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; + return error_wrapper(::recvmsg(s, &msg, flags)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags, + socket_addr_type* addr, socket_addr_len_type* addrlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, addrlen, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; +#if defined(__MACH__) && defined(__APPLE__) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040) + msg.msg_name = reinterpret_cast(addr); +#else + msg.msg_name = addr; +#endif + msg.msg_namelen = *addrlen; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; + int result = error_wrapper(::recvmsg(s, &msg, flags)); + *addrlen = msg.msg_namelen; + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int send(socket_type s, const buf* bufs, size_t count, int flags) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, const_cast(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0)); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + return error_wrapper(::sendmsg(s, &msg, flags)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int sendto(socket_type s, const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, socket_addr_len_type addrlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = ::WSASendTo(s, const_cast(bufs), send_buf_count, + &bytes_transferred, flags, addr, addrlen, 0, 0); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; +#if defined(__MACH__) && defined(__APPLE__) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040) + msg.msg_name = reinterpret_cast(const_cast(addr)); +#else + msg.msg_name = const_cast(addr); +#endif + msg.msg_namelen = addrlen; + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + return error_wrapper(::sendmsg(s, &msg, flags)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline socket_type socket(int af, int type, int protocol) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::WSASocket(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED)); +#elif defined(__MACH__) && defined(__APPLE__) + socket_type s = error_wrapper(::socket(af, type, protocol)); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = error_wrapper(::setsockopt(s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval))); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + return error_wrapper(::socket(af, type, protocol)); +#endif +} + +inline int setsockopt(socket_type s, int level, int optname, + const void* optval, size_t optlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::setsockopt(s, level, optname, + reinterpret_cast(optval), static_cast(optlen))); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::setsockopt(s, level, optname, optval, + static_cast(optlen))); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int getsockopt(socket_type s, int level, int optname, void* optval, + size_t* optlen) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + reinterpret_cast(optval), &tmp_optlen)); + *optlen = static_cast(tmp_optlen); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + socklen_t tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + optval, &tmp_optlen)); + *optlen = static_cast(tmp_optlen); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int getpeername(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); + return error_wrapper(::getpeername(s, addr, addrlen)); +} + +inline int getsockname(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen) +{ + set_error(0); + return error_wrapper(::getsockname(s, addr, addrlen)); +} + +inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::ioctlsocket(s, cmd, arg)); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::ioctl(s, cmd, arg)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::select(nfds, readfds, writefds, exceptfds, timeout)); +} + +inline int poll_read(socket_type s) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + set_error(0); + return error_wrapper(::select(s, &fds, 0, 0, 0)); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + set_error(0); + return error_wrapper(::poll(&fds, 1, -1)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int poll_write(socket_type s) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + set_error(0); + return error_wrapper(::select(s, 0, &fds, 0, 0)); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + set_error(0); + return error_wrapper(::poll(&fds, 1, -1)); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id = 0) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != AF_INET && af != AF_INET6) + { + set_error(asio::error::address_family_not_supported); + return 0; + } + + sockaddr_storage_type address; + DWORD address_length; + if (af == AF_INET) + { + address_length = sizeof(sockaddr_in4_type); + sockaddr_in4_type* ipv4_address = + reinterpret_cast(&address); + ipv4_address->sin_family = AF_INET; + ipv4_address->sin_port = 0; + memcpy(&ipv4_address->sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + sockaddr_in6_type* ipv6_address = + reinterpret_cast(&address); + ipv6_address->sin6_family = AF_INET6; + ipv6_address->sin6_port = 0; + ipv6_address->sin6_flowinfo = 0; + ipv6_address->sin6_scope_id = scope_id; + memcpy(&ipv6_address->sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast(length); + int result = error_wrapper(::WSAAddressToStringA( + reinterpret_cast(&address), + address_length, 0, dest, &string_length)); + + // Windows may not set an error code on failure. + if (result == socket_error_retval && get_error() == 0) + set_error(asio::error::invalid_argument); + + return result == socket_error_retval ? 0 : dest; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + const char* result = error_wrapper(::inet_ntop(af, src, dest, length)); + if (result == 0 && get_error() == 0) + set_error(asio::error::invalid_argument); + if (result != 0 && af == AF_INET6 && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[IF_NAMESIZE + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast(src); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id = 0) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != AF_INET && af != AF_INET6) + { + set_error(asio::error::address_family_not_supported); + return -1; + } + + sockaddr_storage_type address; + int address_length = sizeof(sockaddr_storage_type); + int result = error_wrapper(::WSAStringToAddressA( + const_cast(src), af, 0, + reinterpret_cast(&address), + &address_length)); + + if (af == AF_INET) + { + if (result != socket_error_retval) + { + sockaddr_in4_type* ipv4_address = + reinterpret_cast(&address); + memcpy(dest, &ipv4_address->sin_addr, sizeof(in4_addr_type)); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = INADDR_NONE; + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + sockaddr_in6_type* ipv6_address = + reinterpret_cast(&address); + memcpy(dest, &ipv6_address->sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = ipv6_address->sin6_scope_id; + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && get_error() == 0) + set_error(asio::error::invalid_argument); + + return result == socket_error_retval ? -1 : 1; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::inet_pton(af, src, dest)); + if (result <= 0 && get_error() == 0) + set_error(asio::error::invalid_argument); + if (result > 0 && af == AF_INET6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (const char* if_name = strchr(src, '%')) + { + in6_addr_type* ipv6_address = static_cast(dest); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (is_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int gethostname(char* name, int namelen) +{ + set_error(0); + return error_wrapper(::gethostname(name, namelen)); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline int translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error::success; + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + BOOST_ASSERT(false); + return get_error(); + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, int* error) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af)); + *error = get_error(); + if (!retval) + return 0; + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, error)); + *error = translate_netdb_error(*error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::getipnodebyaddr(addr, length, af, error)); + *error = translate_netdb_error(*error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, + buflength, &retval, error)); + *error = translate_netdb_error(*error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int* error, int ai_flags = 0) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != AF_INET) + { + *error = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname(name)); + *error = get_error(); + if (!retval) + return 0; + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != AF_INET) + { + *error = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, error)); + *error = translate_netdb_error(*error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::getipnodebyname( + name, af, ai_flags, error)); + *error = translate_netdb_error(*error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != AF_INET) + { + *error = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + error_wrapper(::gethostbyname_r(name, result, buffer, buflength, &retval, + error)); + *error = translate_netdb_error(*error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = gai_alloc(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = AF_INET; + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = gai_alloc(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = AF_INET6; + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(serv[0])) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case AF_UNSPEC: + break; + case AF_INET: + case AF_INET6: + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = AF_UNSPEC; + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET6) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + int herr = 0; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), &herr, hints.ai_flags); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + switch (herr) + { + case HOST_NOT_FOUND: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + case NO_RECOVERY: + return EAI_FAIL; + case NO_DATA: + default: + return EAI_NONAME; + } + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + canon = gai_alloc(strlen(hptr->h_name) + 1); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + strcpy(canon, hptr->h_name); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + aihead->ai_canonname = gai_alloc(strlen(search[0].host) + 1); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + strcpy(aihead->ai_canonname, search[0].host); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline int getnameinfo_emulation(const socket_addr_type* sa, + socket_addr_len_type salen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof(sockaddr_in4_type)) + { + set_error(asio::error::invalid_argument); + return 1; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast(sa)->sin_port; + break; + case AF_INET6: + if (salen != sizeof(sockaddr_in6_type)) + { + set_error(asio::error::invalid_argument); + return 1; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast(sa)->sin6_port; + break; + default: + set_error(asio::error::address_family_not_supported); + return 1; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen) == 0) + { + return 1; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + int herr = 0; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), &herr); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + *host = '\0'; + strncat(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + set_error(asio::error::host_not_found); + return 1; + } + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen) == 0) + { + return 1; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + set_error(asio::error::no_buffer_space); + return 1; + } + sprintf(serv, "%u", ntohs(port)); + } + else + { +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + *serv = '\0'; + strncat(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + set_error(asio::error::no_buffer_space); + return 1; + } + sprintf(serv, "%u", ntohs(port)); + } +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + } + } + + set_error(0); + return 0; +} + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +inline int translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error::success; + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. + return get_error(); + } +} + +inline int getaddrinfo(const char* host, const char* service, + const addrinfo_type* hints, addrinfo_type** result) +{ + set_error(0); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, hints, result); + return translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, hints, result); + return translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, hints, result); + return translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + int error = getaddrinfo_emulation(host, service, hints, result); + return translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, hints, result); + return translate_addrinfo_error(error); +#endif +} + +inline void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +inline int getnameinfo(const socket_addr_type* addr, + socket_addr_len_type addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + set_error(0); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + socket_addr_len_type, char*, std::size_t, char*, std::size_t, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + set_error(0); + int error = gni(addr, addrlen, host, hostlen, serv, servlen, flags); + return translate_addrinfo_error(error); + } + } + set_error(0); + int error = getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags); + return translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + tmp_addr.ss_len = addrlen; + addr = reinterpret_cast(&tmp_addr); + set_error(0); + int error = getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags); + return translate_addrinfo_error(error); +#else + set_error(0); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return translate_addrinfo_error(error); +#endif +} + +inline u_long_type network_to_host_long(u_long_type value) +{ + return ntohl(value); +} + +inline u_long_type host_to_network_long(u_long_type value) +{ + return htonl(value); +} + +inline u_short_type network_to_host_short(u_short_type value) +{ + return ntohs(value); +} + +inline u_short_type host_to_network_short(u_short_type value) +{ + return htons(value); +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/library/include/libtorrent/asio/detail/socket_option.hpp b/library/include/libtorrent/asio/detail/socket_option.hpp new file mode 100644 index 000000000..c8e680e14 --- /dev/null +++ b/library/include/libtorrent/asio/detail/socket_option.hpp @@ -0,0 +1,323 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SOCKET_OPTION_HPP +#define ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + boolean(bool value) + : value_(value ? 1 : 0) + { + } + + // Set the value of the boolean. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the boolean. + bool get() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + integer(int value) + : value_(value) + { + } + + // Set the value of the int option. + void set(int value) + { + value_ = value; + } + + // Get the current value of the int option. + int get() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing unsigned integer options. +template +class unsigned_integer +{ +public: + // Default constructor. + unsigned_integer() + : value_(0) + { + } + + // Construct with a specific option value. + unsigned_integer(unsigned int value) + : value_(value) + { + } + + // Set the value of the int option. + void set(unsigned int value) + { + value_ = value; + } + + // Get the current value of the int option. + unsigned int get() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template + unsigned int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const unsigned int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + unsigned int value_; +}; + +// Helper template for implementing linger options. +template +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool value, unsigned short timeout) + { + value_.l_onoff = value ? 1 : 0; + value_.l_linger = timeout; + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout(unsigned short value) + { + value_.l_linger = value; + } + + // Get the value for the linger timeout. + unsigned short timeout() const + { + return value_.l_linger; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the linger data. + template + ::linger* data(const Protocol&) + { + return &value_; + } + + // Get the address of the linger data. + template + const ::linger* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the linger data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + ::linger value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp b/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 000000000..e833213de --- /dev/null +++ b/library/include/libtorrent/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,177 @@ +// +// socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + socket_select_interrupter() + { + socket_holder acceptor(socket_ops::socket(AF_INET, SOCK_STREAM, + IPPROTO_TCP)); + if (acceptor.get() == invalid_socket) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + int opt = 1; + socket_ops::setsockopt(acceptor.get(), + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + sockaddr_in4_type addr; + socket_addr_len_type addr_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len) == socket_error_retval) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (getsockname(acceptor.get(), (socket_addr_type*)&addr, &addr_len) + == socket_error_retval) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (socket_ops::listen(acceptor.get(), SOMAXCONN) == socket_error_retval) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + socket_holder client(socket_ops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + if (client.get() == invalid_socket) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len) == socket_error_retval) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0)); + if (server.get() == invalid_socket) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking)) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(client.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + + non_blocking = 1; + if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking)) + { + asio::error e(socket_ops::get_error()); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(server.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); + } + + // Destructor. + ~socket_select_interrupter() + { + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + socket_ops::send(write_descriptor_, &b, 1, 0); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/library/include/libtorrent/asio/detail/socket_types.hpp b/library/include/libtorrent/asio/detail/socket_types.hpp new file mode 100644 index 000000000..631edbe1c --- /dev/null +++ b/library/include/libtorrent/asio/detail/socket_types.hpp @@ -0,0 +1,184 @@ +// +// socket_types.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_SOCKET_TYPES_HPP +#define ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message("Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately") +# pragma message("Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target)") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately +# warning Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0500 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# include // Needed for __errno +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# if !defined(_WSPIAPI_H_) +# define _WSPIAPI_H_ +# define ASIO_WSPIAPI_H_DEFINED +# endif // !defined(_WSPIAPI_H_) +# endif // defined(__BORLANDC__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if defined(__CYGWIN__) +# if !defined(__USE_W32_SOCKETS) +# error You must add -D__USE_W32_SOCKETS to your compiler options. +# endif // !defined(__USE_W32_SOCKETS) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // defined(__CYGWIN__) +# include +# include +# include +# if defined(ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef ASIO_WSPIAPI_H_DEFINED +# endif // defined(ASIO_WSPIAPI_H_DEFINED) +# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "mswsock.lib") +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# include "asio/detail/old_win_sdk_compat.hpp" +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# if defined(__sun) +# include +# endif +#endif +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef sockaddr socket_addr_type; +typedef int socket_addr_len_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +# if defined(ASIO_HAS_OLD_WIN_SDK) +typedef in6_addr_emulation in6_addr_type; +typedef ipv6_mreq_emulation in6_mreq_type; +typedef sockaddr_in6_emulation sockaddr_in6_type; +typedef sockaddr_storage_emulation sockaddr_storage_type; +typedef addrinfo_emulation addrinfo_type; +# else +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +# endif +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +const int shutdown_receive = SD_RECEIVE; +const int shutdown_send = SD_SEND; +const int shutdown_both = SD_BOTH; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_v4_str_len = INET_ADDRSTRLEN; +const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +typedef sockaddr socket_addr_type; +typedef socklen_t socket_addr_len_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +const int shutdown_receive = SHUT_RD; +const int shutdown_send = SHUT_WR; +const int shutdown_both = SHUT_RDWR; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#endif +const int custom_socket_option_level = 0xA5100000; +const int enable_connection_aborted_option = 1; + +#if defined(_WIN64) +std::size_t hash_value(SOCKET s) +{ + return static_cast(s); +} +#endif // defined(_WIN64) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/library/include/libtorrent/asio/detail/strand_service.hpp b/library/include/libtorrent/asio/detail/strand_service.hpp new file mode 100644 index 000000000..778cff66f --- /dev/null +++ b/library/include/libtorrent/asio/detail/strand_service.hpp @@ -0,0 +1,528 @@ +// +// strand_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_service + : public asio::io_service::service +{ +public: + class handler_base; + class invoke_current_handler; + class post_next_waiter_on_exit; + + // The underlying implementation of a strand. + class strand_impl + { +#if defined (__BORLANDC__) + public: +#else + private: +#endif + void add_ref() + { + asio::detail::mutex::scoped_lock lock(mutex_); + ++ref_count_; + } + + void release() + { + asio::detail::mutex::scoped_lock lock(mutex_); + --ref_count_; + if (ref_count_ == 0) + { + lock.unlock(); + delete this; + } + } + + private: + // Only this service will have access to the internal values. + friend class strand_service; + friend class post_next_waiter_on_exit; + friend class invoke_current_handler; + + strand_impl(strand_service& owner) + : owner_(owner), + current_handler_(0), + first_waiter_(0), + last_waiter_(0), + ref_count_(0) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(owner_.mutex_); + next_ = owner_.impl_list_; + prev_ = 0; + if (owner_.impl_list_) + owner_.impl_list_->prev_ = this; + owner_.impl_list_ = this; + } + + ~strand_impl() + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(owner_.mutex_); + if (owner_.impl_list_ == this) + owner_.impl_list_ = next_; + if (prev_) + prev_->next_ = next_; + if (next_) + next_->prev_= prev_; + next_ = 0; + prev_ = 0; + lock.unlock(); + + if (current_handler_) + { + current_handler_->destroy(); + } + + while (first_waiter_) + { + handler_base* next = first_waiter_->next_; + first_waiter_->destroy(); + first_waiter_ = next; + } + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The service that owns this implementation. + strand_service& owner_; + + // The handler that is ready to execute. If this pointer is non-null then it + // indicates that a handler holds the lock. + handler_base* current_handler_; + + // The start of the list of waiting handlers for the strand. + handler_base* first_waiter_; + + // The end of the list of waiting handlers for the strand. + handler_base* last_waiter_; + + // Storage for posted handlers. + typedef boost::aligned_storage<64> handler_storage_type; +#if defined(__BORLANDC__) + boost::aligned_storage<64> handler_storage_; +#else + handler_storage_type handler_storage_; +#endif + + // Pointers to adjacent socket implementations in linked list. + strand_impl* next_; + strand_impl* prev_; + + // The reference count on the strand implementation. + size_t ref_count_; + +#if !defined(__BORLANDC__) + friend void intrusive_ptr_add_ref(strand_impl* p) + { + p->add_ref(); + } + + friend void intrusive_ptr_release(strand_impl* p) + { + p->release(); + } +#endif + }; + + friend class strand_impl; + + typedef boost::intrusive_ptr implementation_type; + + // Base class for all handler types. + class handler_base + { + public: + typedef void (*invoke_func_type)(handler_base*, + strand_service&, implementation_type&); + typedef void (*destroy_func_type)(handler_base*); + + handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func) + : next_(0), + invoke_func_(invoke_func), + destroy_func_(destroy_func) + { + } + + void invoke(strand_service& service_impl, implementation_type& impl) + { + invoke_func_(this, service_impl, impl); + } + + void destroy() + { + destroy_func_(this); + } + + protected: + ~handler_base() + { + } + + private: + friend class strand_service; + friend class strand_impl; + friend class post_next_waiter_on_exit; + handler_base* next_; + invoke_func_type invoke_func_; + destroy_func_type destroy_func_; + }; + + // Helper class to allow handlers to be dispatched. + class invoke_current_handler + { + public: + invoke_current_handler(strand_service& service_impl, + const implementation_type& impl) + : service_impl_(service_impl), + impl_(impl) + { + } + + void operator()() + { + impl_->current_handler_->invoke(service_impl_, impl_); + } + + friend void* asio_handler_allocate(std::size_t size, + invoke_current_handler* this_handler) + { + return this_handler->do_handler_allocate(size); + } + + friend void asio_handler_deallocate(void*, std::size_t, + invoke_current_handler*) + { + } + + void* do_handler_allocate(std::size_t size) + { +#if defined(__BORLANDC__) + BOOST_ASSERT(size <= boost::aligned_storage<64>::size); +#else + BOOST_ASSERT(size <= strand_impl::handler_storage_type::size); +#endif + return impl_->handler_storage_.address(); + } + + template + friend void asio_handler_invoke(Function function, + invoke_current_handler*) + { + function(); + } + + private: + strand_service& service_impl_; + implementation_type impl_; + }; + + // Helper class to automatically enqueue next waiter on block exit. + class post_next_waiter_on_exit + { + public: + post_next_waiter_on_exit(strand_service& service_impl, + implementation_type& impl) + : service_impl_(service_impl), + impl_(impl), + cancelled_(false) + { + } + + ~post_next_waiter_on_exit() + { + if (!cancelled_) + { + asio::detail::mutex::scoped_lock lock(impl_->mutex_); + impl_->current_handler_ = impl_->first_waiter_; + if (impl_->current_handler_) + { + impl_->first_waiter_ = impl_->first_waiter_->next_; + if (impl_->first_waiter_ == 0) + impl_->last_waiter_ = 0; + lock.unlock(); + service_impl_.io_service().post( + invoke_current_handler(service_impl_, impl_)); + } + } + } + + void cancel() + { + cancelled_ = true; + } + + private: + strand_service& service_impl_; + implementation_type& impl_; + bool cancelled_; + }; + + // Class template for a waiter. + template + class handler_wrapper + : public handler_base + { + public: + handler_wrapper(Handler handler) + : handler_base(&handler_wrapper::do_invoke, + &handler_wrapper::do_destroy), + handler_(handler) + { + } + + static void do_invoke(handler_base* base, + strand_service& service_impl, implementation_type& impl) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + post_next_waiter_on_exit p1(service_impl, impl); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(h->handler_); + + // A handler object must still be valid when the next waiter is posted + // since destroying the last handler might cause the strand object to be + // destroyed. Therefore we create a second post_next_waiter_on_exit object + // that will be destroyed before the handler object. + p1.cancel(); + post_next_waiter_on_exit p2(service_impl, impl); + + // Free the memory associated with the handler. + ptr.reset(); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl.get()); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void do_destroy(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + } + + private: + Handler handler_; + }; + + // Construct a new strand service for the specified io_service. + explicit strand_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Construct a list of all handlers to be destroyed. + asio::detail::mutex::scoped_lock lock(mutex_); + strand_impl* impl = impl_list_; + handler_base* first_handler = 0; + while (impl) + { + if (impl->current_handler_) + { + impl->current_handler_->next_ = first_handler; + first_handler = impl->current_handler_; + impl->current_handler_ = 0; + } + if (impl->first_waiter_) + { + impl->last_waiter_->next_ = first_handler; + first_handler = impl->first_waiter_; + impl->first_waiter_ = 0; + impl->last_waiter_ = 0; + } + impl = impl->next_; + } + + // Destroy all handlers without holding the lock. + lock.unlock(); + while (first_handler) + { + handler_base* next = first_handler->next_; + first_handler->destroy(); + first_handler = next; + } + } + + // Construct a new strand implementation. + void construct(implementation_type& impl) + { + impl = implementation_type(new strand_impl(*this)); + } + + // Destroy a strand implementation. + void destroy(implementation_type& impl) + { + implementation_type().swap(impl); + } + + // Request the io_service to invoke the given handler. + template + void dispatch(implementation_type& impl, Handler handler) + { + if (call_stack::contains(impl.get())) + { + asio_handler_invoke_helpers::invoke(handler, &handler); + } + else + { + asio::detail::mutex::scoped_lock lock(impl->mutex_); + + // Allocate and construct an object to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + if (impl->current_handler_ == 0) + { + // This handler now has the lock, so can be dispatched immediately. + impl->current_handler_ = ptr.get(); + lock.unlock(); + io_service().dispatch(invoke_current_handler(*this, impl)); + ptr.release(); + } + else + { + // Another handler already holds the lock, so this handler must join + // the list of waiters. The handler will be posted automatically when + // its turn comes. + if (impl->last_waiter_) + { + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; + } + else + { + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); + } + ptr.release(); + } + } + } + + // Request the io_service to invoke the given handler and return immediately. + template + void post(implementation_type& impl, Handler handler) + { + asio::detail::mutex::scoped_lock lock(impl->mutex_); + + // Allocate and construct an object to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + if (impl->current_handler_ == 0) + { + // This handler now has the lock, so can be dispatched immediately. + impl->current_handler_ = ptr.get(); + lock.unlock(); + io_service().post(invoke_current_handler(*this, impl)); + ptr.release(); + } + else + { + // Another handler already holds the lock, so this handler must join the + // list of waiters. The handler will be posted automatically when its turn + // comes. + if (impl->last_waiter_) + { + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; + } + else + { + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); + } + ptr.release(); + } + } + +private: + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + strand_impl* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#if defined(__BORLANDC__) + +namespace boost { + +inline void intrusive_ptr_add_ref( + asio::detail::strand_service::strand_impl* p) +{ + p->add_ref(); +} + +inline void intrusive_ptr_release( + asio::detail::strand_service::strand_impl* p) +{ + p->release(); +} + +} // namespace boost + +#endif // defined(__BORLANDC__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/task_io_service.hpp b/library/include/libtorrent/asio/detail/task_io_service.hpp new file mode 100644 index 000000000..fbe56cbc3 --- /dev/null +++ b/library/include/libtorrent/asio/detail/task_io_service.hpp @@ -0,0 +1,526 @@ +// +// task_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_TASK_IO_SERVICE_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/task_io_service_fwd.hpp" + +namespace asio { +namespace detail { + +template +class task_io_service + : public asio::io_service::service +{ +public: + // Constructor. + task_io_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + mutex_(), + task_(use_service(io_service)), + outstanding_work_(0), + handler_queue_(&task_handler_), + handler_queue_end_(&task_handler_), + interrupted_(false), + shutdown_(false), + first_idle_thread_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + // Destroy handler objects. + while (handler_queue_) + { + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (h != &task_handler_) + h->destroy(); + } + + // Reset handler queue to initial state. + handler_queue_ = &task_handler_; + handler_queue_end_ = &task_handler_; + } + + // Run the event loop until interrupted or no more work. + size_t run() + { + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + while (do_one(lock, &this_idle_thread)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Run until interrupted or one operation is performed. + size_t run_one() + { + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, &this_idle_thread); + } + + // Poll for operations without blocking. + size_t poll() + { + typename call_stack::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + while (do_one(lock, 0)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one() + { + typename call_stack::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, 0); + } + + // Interrupt the event processing loop. + void interrupt() + { + asio::detail::mutex::scoped_lock lock(mutex_); + interrupt_all_threads(); + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + asio::detail::mutex::scoped_lock lock(mutex_); + interrupted_ = false; + } + + // Notify that some work has started. + void work_started() + { + asio::detail::mutex::scoped_lock lock(mutex_); + ++outstanding_work_; + } + + // Notify that some work has finished. + void work_finished() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (--outstanding_work_ == 0) + interrupt_all_threads(); + } + + // Request invocation of the given handler. + template + void dispatch(Handler handler) + { + if (call_stack::contains(this)) + asio_handler_invoke_helpers::invoke(handler, &handler); + else + post(handler); + } + + // Request invocation of the given handler and return immediately. + template + void post(Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + asio::detail::mutex::scoped_lock lock(mutex_); + + // If the service has been shut down we silently discard the handler. + if (shutdown_) + return; + + // Add the handler to the end of the queue. + if (handler_queue_end_) + { + handler_queue_end_->next_ = ptr.get(); + handler_queue_end_ = ptr.get(); + } + else + { + handler_queue_ = handler_queue_end_ = ptr.get(); + } + ptr.release(); + + // An undelivered handler is treated as unfinished work. + ++outstanding_work_; + + // Wake up a thread to execute the handler. + if (!interrupt_one_idle_thread()) + if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_) + task_.interrupt(); + } + +private: + struct idle_thread_info; + + size_t do_one(asio::detail::mutex::scoped_lock& lock, + idle_thread_info* this_idle_thread) + { + if (outstanding_work_ == 0 && !interrupted_) + { + interrupt_all_threads(); + return 0; + } + + bool polling = !this_idle_thread; + bool task_has_run = false; + while (!interrupted_) + { + if (handler_queue_) + { + // Prepare to execute first handler from queue. + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (handler_queue_ == 0) + handler_queue_end_ = 0; + bool more_handlers = (handler_queue_ != 0); + lock.unlock(); + + if (h == &task_handler_) + { + // If the task has already run and we're polling then we're done. + if (task_has_run && polling) + return 0; + task_has_run = true; + + task_cleanup c(lock, *this); + + // Run the task. May throw an exception. Only block if the handler + // queue is empty and we have an idle_thread_info object, otherwise + // we want to return as soon as possible. + task_.run(!more_handlers && !polling); + } + else + { + handler_cleanup c(lock, *this); + + // Invoke the handler. May throw an exception. + h->call(); // call() deletes the handler object + + return 1; + } + } + else if (this_idle_thread) + { + // Nothing to run right now, so just wait for work to do. + if (first_idle_thread_) + { + this_idle_thread->next = first_idle_thread_; + this_idle_thread->prev = first_idle_thread_->prev; + first_idle_thread_->prev->next = this_idle_thread; + first_idle_thread_->prev = this_idle_thread; + } + first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(); + lock.unlock(); + this_idle_thread->wakeup_event.wait(); + lock.lock(); + if (this_idle_thread->next == this_idle_thread) + { + first_idle_thread_ = 0; + } + else + { + if (first_idle_thread_ == this_idle_thread) + first_idle_thread_ = this_idle_thread->next; + this_idle_thread->next->prev = this_idle_thread->prev; + this_idle_thread->prev->next = this_idle_thread->next; + this_idle_thread->next = this_idle_thread; + this_idle_thread->prev = this_idle_thread; + } + } + else + { + return 0; + } + } + + return 0; + } + + // Interrupt the task and all idle threads. + void interrupt_all_threads() + { + interrupted_ = true; + interrupt_all_idle_threads(); + if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_) + task_.interrupt(); + } + + // Interrupt a single idle thread. Returns true if a thread was interrupted, + // false if no running thread could be found to interrupt. + bool interrupt_one_idle_thread() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + first_idle_thread_ = first_idle_thread_->next; + return true; + } + return false; + } + + // Interrupt all idle threads. + void interrupt_all_idle_threads() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + idle_thread_info* current_idle_thread = first_idle_thread_->next; + while (current_idle_thread != first_idle_thread_) + { + current_idle_thread->wakeup_event.signal(); + current_idle_thread = current_idle_thread->next; + } + } + } + + class task_cleanup; + + // The base class for all handler wrappers. A function pointer is used + // instead of virtual functions to avoid the associated overhead. + class handler_base + { + public: + typedef void (*call_func_type)(handler_base*); + typedef void (*destroy_func_type)(handler_base*); + + handler_base(call_func_type call_func, destroy_func_type destroy_func) + : next_(0), + call_func_(call_func), + destroy_func_(destroy_func) + { + } + + void call() + { + call_func_(this); + } + + void destroy() + { + destroy_func_(this); + } + + protected: + // Prevent deletion through this type. + ~handler_base() + { + } + + private: + friend class task_io_service; + friend class task_cleanup; + handler_base* next_; + call_func_type call_func_; + destroy_func_type destroy_func_; + }; + + // Template wrapper for handlers. + template + class handler_wrapper + : public handler_base + { + public: + handler_wrapper(Handler handler) + : handler_base(&handler_wrapper::do_call, + &handler_wrapper::do_destroy), + handler_(handler) + { + } + + static void do_call(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(h->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void do_destroy(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + } + + private: + Handler handler_; + }; + + // Helper class to perform task-related operations on block exit. + class task_cleanup; + friend class task_cleanup; + class task_cleanup + { + public: + task_cleanup(asio::detail::mutex::scoped_lock& lock, + task_io_service& task_io_svc) + : lock_(lock), + task_io_service_(task_io_svc) + { + } + + ~task_cleanup() + { + // Reinsert the task at the end of the handler queue. + lock_.lock(); + task_io_service_.task_handler_.next_ = 0; + if (task_io_service_.handler_queue_end_) + { + task_io_service_.handler_queue_end_->next_ + = &task_io_service_.task_handler_; + task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; + } + else + { + task_io_service_.handler_queue_ + = task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; + } + } + + private: + asio::detail::mutex::scoped_lock& lock_; + task_io_service& task_io_service_; + }; + + // Helper class to perform handler-related operations on block exit. + class handler_cleanup; + friend class handler_cleanup; + class handler_cleanup + { + public: + handler_cleanup(asio::detail::mutex::scoped_lock& lock, + task_io_service& task_io_svc) + : lock_(lock), + task_io_service_(task_io_svc) + { + } + + ~handler_cleanup() + { + lock_.lock(); + if (--task_io_service_.outstanding_work_ == 0) + task_io_service_.interrupt_all_threads(); + } + + private: + asio::detail::mutex::scoped_lock& lock_; + task_io_service& task_io_service_; + }; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The task to be run by this service. + Task& task_; + + // Handler object to represent the position of the task in the queue. + class task_handler + : public handler_base + { + public: + task_handler() + : handler_base(0, 0) + { + } + } task_handler_; + + // The count of unfinished work. + int outstanding_work_; + + // The start of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_; + + // The end of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_end_; + + // Flag to indicate that the dispatcher has been interrupted. + bool interrupted_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; + + // Structure containing information about an idle thread. + struct idle_thread_info + { + event wakeup_event; + idle_thread_info* prev; + idle_thread_info* next; + }; + + // The number of threads that are currently idle. + idle_thread_info* first_idle_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp b/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp new file mode 100644 index 000000000..8100beac7 --- /dev/null +++ b/library/include/libtorrent/asio/detail/task_io_service_fwd.hpp @@ -0,0 +1,31 @@ +// +// task_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class task_io_service; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP diff --git a/library/include/libtorrent/asio/detail/thread.hpp b/library/include/libtorrent/asio/detail/thread.hpp new file mode 100644 index 000000000..9d7452b8a --- /dev/null +++ b/library/include/libtorrent/asio/detail/thread.hpp @@ -0,0 +1,50 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_THREAD_HPP +#define ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_thread.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_thread.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_thread.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_thread thread; +#elif defined(BOOST_WINDOWS) +typedef win_thread thread; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_thread thread; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_HPP diff --git a/library/include/libtorrent/asio/detail/timer_queue.hpp b/library/include/libtorrent/asio/detail/timer_queue.hpp new file mode 100644 index 000000000..2526da948 --- /dev/null +++ b/library/include/libtorrent/asio/detail/timer_queue.hpp @@ -0,0 +1,345 @@ +// +// timer_queue.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_TIMER_QUEUE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/timer_queue_base.hpp" + +namespace asio { +namespace detail { + +template +class timer_queue + : public timer_queue_base +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Constructor. + timer_queue() + : timers_(), + heap_() + { + } + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + template + bool enqueue_timer(const time_type& time, Handler handler, void* token) + { + // Ensure that there is space for the timer in the heap. We reserve here so + // that the push_back below will not throw due to a reallocation failure. + heap_.reserve(heap_.size() + 1); + + // Create a new timer object. + std::auto_ptr > new_timer( + new timer(time, handler, token)); + + // Insert the new timer into the hash. + typedef typename hash_map::iterator iterator; + typedef typename hash_map::value_type value_type; + std::pair result = + timers_.insert(value_type(token, new_timer.get())); + if (!result.second) + { + result.first->second->prev_ = new_timer.get(); + new_timer->next_ = result.first->second; + result.first->second = new_timer.get(); + } + + // Put the timer at the correct position in the heap. + new_timer->heap_index_ = heap_.size(); + heap_.push_back(new_timer.get()); + up_heap(heap_.size() - 1); + bool is_first = (heap_[0] == new_timer.get()); + + // Ownership of the timer is transferred to the timer queue. + new_timer.release(); + + return is_first; + } + + // Whether there are no timers in the queue. + virtual bool empty() const + { + return heap_.empty(); + } + + // Get the time for the timer that is earliest in the queue. + virtual boost::posix_time::time_duration wait_duration() const + { + return Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); + } + + // Dispatch the timers that are earlier than the specified time. + virtual void dispatch_timers() + { + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) + { + timer_base* t = heap_[0]; + remove_timer(t); + t->invoke(0); + } + } + + // Cancel the timer with the given token. The handler will be invoked + // immediately with the result operation_aborted. + std::size_t cancel_timer(void* timer_token) + { + std::size_t num_cancelled = 0; + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(timer_token); + if (it != timers_.end()) + { + timer_base* t = it->second; + while (t) + { + timer_base* next = t->next_; + remove_timer(t); + t->invoke(asio::error::operation_aborted); + t = next; + ++num_cancelled; + } + } + return num_cancelled; + } + + // Destroy all timers. + virtual void destroy_timers() + { + typename hash_map::iterator i = timers_.begin(); + typename hash_map::iterator end = timers_.end(); + while (i != end) + { + timer_base* t = i->second; + typename hash_map::iterator old_i = i++; + timers_.erase(old_i); + t->destroy(); + } + heap_.clear(); + timers_.clear(); + } + +private: + // Base class for timer operations. Function pointers are used instead of + // virtual functions to avoid the associated overhead. + class timer_base + { + public: + // Perform the timer operation and then destroy. + void invoke(int result) + { + invoke_func_(this, result); + } + + // Destroy the timer operation. + void destroy() + { + destroy_func_(this); + } + + protected: + typedef void (*invoke_func_type)(timer_base*, int); + typedef void (*destroy_func_type)(timer_base*); + + // Constructor. + timer_base(invoke_func_type invoke_func, destroy_func_type destroy_func, + const time_type& time, void* token) + : invoke_func_(invoke_func), + destroy_func_(destroy_func), + time_(time), + token_(token), + next_(0), + prev_(0), + heap_index_( + std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + } + + // Prevent deletion through this type. + ~timer_base() + { + } + + private: + friend class timer_queue; + + // The function to be called to dispatch the handler. + invoke_func_type invoke_func_; + + // The function to be called to destroy the handler. + destroy_func_type destroy_func_; + + // The time when the operation should fire. + time_type time_; + + // The token associated with the timer. + void* token_; + + // The next timer known to the queue. + timer_base* next_; + + // The previous timer known to the queue. + timer_base* prev_; + + // The index of the timer in the heap. + size_t heap_index_; + }; + + // Adaptor class template for using handlers in timers. + template + class timer + : public timer_base + { + public: + // Constructor. + timer(const time_type& time, Handler handler, void* token) + : timer_base(&timer::invoke_handler, + &timer::destroy_handler, time, token), + handler_(handler) + { + } + + // Invoke the handler and then destroy it. + static void invoke_handler(timer_base* base, int result) + { + std::auto_ptr > t(static_cast*>(base)); + t->handler_(result); + } + + // Destroy the handler. + static void destroy_handler(timer_base* base) + { + delete static_cast*>(base); + } + + private: + Handler handler_; + }; + + // Move the item at the given index up the heap to its correct position. + void up_heap(size_t index) + { + size_t parent = (index - 1) / 2; + while (index > 0 + && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_)) + { + swap_heap(index, parent); + index = parent; + parent = (index - 1) / 2; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(size_t index) + { + size_t child = index * 2 + 1; + while (child < heap_.size()) + { + size_t min_child = (child + 1 == heap_.size() + || Time_Traits::less_than( + heap_[child]->time_, heap_[child + 1]->time_)) + ? child : child + 1; + if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(size_t index1, size_t index2) + { + timer_base* tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1]->heap_index_ = index1; + heap_[index2]->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(timer_base* t) + { + // Remove the timer from the heap. + size_t index = t->heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + heap_.pop_back(); + size_t parent = (index - 1) / 2; + if (index > 0 && Time_Traits::less_than(t->time_, heap_[parent]->time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the hash. + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(t->token_); + if (it != timers_.end()) + { + if (it->second == t) + it->second = t->next_; + if (t->prev_) + t->prev_->next_ = t->next_; + if (t->next_) + t->next_->prev_ = t->prev_; + if (it->second == 0) + timers_.erase(it); + } + } + + // A hash of timer token to linked lists of timers. + hash_map timers_; + + // The heap of timers, with the earliest timer at the front. + std::vector heap_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_HPP diff --git a/library/include/libtorrent/asio/detail/timer_queue_base.hpp b/library/include/libtorrent/asio/detail/timer_queue_base.hpp new file mode 100644 index 000000000..22f3bb1e5 --- /dev/null +++ b/library/include/libtorrent/asio/detail/timer_queue_base.hpp @@ -0,0 +1,56 @@ +// +// timer_queue_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_TIMER_QUEUE_BASE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class timer_queue_base + : private noncopyable +{ +public: + // Destructor. + virtual ~timer_queue_base() {} + + // Whether there are no timers in the queue. + virtual bool empty() const = 0; + + // Get the time to wait until the next timer. + virtual boost::posix_time::time_duration wait_duration() const = 0; + + // Dispatch all ready timers. + virtual void dispatch_timers() = 0; + + // Destroy all timers. + virtual void destroy_timers() = 0; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP diff --git a/library/include/libtorrent/asio/detail/tss_ptr.hpp b/library/include/libtorrent/asio/detail/tss_ptr.hpp new file mode 100644 index 000000000..24ce434c2 --- /dev/null +++ b/library/include/libtorrent/asio/detail/tss_ptr.hpp @@ -0,0 +1,65 @@ +// +// tss_ptr.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_TSS_PTR_HPP +#define ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_tss_ptr.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_tss_ptr.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_tss_ptr.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +template +class tss_ptr +#if !defined(BOOST_HAS_THREADS) + : public null_tss_ptr +#elif defined(BOOST_WINDOWS) + : public win_tss_ptr +#elif defined(BOOST_HAS_PTHREADS) + : public posix_tss_ptr +#endif +{ +public: + void operator=(T* value) + { +#if !defined(BOOST_HAS_THREADS) + null_tss_ptr::operator=(value); +#elif defined(BOOST_WINDOWS) + win_tss_ptr::operator=(value); +#elif defined(BOOST_HAS_PTHREADS) + posix_tss_ptr::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TSS_PTR_HPP diff --git a/library/include/libtorrent/asio/detail/win_event.hpp b/library/include/libtorrent/asio/detail/win_event.hpp new file mode 100644 index 000000000..b08cf8a08 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_event.hpp @@ -0,0 +1,88 @@ +// +// win_event.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_EVENT_HPP +#define ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + win_event() + : event_(::CreateEvent(0, true, false, 0)) + { + if (!event_) + { + DWORD last_error = ::GetLastError(); + system_exception e("event", last_error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_event() + { + ::CloseHandle(event_); + } + + // Signal the event. + void signal() + { + ::SetEvent(event_); + } + + // Reset the event. + void clear() + { + ::ResetEvent(event_); + } + + // Wait for the event to become signalled. + void wait() + { + ::WaitForSingleObject(event_, INFINITE); + } + +private: + HANDLE event_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp b/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 000000000..1f393dc11 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,83 @@ +// +// win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter +{ +public: + win_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + fd_set_.fd_count = 0; + } + + void set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_.fd_count; ++i) + if (fd_set_.fd_array[i] == descriptor) + return; + if (fd_set_.fd_count < win_fd_set_size) + fd_set_.fd_array[fd_set_.fd_count++] = descriptor; + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast(reinterpret_cast(&fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast(&fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. + enum { win_fd_set_size = 1024 }; + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[win_fd_set_size]; + }; + + win_fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp b/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp new file mode 100644 index 000000000..e39fd33a6 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_iocp_io_service.hpp @@ -0,0 +1,389 @@ +// +// win_iocp_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/system_exception.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_operation.hpp" + +namespace asio { +namespace detail { + +class win_iocp_io_service + : public asio::io_service::service +{ +public: + // Base class for all operations. + typedef win_iocp_operation operation; + + // Constructor. + win_iocp_io_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + iocp_(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0)), + outstanding_work_(0), + interrupted_(0), + shutdown_(0) + { + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + system_exception e("iocp", last_error); + boost::throw_exception(e); + } + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + ::InterlockedExchange(&shutdown_, 1); + + for (;;) + { + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::GetQueuedCompletionStatus(iocp_.handle, + &bytes_transferred, &completion_key, &overlapped, 0); + DWORD last_error = ::GetLastError(); + if (last_error == WAIT_TIMEOUT) + break; + if (overlapped) + static_cast(overlapped)->destroy(); + } + } + + // Register a socket with the IO completion port. + void register_socket(socket_type sock) + { + HANDLE sock_as_handle = reinterpret_cast(sock); + ::CreateIoCompletionPort(sock_as_handle, iocp_.handle, 0, 0); + } + + // Run the event loop until interrupted or no more work. + size_t run() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(true)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Run until interrupted or one operation is performed. + size_t run_one() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + return do_one(true); + } + + // Poll for operations without blocking. + size_t poll() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(false)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one() + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + return 0; + + call_stack::context ctx(this); + + return do_one(false); + } + + // Interrupt the event processing loop. + void interrupt() + { + if (::InterlockedExchange(&interrupted_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + system_exception e("pqcs", last_error); + boost::throw_exception(e); + } + } + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + ::InterlockedExchange(&interrupted_, 0); + } + + // Notify that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + interrupt(); + } + + // Request invocation of the given handler. + template + void dispatch(Handler handler) + { + if (call_stack::contains(this)) + asio_handler_invoke_helpers::invoke(handler, &handler); + else + post(handler); + } + + // Request invocation of the given handler and return immediately. + template + void post(Handler handler) + { + // If the service has been shut down we silently discard the handler. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return; + + // Allocate and construct an operation to wrap the handler. + typedef handler_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, *this, handler); + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, ptr.get())) + { + DWORD last_error = ::GetLastError(); + system_exception e("pqcs", last_error); + boost::throw_exception(e); + } + + // Operation has been successfully posted. + ptr.release(); + } + + // Request invocation of the given OVERLAPPED-derived operation. + void post_completion(win_iocp_operation* op, DWORD op_last_error, + DWORD bytes_transferred) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + bytes_transferred, op_last_error, op)) + { + DWORD last_error = ::GetLastError(); + system_exception e("pqcs", last_error); + boost::throw_exception(e); + } + } + +private: + // Dequeues at most one operation from the I/O completion port, and then + // executes it. Returns the number of operations that were dequeued (i.e. + // either 0 or 1). + size_t do_one(bool block) + { + for (;;) + { + // Get the next operation from the queue. + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, block ? INFINITE : 0); + DWORD last_error = ::GetLastError(); + + if (!ok && overlapped == 0) + return 0; + + if (overlapped) + { + // We may have been passed a last_error value in the completion_key. + if (last_error == 0) + { + last_error = completion_key; + } + + // Ensure that the io_service does not exit due to running out of work + // while we make the upcall. + auto_work work(*this); + + // Dispatch the operation. + operation* op = static_cast(overlapped); + op->do_completion(last_error, bytes_transferred); + + return 1; + } + else + { + // The interrupted_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&interrupted_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + system_exception e("pqcs", last_error); + boost::throw_exception(e); + } + + return 0; + } + } + } + } + + struct auto_work + { + auto_work(win_iocp_io_service& io_service) + : io_service_(io_service) + { + io_service_.work_started(); + } + + ~auto_work() + { + io_service_.work_finished(); + } + + private: + win_iocp_io_service& io_service_; + }; + + template + struct handler_operation + : public operation + { + handler_operation(win_iocp_io_service& io_service, + Handler handler) + : operation(&handler_operation::do_completion_impl, + &handler_operation::destroy_impl), + io_service_(io_service), + handler_(handler) + { + io_service_.work_started(); + } + + ~handler_operation() + { + io_service_.work_finished(); + } + + private: + // Prevent copying and assignment. + handler_operation(const handler_operation&); + void operator=(const handler_operation&); + + static void do_completion_impl(operation* op, DWORD, size_t) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + Handler handler_; + }; + + // The IO completion port used for queueing operations. + struct iocp_holder + { + HANDLE handle; + iocp_holder(HANDLE h) : handle(h) {} + ~iocp_holder() { ::CloseHandle(handle); } + } iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been interrupted. + long interrupted_; + + // Flag to indicate whether the service has been shut down. + long shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp b/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp new file mode 100644 index 000000000..005677207 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp @@ -0,0 +1,46 @@ +// +// win_iocp_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +// This service is only supported on Win32 (NT4 and later). +#if !defined(ASIO_DISABLE_IOCP) +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) + +// Define this to indicate that IOCP is supported on the target platform. +#define ASIO_HAS_IOCP 1 + +namespace asio { +namespace detail { + +class win_iocp_io_service; + +} // namespace detail +} // namespace asio + +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP diff --git a/library/include/libtorrent/asio/detail/win_iocp_operation.hpp b/library/include/libtorrent/asio/detail/win_iocp_operation.hpp new file mode 100644 index 000000000..79eb348b4 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,81 @@ +// +// win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +// Base class for all IOCP operations. A function pointer is used instead of +// virtual functions to avoid the associated overhead. +// +// This class inherits from OVERLAPPED so that we can downcast to get back to +// the win_iocp_operation pointer from the LPOVERLAPPED out parameter of +// GetQueuedCompletionStatus. +struct win_iocp_operation + : public OVERLAPPED +{ + typedef void (*invoke_func_type)(win_iocp_operation*, DWORD, size_t); + typedef void (*destroy_func_type)(win_iocp_operation*); + + win_iocp_operation(invoke_func_type invoke_func, + destroy_func_type destroy_func) + : invoke_func_(invoke_func), + destroy_func_(destroy_func) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + } + + void do_completion(DWORD last_error, size_t bytes_transferred) + { + invoke_func_(this, last_error, bytes_transferred); + } + + void destroy() + { + destroy_func_(this); + } + +protected: + // Prevent deletion through this type. + ~win_iocp_operation() + { + } + +private: + invoke_func_type invoke_func_; + destroy_func_type destroy_func_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp b/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 000000000..3e4cd4d64 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,2077 @@ +// +// win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_service + : public asio::io_service::service +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // Base class for all operations. + typedef win_iocp_operation operation; + + struct noop_deleter { void operator()(void*) {} }; + typedef boost::shared_ptr shared_cancel_token_type; + typedef boost::weak_ptr weak_cancel_token_type; + + // The native type of a socket. + class native_type + { + public: + native_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; + + // The implementation type of the socket. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + cancel_token_(), + protocol_(endpoint_type().protocol()), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_socket_service; + + // The native socket representation. + native_type socket_; + + enum + { + enable_connection_aborted = 1, // User wants connection_aborted errors. + user_set_linger = 2 // The user set the linger option. + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + shared_cancel_token_type cancel_token_; + + // The protocol associated with the socket. + protocol_type protocol_; + + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; + + // Pointers to adjacent socket implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + // The type of the reactor used for connect operations. + typedef detail::select_reactor reactor_type; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. + win_iocp_socket_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + iocp_service_(asio::use_service(io_service)), + reactor_(0), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + close(*impl, asio::ignore_error()); + impl = impl->next_; + } + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + if (impl.socket_ != invalid_socket) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (reactor) + reactor->close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)); + } + + socket_ops::close(impl.socket_); + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + } + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + // Open a new socket implementation. + template + void open(implementation_type& impl, const protocol_type& protocol, + Error_Handler error_handler) + { + close(impl, asio::ignore_error()); + + socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(), + protocol.protocol())); + if (sock.get() == invalid_socket) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + iocp_service_.register_socket(sock.get()); + + impl.socket_ = sock.release(); + impl.cancel_token_.reset(static_cast(0), noop_deleter()); + impl.protocol_ = protocol; + + error_handler(asio::error(0)); + } + + // Assign a native socket to a socket implementation. + template + void assign(implementation_type& impl, const protocol_type& protocol, + const native_type& native_socket, Error_Handler error_handler) + { + close(impl, asio::ignore_error()); + + iocp_service_.register_socket(native_socket); + + impl.socket_ = native_socket; + impl.cancel_token_.reset(static_cast(0), noop_deleter()); + impl.protocol_ = protocol; + + error_handler(asio::error(0)); + } + + // Destroy a socket implementation. + template + void close(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ != invalid_socket) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (reactor) + reactor->close_descriptor(impl.socket_); + + if (socket_ops::close(impl.socket_) == socket_error_retval) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + else + { + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + } + } + + error_handler(asio::error(0)); + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + error_handler(error); + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + error_handler(asio::error(0)); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + error_handler(asio::error(last_error)); + } + else + { + error_handler(asio::error(0)); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + error_handler(asio::error(asio::error::not_supported)); + } + } + + // Bind the socket to the specified local endpoint. + template + void bind(implementation_type& impl, const endpoint_type& endpoint, + Error_Handler error_handler) + { + if (socket_ops::bind(impl.socket_, endpoint.data(), + endpoint.size()) == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Place the socket into the state where it will listen for new connections. + template + void listen(implementation_type& impl, int backlog, + Error_Handler error_handler) + { + if (socket_ops::listen(impl.socket_, backlog) == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Set a socket option. + template + void set_option(implementation_type& impl, const Option& option, + Error_Handler error_handler) + { + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + error_handler(asio::error(asio::error::invalid_argument)); + } + else + { + if (*reinterpret_cast(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + error_handler(asio::error(0)); + } + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + + if (socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_))) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + } + + // Set a socket option. + template + void get_option(const implementation_type& impl, Option& option, + Error_Handler error_handler) const + { + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + error_handler(asio::error(asio::error::invalid_argument)); + } + else + { + int* target = reinterpret_cast(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + error_handler(asio::error(0)); + } + } + else + { + size_t size = option.size(impl.protocol_); + if (socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size)) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + } + + // Perform an IO control command on the socket. + template + void io_control(implementation_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + if (socket_ops::ioctl(impl.socket_, command.name(), + static_cast(command.data()))) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Get the local endpoint. + template + void get_local_endpoint(const implementation_type& impl, + endpoint_type& endpoint, Error_Handler error_handler) const + { + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len)) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + endpoint.resize(addr_len); + error_handler(asio::error(0)); + } + + // Get the remote endpoint. + template + void get_remote_endpoint(const implementation_type& impl, + endpoint_type& endpoint, Error_Handler error_handler) const + { + if (impl.socket_.have_remote_endpoint()) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len) == socket_error_retval) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + if (connect_time == 0xFFFFFFFF) + { + error_handler(asio::error(asio::error::not_connected)); + return; + } + + endpoint = impl.socket_.remote_endpoint(); + error_handler(asio::error(0)); + } + else + { + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len)) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + + endpoint.resize(addr_len); + error_handler(asio::error(0)); + } + } + + /// Disable sends or receives on the socket. + template + void shutdown(implementation_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + if (socket_ops::shutdown(impl.socket_, what) != 0) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + // Send the given data to the peer. Returns the number of bytes sent. + template + size_t send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(asio::error(0)); + return 0; + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, bufs, + i, &bytes_transferred, flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + error_handler(asio::error(last_error)); + return 0; + } + + error_handler(asio::error(0)); + return bytes_transferred; + } + + template + class send_operation + : public operation + { + public: + send_operation(asio::io_service& io_service, + weak_cancel_token_type cancel_token, + const Const_Buffers& buffers, Handler handler) + : operation( + &send_operation::do_completion_impl, + &send_operation::destroy_impl), + work_(io_service), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef send_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Map ERROR_NETNAME_DELETED to more useful error. + if (last_error == ERROR_NETNAME_DELETED) + { + if (handler_op->cancel_token_.expired()) + last_error = ERROR_OPERATION_ABORTED; + else + last_error = WSAECONNRESET; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef send_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + weak_cancel_token_type cancel_token_; + Const_Buffers buffers_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef send_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + io_service(), impl.cancel_token_, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ptr.reset(); + asio::error error(asio::error::success); + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, bufs, i, + &bytes_transferred, flags, ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, + flags, destination.data(), destination.size(), 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + error_handler(asio::error(last_error)); + return 0; + } + + error_handler(asio::error(0)); + return bytes_transferred; + } + + template + class send_to_operation + : public operation + { + public: + send_to_operation(asio::io_service& io_service, + const Const_Buffers& buffers, Handler handler) + : operation( + &send_to_operation::do_completion_impl, + &send_to_operation::destroy_impl), + work_(io_service), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef send_to_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef send_to_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + Const_Buffers buffers_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, const Const_Buffers& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef send_to_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, io_service(), buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Const_Buffers::const_iterator iter = buffers.begin(); + typename Const_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, + flags, destination.data(), destination.size(), ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + error_handler(asio::error(0)); + return 0; + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, bufs, i, + &bytes_transferred, &recv_flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + error_handler(asio::error(last_error)); + return 0; + } + if (bytes_transferred == 0) + { + error_handler(asio::error(asio::error::eof)); + return 0; + } + + error_handler(asio::error(0)); + return bytes_transferred; + } + + template + class receive_operation + : public operation + { + public: + receive_operation(asio::io_service& io_service, + weak_cancel_token_type cancel_token, + const Mutable_Buffers& buffers, Handler handler) + : operation( + &receive_operation::do_completion_impl, + &receive_operation::destroy_impl), + work_(io_service), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef receive_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Map ERROR_NETNAME_DELETED to more useful error. + if (last_error == ERROR_NETNAME_DELETED) + { + if (handler_op->cancel_token_.expired()) + last_error = ERROR_OPERATION_ABORTED; + else + last_error = WSAECONNRESET; + } + + // Check for connection closed. + else if (last_error == 0 && bytes_transferred == 0) + { + last_error = asio::error::eof; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef receive_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + weak_cancel_token_type cancel_token_; + Mutable_Buffers buffers_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef receive_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + io_service(), impl.cancel_token_, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ptr.reset(); + asio::error error(asio::error::success); + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, bufs, i, + &bytes_transferred, &recv_flags, ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, const Mutable_Buffers& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + Error_Handler error_handler) + { + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int endpoint_size = sender_endpoint.capacity(); + int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, + &recv_flags, sender_endpoint.data(), &endpoint_size, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + error_handler(asio::error(last_error)); + return 0; + } + if (bytes_transferred == 0) + { + error_handler(asio::error(asio::error::eof)); + return 0; + } + + sender_endpoint.resize(endpoint_size); + + error_handler(asio::error(0)); + return bytes_transferred; + } + + template + class receive_from_operation + : public operation + { + public: + receive_from_operation(asio::io_service& io_service, + endpoint_type& endpoint, const Mutable_Buffers& buffers, + Handler handler) + : operation( + &receive_from_operation::do_completion_impl, + &receive_from_operation::destroy_impl), + endpoint_(endpoint), + endpoint_size_(endpoint.capacity()), + work_(io_service), + buffers_(buffers), + handler_(handler) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef receive_from_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Check for connection closed. + if (last_error == 0 && bytes_transferred == 0) + { + last_error = asio::error::eof; + } + + // Record the size of the endpoint returned by the operation. + handler_op->endpoint_.resize(handler_op->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef receive_from_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + endpoint_type& endpoint_; + int endpoint_size_; + asio::io_service::work work_; + Mutable_Buffers buffers_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const Mutable_Buffers& buffers, endpoint_type& sender_endp, + socket_base::message_flags flags, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef receive_from_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + io_service(), sender_endp, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename Mutable_Buffers::const_iterator iter = buffers.begin(); + typename Mutable_Buffers::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, + &recv_flags, sender_endp.data(), &ptr.get()->endpoint_size(), + ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Accept a new connection. + template + void accept(implementation_type& impl, Socket& peer, + Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.native() != invalid_socket) + { + error_handler(asio::error(asio::error::already_connected)); + return; + } + + for (;;) + { + socket_holder new_socket(socket_ops::accept(impl.socket_, 0, 0)); + if (int err = socket_ops::get_error()) + { + if (err == asio::error::connection_aborted + && !(impl.flags_ & implementation_type::enable_connection_aborted)) + { + // Retry accept operation. + continue; + } + else + { + error_handler(asio::error(err)); + return; + } + } + + asio::error temp_error; + peer.assign(impl.protocol_, new_socket.get(), + asio::assign_error(temp_error)); + if (temp_error) + { + error_handler(temp_error); + return; + } + else + { + new_socket.release(); + error_handler(asio::error(0)); + return; + } + } + } + + // Accept a new connection. + template + void accept_endpoint(implementation_type& impl, Socket& peer, + endpoint_type& peer_endpoint, Error_Handler error_handler) + { + // We cannot accept a socket that is already open. + if (peer.native() != invalid_socket) + { + error_handler(asio::error(asio::error::already_connected)); + return; + } + + for (;;) + { + socket_addr_len_type addr_len = peer_endpoint.capacity(); + socket_holder new_socket(socket_ops::accept( + impl.socket_, peer_endpoint.data(), &addr_len)); + if (int err = socket_ops::get_error()) + { + if (err == asio::error::connection_aborted + && !(impl.flags_ & implementation_type::enable_connection_aborted)) + { + // Retry accept operation. + continue; + } + else + { + error_handler(asio::error(err)); + return; + } + } + + peer_endpoint.resize(addr_len); + + asio::error temp_error; + peer.assign(impl.protocol_, new_socket.get(), + asio::assign_error(temp_error)); + if (temp_error) + { + error_handler(temp_error); + return; + } + else + { + new_socket.release(); + error_handler(asio::error(0)); + return; + } + } + } + + template + class accept_operation + : public operation + { + public: + accept_operation(win_iocp_io_service& io_service, socket_type socket, + socket_type new_socket, Socket& peer, const protocol_type& protocol, + bool enable_connection_aborted, Handler handler) + : operation( + &accept_operation::do_completion_impl, + &accept_operation::destroy_impl), + io_service_(io_service), + socket_(socket), + new_socket_(new_socket), + peer_(peer), + protocol_(protocol), + work_(io_service.io_service()), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + socket_type new_socket() + { + return new_socket_.get(); + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef accept_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. + if (last_error == ERROR_NETNAME_DELETED) + { + last_error = asio::error::connection_aborted; + } + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (last_error == asio::error::connection_aborted + && !ptr.get()->enable_connection_aborted_) + { + // Reset OVERLAPPED structure. + ptr.get()->Internal = 0; + ptr.get()->InternalHigh = 0; + ptr.get()->Offset = 0; + ptr.get()->OffsetHigh = 0; + ptr.get()->hEvent = 0; + + // Create a new socket for the next connection, since the AcceptEx call + // fails with WSAEINVAL if we try to reuse the same socket. + ptr.get()->new_socket_.reset(); + ptr.get()->new_socket_.reset( + socket_ops::socket(ptr.get()->protocol_.family(), + ptr.get()->protocol_.type(), ptr.get()->protocol_.protocol())); + last_error = socket_ops::get_error(); + if (ptr.get()->new_socket() != invalid_socket) + { + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (last_error == ERROR_NETNAME_DELETED + || last_error == asio::error::connection_aborted) + { + // Post this handler so that operation will be restarted again. + ptr.get()->io_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + return; + } + else + { + // Operation already complete. Continue with rest of this handler. + } + } + else + { + // Asynchronous operation has been successfully restarted. + ptr.release(); + return; + } + } + } + + // Get the address of the peer. + endpoint_type peer_endpoint; + if (last_error == 0) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(handler_op->output_buffer(), 0, + handler_op->address_length(), handler_op->address_length(), + &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); + if (remote_addr_length > peer_endpoint.capacity()) + { + last_error = asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); + peer_endpoint.resize(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + if (last_error == 0) + { + SOCKET update_ctx_param = handler_op->socket_; + if (socket_ops::setsockopt(handler_op->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET)) != 0) + { + last_error = socket_ops::get_error(); + } + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (last_error == 0) + { + asio::error temp_error; + handler_op->peer_.assign(handler_op->protocol_, + native_type(handler_op->new_socket_.get(), peer_endpoint), + asio::assign_error(temp_error)); + if (temp_error) + last_error = temp_error.code(); + else + handler_op->new_socket_.release(); + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef accept_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + protocol_type protocol_; + asio::io_service::work work_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer object must be valid until the + // accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Check whether acceptor has been initialised. + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error)); + return; + } + + // Check that peer socket has not already been connected. + if (peer.native() != invalid_socket) + { + asio::error error(asio::error::already_connected); + io_service().post(bind_handler(handler, error)); + return; + } + + // Create a new socket for the connection. + socket_holder sock(socket_ops::socket(impl.protocol_.family(), + impl.protocol_.type(), impl.protocol_.protocol())); + if (sock.get() == invalid_socket) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef accept_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + socket_type new_socket = sock.get(); + bool enable_connection_aborted = + (impl.flags_ & implementation_type::enable_connection_aborted); + handler_ptr ptr(raw_ptr, + iocp_service_, impl.socket_, new_socket, peer, impl.protocol_, + enable_connection_aborted, handler); + sock.release(); + + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (!enable_connection_aborted + && (last_error == ERROR_NETNAME_DELETED + || last_error == asio::error::connection_aborted)) + { + // Post handler so that operation will be restarted again. We do not + // perform the AcceptEx again here to avoid the possibility of starving + // other handlers. + iocp_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + } + else + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error)); + } + } + else + { + ptr.release(); + } + } + + template + class accept_endp_operation + : public operation + { + public: + accept_endp_operation(win_iocp_io_service& io_service, + socket_type socket, socket_type new_socket, Socket& peer, + const protocol_type& protocol, endpoint_type& peer_endpoint, + bool enable_connection_aborted, Handler handler) + : operation( + &accept_endp_operation::do_completion_impl, + &accept_endp_operation::destroy_impl), + io_service_(io_service), + socket_(socket), + new_socket_(new_socket), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + work_(io_service.io_service()), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + socket_type new_socket() + { + return new_socket_.get(); + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef accept_endp_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. + if (last_error == ERROR_NETNAME_DELETED) + { + last_error = asio::error::connection_aborted; + } + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (last_error == asio::error::connection_aborted + && !ptr.get()->enable_connection_aborted_) + { + // Reset OVERLAPPED structure. + ptr.get()->Internal = 0; + ptr.get()->InternalHigh = 0; + ptr.get()->Offset = 0; + ptr.get()->OffsetHigh = 0; + ptr.get()->hEvent = 0; + + // Create a new socket for the next connection, since the AcceptEx call + // fails with WSAEINVAL if we try to reuse the same socket. + ptr.get()->new_socket_.reset(); + ptr.get()->new_socket_.reset( + socket_ops::socket(ptr.get()->protocol_.family(), + ptr.get()->protocol_.type(), ptr.get()->protocol_.protocol())); + last_error = socket_ops::get_error(); + if (ptr.get()->new_socket() != invalid_socket) + { + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (last_error == ERROR_NETNAME_DELETED + || last_error == asio::error::connection_aborted) + { + // Post this handler so that operation will be restarted again. + ptr.get()->io_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + return; + } + else + { + // Operation already complete. Continue with rest of this handler. + } + } + else + { + // Asynchronous operation has been successfully restarted. + ptr.release(); + return; + } + } + } + + // Get the address of the peer. + if (last_error == 0) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(handler_op->output_buffer(), 0, + handler_op->address_length(), handler_op->address_length(), + &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); + if (remote_addr_length > handler_op->peer_endpoint_.capacity()) + { + last_error = asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(handler_op->peer_endpoint_.data(), + remote_addr, remote_addr_length); + handler_op->peer_endpoint_.resize(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + if (last_error == 0) + { + SOCKET update_ctx_param = handler_op->socket_; + if (socket_ops::setsockopt(handler_op->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET)) != 0) + { + last_error = socket_ops::get_error(); + } + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (last_error == 0) + { + asio::error temp_error; + handler_op->peer_.assign(handler_op->peer_endpoint_.protocol(), + native_type(handler_op->new_socket_.get(), + handler_op->peer_endpoint_), + asio::assign_error(temp_error)); + if (temp_error) + last_error = temp_error.code(); + else + handler_op->new_socket_.release(); + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error error(last_error); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, error), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef accept_endp_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + protocol_type protocol_; + endpoint_type& peer_endpoint_; + asio::io_service::work work_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept_endpoint(implementation_type& impl, Socket& peer, + endpoint_type& peer_endpoint, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Check whether acceptor has been initialised. + if (impl.socket_ == invalid_socket) + { + asio::error error(asio::error::bad_descriptor); + io_service().post(bind_handler(handler, error)); + return; + } + + // Check that peer socket has not already been connected. + if (peer.native() != invalid_socket) + { + asio::error error(asio::error::already_connected); + io_service().post(bind_handler(handler, error)); + return; + } + + // Create a new socket for the connection. + socket_holder sock(socket_ops::socket(impl.protocol_.family(), + impl.protocol_.type(), impl.protocol_.protocol())); + if (sock.get() == invalid_socket) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef accept_endp_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + socket_type new_socket = sock.get(); + bool enable_connection_aborted = + (impl.flags_ & implementation_type::enable_connection_aborted); + handler_ptr ptr(raw_ptr, + iocp_service_, impl.socket_, new_socket, peer, impl.protocol_, + peer_endpoint, enable_connection_aborted, handler); + sock.release(); + + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (!enable_connection_aborted + && (last_error == ERROR_NETNAME_DELETED + || last_error == asio::error::connection_aborted)) + { + // Post handler so that operation will be restarted again. We do not + // perform the AcceptEx again here to avoid the possibility of starving + // other handlers. + iocp_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + } + else + { + ptr.reset(); + asio::error error(last_error); + iocp_service_.post(bind_handler(handler, error)); + } + } + else + { + ptr.release(); + } + } + + // Connect the socket to the specified endpoint. + template + void connect(implementation_type& impl, const endpoint_type& peer_endpoint, + Error_Handler error_handler) + { + // Open the socket if it is not already open. + if (impl.socket_ == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl.socket_ = socket_ops::socket(family, type, proto); + if (impl.socket_ == invalid_socket) + { + error_handler(asio::error(socket_ops::get_error())); + return; + } + iocp_service_.register_socket(impl.socket_); + } + + // Perform the connect operation. + int result = socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size()); + if (result == socket_error_retval) + error_handler(asio::error(socket_ops::get_error())); + else + error_handler(asio::error(0)); + } + + template + class connect_handler + { + public: + connect_handler(socket_type socket, + boost::shared_ptr completed, + asio::io_service& io_service, + reactor_type& reactor, Handler handler) + : socket_(socket), + completed_(completed), + io_service_(io_service), + reactor_(reactor), + work_(io_service), + handler_(handler) + { + } + + bool operator()(int result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return true; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(socket_); + + // Check whether the operation was successful. + if (result != 0) + { + asio::error error(result); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len) == socket_error_retval) + { + asio::error error(socket_ops::get_error()); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + asio::error error(connect_error); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Make the socket blocking again (the default). + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + // Post the result of the successful connection operation. + asio::error error(asio::error::success); + io_service_.post(bind_handler(handler_, error)); + return true; + } + + private: + socket_type socket_; + boost::shared_ptr completed_; + asio::io_service& io_service_; + reactor_type& reactor_; + asio::io_service::work work_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service(io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + // Open the socket if it is not already open. + if (impl.socket_ == invalid_socket) + { + // Get the flags used to create the new socket. + int family = peer_endpoint.protocol().family(); + int type = peer_endpoint.protocol().type(); + int proto = peer_endpoint.protocol().protocol(); + + // Create a new socket. + impl.socket_ = socket_ops::socket(family, type, proto); + if (impl.socket_ == invalid_socket) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + iocp_service_.register_socket(impl.socket_); + } + + // Mark the socket as non-blocking so that the connection will take place + // asynchronously. + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking)) + { + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + return; + } + + // Start the connect operation. + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size()) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + asio::error error(asio::error::success); + io_service().post(bind_handler(handler, error)); + } + else if (socket_ops::get_error() == asio::error::in_progress + || socket_ops::get_error() == asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor->start_write_and_except_ops(impl.socket_, + connect_handler( + impl.socket_, completed, io_service(), *reactor, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + asio::error error(socket_ops::get_error()); + io_service().post(bind_handler(handler, error)); + } + } + +private: + // Helper function to provide InterlockedCompareExchangePointer functionality + // on very old Platform SDKs. + void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp) + { +#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86) + return reinterpret_cast(InterlockedCompareExchange( + reinterpret_cast(dest), reinterpret_cast(exch), + reinterpret_cast(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif + } + + // Helper function to provide InterlockedExchangePointer functionality on very + // old Platform SDKs. + void* interlocked_exchange_pointer(void** dest, void* val) + { +#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86) + return reinterpret_cast(InterlockedExchange( + reinterpret_cast(dest), reinterpret_cast(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif + } + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_service& iocp_service_; + + // The reactor used for performing connect operations. This object is created + // only if needed. + reactor_type* reactor_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp b/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp new file mode 100644 index 000000000..c909e1af3 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// win_local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class win_local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit win_local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~win_local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/library/include/libtorrent/asio/detail/win_mutex.hpp b/library/include/libtorrent/asio/detail/win_mutex.hpp new file mode 100644 index 000000000..9f3dd85c9 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_mutex.hpp @@ -0,0 +1,142 @@ +// +// win_mutex.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_MUTEX_HPP +#define ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + win_mutex() + { + int error = do_init(); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + int error = do_lock(); + if (error != 0) + { + system_exception e("mutex", error); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + int do_init() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::InitializeCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::InitializeCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif + } + + // Locking must be performed in a separate function to lock() since the + // compiler does not support the use of structured exceptions and C++ + // exceptions in the same function. + int do_lock() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::EnterCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::EnterCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_INVALID_HANDLE + || GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + if (GetExceptionCode() == STATUS_NO_MEMORY) + return ERROR_OUTOFMEMORY; + return ERROR_INVALID_HANDLE; + } + + return 0; +#endif + } + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/library/include/libtorrent/asio/detail/win_signal_blocker.hpp b/library/include/libtorrent/asio/detail/win_signal_blocker.hpp new file mode 100644 index 000000000..a1c2e992b --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_signal_blocker.hpp @@ -0,0 +1,67 @@ +// +// win_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class win_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + win_signal_blocker() + { + // No-op. + } + + // Destructor restores the previous signal mask. + ~win_signal_blocker() + { + // No-op. + } + + // Block all signals for the calling thread. + void block() + { + // No-op. + } + + // Restore the previous signal mask. + void unblock() + { + // No-op. + } +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP diff --git a/library/include/libtorrent/asio/detail/win_thread.hpp b/library/include/libtorrent/asio/detail/win_thread.hpp new file mode 100644 index 000000000..4e6adb880 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_thread.hpp @@ -0,0 +1,121 @@ +// +// win_thread.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_THREAD_HPP +#define ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +unsigned int __stdcall win_thread_function(void* arg); + +class win_thread + : private noncopyable +{ +public: + // Constructor. + template + win_thread(Function f) + { + std::auto_ptr arg(new func(f)); + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, 0, + win_thread_function, arg.get(), 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + system_exception e("thread", last_error); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~win_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + +private: + friend unsigned int __stdcall win_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline unsigned int __stdcall win_thread_function(void* arg) +{ + std::auto_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/library/include/libtorrent/asio/detail/win_tss_ptr.hpp b/library/include/libtorrent/asio/detail/win_tss_ptr.hpp new file mode 100644 index 000000000..5381094c5 --- /dev/null +++ b/library/include/libtorrent/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,85 @@ +// +// win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WIN_TSS_PTR_HPP +#define ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template +class win_tss_ptr + : private noncopyable +{ +public: + // Constructor. + win_tss_ptr() + { + tss_key_ = ::TlsAlloc(); + if (tss_key_ == TLS_OUT_OF_INDEXES) + { + DWORD last_error = ::GetLastError(); + system_exception e("tss", last_error); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/library/include/libtorrent/asio/detail/winsock_init.hpp b/library/include/libtorrent/asio/detail/winsock_init.hpp new file mode 100644 index 000000000..7ae66d0e5 --- /dev/null +++ b/library/include/libtorrent/asio/detail/winsock_init.hpp @@ -0,0 +1,116 @@ +// +// winsock_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WINSOCK_INIT_HPP +#define ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_exception.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template +class winsock_init + : private noncopyable +{ +private: + // Structure to perform the actual initialisation. + struct do_init + { + do_init() + { + WSADATA wsa_data; + result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data); + } + + ~do_init() + { + ::WSACleanup(); + } + + int result() const + { + return result_; + } + + // Helper function to manage a do_init singleton. The static instance of the + // winsock_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + int result_; + }; + +public: + // Constructor. + winsock_init() + : ref_(do_init::instance()) + { + // Check whether winsock was successfully initialised. This check is not + // performed for the global instance since there will be nobody around to + // catch the exception. + if (this != &instance_ && ref_->result() != 0) + { + system_exception e("winsock", ref_->result()); + boost::throw_exception(e); + } + } + + // Destructor. + ~winsock_init() + { + } + +private: + // Instance to force initialisation of winsock at global scope. + static winsock_init instance_; + + // Reference to singleton do_init object to ensure that winsock does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +winsock_init winsock_init::instance_; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/library/include/libtorrent/asio/detail/wrapped_handler.hpp b/library/include/libtorrent/asio/detail/wrapped_handler.hpp new file mode 100644 index 000000000..e77b38dd8 --- /dev/null +++ b/library/include/libtorrent/asio/detail/wrapped_handler.hpp @@ -0,0 +1,187 @@ +// +// wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler(Dispatcher& dispatcher, Handler handler) + : dispatcher_(dispatcher), + handler_(handler) + { + } + + void operator()() + { + dispatcher_.dispatch(handler_); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template + void operator()(const Arg1& arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +//private: + Dispatcher& dispatcher_; + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + wrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +class rewrapped_handler +{ +public: + explicit rewrapped_handler(const Handler& handler, const Context& context) + : handler_(handler), + context_(context) + { + } + + void operator()() + { + handler_(); + } + + void operator()() const + { + handler_(); + } + +//private: + Handler handler_; + Context context_; +}; + +template +inline void asio_handler_invoke(const Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +} + +template +inline void asio_handler_invoke(const Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->context_); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP diff --git a/library/include/libtorrent/asio/error.hpp b/library/include/libtorrent/asio/error.hpp new file mode 100644 index 000000000..c52df6cd8 --- /dev/null +++ b/library/include/libtorrent/asio/error.hpp @@ -0,0 +1,387 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_ERROR_HPP +#define ASIO_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_local_free_on_block_exit.hpp" + +namespace asio { + +#if defined(GENERATING_DOCUMENTATION) +/// INTERNAL ONLY. +# define ASIO_SOCKET_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_NETDB_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_GETADDRINFO_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_OS_ERROR(e_win, e_posix) implementation_defined +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# define ASIO_SOCKET_ERROR(e) WSA ## e +# define ASIO_NETDB_ERROR(e) WSA ## e +# define ASIO_GETADDRINFO_ERROR(e) e +# define ASIO_OS_ERROR(e_win, e_posix) e_win +#else +# define ASIO_SOCKET_ERROR(e) e +# define ASIO_NETDB_ERROR(e) 16384 + e +# define ASIO_GETADDRINFO_ERROR(e) 32768 + e +# define ASIO_OS_ERROR(e_win, e_posix) e_posix +#endif + +/// The error class is used to encapsulate system error codes. +class error + : public std::exception +{ +public: + /// Error codes. + enum code_type + { + /// Permission denied. + access_denied = ASIO_SOCKET_ERROR(EACCES), + + /// Address family not supported by protocol. + address_family_not_supported = ASIO_SOCKET_ERROR(EAFNOSUPPORT), + + /// Address already in use. + address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE), + + /// Transport endpoint is already connected. + already_connected = ASIO_SOCKET_ERROR(EISCONN), + + /// Operation already in progress. + already_started = ASIO_SOCKET_ERROR(EALREADY), + + /// A connection has been aborted. + connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED), + + /// Connection refused. + connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED), + + /// Connection reset by peer. + connection_reset = ASIO_SOCKET_ERROR(ECONNRESET), + + /// Bad file descriptor. + bad_descriptor = ASIO_SOCKET_ERROR(EBADF), + + /// End of file or stream. + eof = ASIO_OS_ERROR(ERROR_HANDLE_EOF, -1), + + /// Bad address. + fault = ASIO_SOCKET_ERROR(EFAULT), + + /// Host not found (authoritative). + host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND), + + /// Host not found (non-authoritative). + host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN), + + /// No route to host. + host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH), + + /// Operation now in progress. + in_progress = ASIO_SOCKET_ERROR(EINPROGRESS), + + /// Interrupted system call. + interrupted = ASIO_SOCKET_ERROR(EINTR), + + /// Invalid argument. + invalid_argument = ASIO_SOCKET_ERROR(EINVAL), + + /// Message too long. + message_size = ASIO_SOCKET_ERROR(EMSGSIZE), + + /// Network is down. + network_down = ASIO_SOCKET_ERROR(ENETDOWN), + + /// Network dropped connection on reset. + network_reset = ASIO_SOCKET_ERROR(ENETRESET), + + /// Network is unreachable. + network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH), + + /// Too many open files. + no_descriptors = ASIO_SOCKET_ERROR(EMFILE), + + /// No buffer space available. + no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS), + + /// The query is valid but does not have associated address data. + no_data = ASIO_NETDB_ERROR(NO_DATA), + + /// Cannot allocate memory. + no_memory = ASIO_OS_ERROR(ERROR_OUTOFMEMORY, ENOMEM), + + /// Operation not permitted. + no_permission = ASIO_OS_ERROR(ERROR_ACCESS_DENIED, EPERM), + + /// Protocol not available. + no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT), + + /// A non-recoverable error occurred. + no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY), + + /// Transport endpoint is not connected. + not_connected = ASIO_SOCKET_ERROR(ENOTCONN), + + /// Socket operation on non-socket. + not_socket = ASIO_SOCKET_ERROR(ENOTSOCK), + + /// Operation not supported. + not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP), + + /// Operation cancelled. + operation_aborted = ASIO_OS_ERROR(ERROR_OPERATION_ABORTED, ECANCELED), + + /// The service is not supported for the given socket type. + service_not_found = ASIO_OS_ERROR( + WSATYPE_NOT_FOUND, + ASIO_GETADDRINFO_ERROR(EAI_SERVICE)), + + /// The socket type is not supported. + socket_type_not_supported = ASIO_OS_ERROR( + WSAESOCKTNOSUPPORT, + ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)), + + /// Cannot send after transport endpoint shutdown. + shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN), + + /// Success. + success = 0, + + /// Connection timed out. + timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT), + + /// Resource temporarily unavailable. + try_again = ASIO_OS_ERROR(ERROR_RETRY, EAGAIN), + + /// The socket is marked non-blocking and the requested operation would + /// block. + would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK) + }; + + /// Default constructor. + error() + : code_(success) + { + } + + /// Construct with a specific error code. + error(int code) + : code_(code) + { + } + + /// Copy constructor. + error(const error& e) + : std::exception(e), + code_(e.code_) + { + } + + /// Destructor. + virtual ~error() throw () + { + } + + /// Assignment operator. + error& operator=(const error& e) + { + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + try + { + if (!what_) + { + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::win_local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + what_.reset(new std::string(msg)); + else + return "asio error"; + } + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#else // defined(BOOST_WINDOWS) + switch (code_) + { + case error::eof: + return "End of file."; + case error::host_not_found: + return "Host not found (authoritative)."; + case error::host_not_found_try_again: + return "Host not found (non-authoritative), try again later."; + case error::no_recovery: + return "A non-recoverable error occurred during database lookup."; + case error::no_data: + return "The query is valid, but it does not have associated data."; +#if !defined(__sun) + case error::operation_aborted: + return "Operation aborted."; +#endif // !defined(__sun) + case error::service_not_found: + return "Service not found."; + case error::socket_type_not_supported: + return "Socket type not supported."; + default: +#if defined(__sun) || defined(__QNX__) + return strerror(code_); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + try + { + char buf[256] = ""; + strerror_r(code_, buf, sizeof(buf)); + what_.reset(new std::string(buf)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#else + try + { + char buf[256] = ""; + what_.reset(new std::string(strerror_r(code_, buf, sizeof(buf)))); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio error"; + } +#endif + } +#endif // defined(BOOST_WINDOWS) + } + + /// Get the code associated with the error. + int code() const + { + return code_; + } + + struct unspecified_bool_type_t + { + }; + + typedef unspecified_bool_type_t* unspecified_bool_type; + + /// Operator returns non-null if there is a non-success error code. + operator unspecified_bool_type() const + { + if (code_ == success) + return 0; + else + return reinterpret_cast(1); + } + + /// Operator to test if the error represents success. + bool operator!() const + { + return code_ == success; + } + + /// Equality operator to compare two error objects. + friend bool operator==(const error& e1, const error& e2) + { + return e1.code_ == e2.code_; + } + + /// Inequality operator to compare two error objects. + friend bool operator!=(const error& e1, const error& e2) + { + return e1.code_ != e2.code_; + } + +private: + // The code associated with the error. + int code_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +/// Output the string associated with an error. +/** + * Used to output a human-readable string that is associated with an error. + * + * @param os The output stream to which the string will be written. + * + * @param e The error to be written. + * + * @return The output stream. + * + * @relates asio::error + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const error& e) +{ + os << e.what(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const error& e) +{ + os << e.what(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace asio + +#undef ASIO_SOCKET_ERROR +#undef ASIO_NETDB_ERROR +#undef ASIO_GETADDRINFO_ERROR +#undef ASIO_OS_ERROR + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_HPP diff --git a/library/include/libtorrent/asio/error_handler.hpp b/library/include/libtorrent/asio/error_handler.hpp new file mode 100644 index 000000000..c315c8d5e --- /dev/null +++ b/library/include/libtorrent/asio/error_handler.hpp @@ -0,0 +1,120 @@ +// +// error_handler.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_ERROR_HANDLER_HPP +#define ASIO_ERROR_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +namespace detail { + +class ignore_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error&) const + { + } +}; + +class throw_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error& err) const + { + if (err) + boost::throw_exception(err); + } +}; + +template +class assign_error_t +{ +public: + typedef void result_type; + + assign_error_t(Target& target) + : target_(&target) + { + } + + template + void operator()(const Error& err) const + { + *target_ = err; + } + +private: + Target* target_; +}; + +} // namespace detail + +/** + * @defgroup error_handler Error Handler Function Objects + * + * Function objects for custom error handling. + */ +/*@{*/ + +/// Return a function object that always ignores the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified ignore_error(); +#else +inline detail::ignore_error_t ignore_error() +{ + return detail::ignore_error_t(); +} +#endif + +/// Return a function object that always throws the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified throw_error(); +#else +inline detail::throw_error_t throw_error() +{ + return detail::throw_error_t(); +} +#endif + +/// Return a function object that assigns the error to a variable. +#if defined(GENERATING_DOCUMENTATION) +template +unspecified assign_error(Target& target); +#else +template +inline detail::assign_error_t assign_error(Target& target) +{ + return detail::assign_error_t(target); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_HANDLER_HPP diff --git a/library/include/libtorrent/asio/handler_alloc_hook.hpp b/library/include/libtorrent/asio/handler_alloc_hook.hpp new file mode 100644 index 000000000..d15c85061 --- /dev/null +++ b/library/include/libtorrent/asio/handler_alloc_hook.hpp @@ -0,0 +1,88 @@ +// +// handler_alloc_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_HANDLER_ALLOC_HOOK_HPP +#define ASIO_HANDLER_ALLOC_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +/// Default allocation function for handlers. +/** + * Asynchronous operations may need to allocate temporary objects. Since + * asynchronous operations have a handler function object, these temporary + * objects can be said to be associated with the handler. + * + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for these temporary objects. + * + * This default implementation is simply: + * @code + * return ::operator new(bytes); + * @endcode + * + * @note All temporary objects associated with a handler will be deallocated + * before the upcall to the handler is performed. This allows the same memory to + * be reused for a subsequent asynchronous operation initiated by the handler. + * + * @par Example: + * @code + * class my_handler; + * + * void* asio_handler_allocate(std::size_t size, my_handler* context) + * { + * return ::operator new(size); + * } + * + * void asio_handler_deallocate(void* pointer, std::size_t size, + * my_handler* context) + * { + * ::operator delete(pointer); + * } + * @endcode + */ +inline void* asio_handler_allocate(std::size_t size, ...) +{ + return ::operator new(size); +} + +/// Default deallocation function for handlers. +/** + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for the associated temporary objects. + * + * This default implementation is simply: + * @code + * ::operator delete(pointer); + * @endcode + * + * @sa asio_handler_allocate. + */ +inline void asio_handler_deallocate(void* pointer, std::size_t size, ...) +{ + (void)(size); + return ::operator delete(pointer); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_ALLOC_HOOK_HPP diff --git a/library/include/libtorrent/asio/handler_invoke_hook.hpp b/library/include/libtorrent/asio/handler_invoke_hook.hpp new file mode 100644 index 000000000..124b76e20 --- /dev/null +++ b/library/include/libtorrent/asio/handler_invoke_hook.hpp @@ -0,0 +1,69 @@ +// +// handler_invoke_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_HANDLER_INVOKE_HOOK_HPP +#define ASIO_HANDLER_INVOKE_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default invoke function for handlers. +/** + * Completion handlers for asynchronous operations are invoked by the + * io_service associated with the corresponding object (e.g. a socket or + * deadline_timer). Certain guarantees are made on when the handler may be + * invoked, in particular that a handler can only be invoked from a thread that + * is currently calling asio::io_service::run() on the corresponding + * io_service object. Handlers may subsequently be invoked through other + * objects (such as asio::strand objects) that provide additional + * guarantees. + * + * When asynchronous operations are composed from other asynchronous + * operations, all intermediate handlers should be invoked using the same + * method as the final handler. This is required to ensure that user-defined + * objects are not accessed in a way that may violate the guarantees. This + * hooking function ensures that the invoked method used for the final handler + * is accessible at each intermediate step. + * + * Implement asio_handler_invoke for your own handlers to specify a custom + * invocation strategy. + * + * This default implementation is simply: + * @code + * function(); + * @endcode + * + * @par Example: + * @code + * class my_handler; + * + * template + * void asio_handler_invoke(Function function, my_handler* context) + * { + * context->strand_.dispatch(function); + * } + * @endcode + */ +template +inline void asio_handler_invoke(Function function, ...) +{ + function(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_INVOKE_HOOK_HPP diff --git a/library/include/libtorrent/asio/impl/io_service.ipp b/library/include/libtorrent/asio/impl/io_service.ipp new file mode 100644 index 000000000..5e017831b --- /dev/null +++ b/library/include/libtorrent/asio/impl/io_service.ipp @@ -0,0 +1,150 @@ +// +// io_service.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IO_SERVICE_IPP +#define ASIO_IO_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { + +inline io_service::io_service() + : service_registry_(*this), + impl_(service_registry_.use_service()) +{ +} + +inline size_t io_service::run() +{ + return impl_.run(); +} + +inline size_t io_service::run_one() +{ + return impl_.run_one(); +} + +inline size_t io_service::poll() +{ + return impl_.poll(); +} + +inline size_t io_service::poll_one() +{ + return impl_.poll_one(); +} + +inline void io_service::interrupt() +{ + impl_.interrupt(); +} + +inline void io_service::reset() +{ + impl_.reset(); +} + +template +inline void io_service::dispatch(Handler handler) +{ + impl_.dispatch(handler); +} + +template +inline void io_service::post(Handler handler) +{ + impl_.post(handler); +} + +template +#if defined(GENERATING_DOCUMENTATION) +unspecified +#else +inline detail::wrapped_handler +#endif +io_service::wrap(Handler handler) +{ + return detail::wrapped_handler(*this, handler); +} + +inline io_service::work::work(asio::io_service& io_service) + : io_service_(io_service) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::work(const work& other) + : io_service_(other.io_service_) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::~work() +{ + io_service_.impl_.work_finished(); +} + +inline asio::io_service& io_service::work::io_service() +{ + return io_service_; +} + +inline io_service::service::service(asio::io_service& owner) + : owner_(owner), + type_info_(0), + next_(0) +{ +} + +inline io_service::service::~service() +{ +} + +inline asio::io_service& io_service::service::io_service() +{ + return owner_; +} + +template +inline Service& use_service(io_service& ios) +{ + return ios.service_registry_.template use_service(); +} + +template +void add_service(io_service& ios, Service* svc) +{ + if (&ios != &svc->io_service()) + boost::throw_exception(invalid_service_owner()); + if (!ios.service_registry_.template add_service(svc)) + boost::throw_exception(service_already_exists()); +} + +template +bool has_service(io_service& ios) +{ + return ios.service_registry_.template has_service(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IO_SERVICE_IPP diff --git a/library/include/libtorrent/asio/impl/read.ipp b/library/include/libtorrent/asio/impl/read.ipp new file mode 100644 index 000000000..dece26985 --- /dev/null +++ b/library/include/libtorrent/asio/impl/read.ipp @@ -0,0 +1,294 @@ +// +// read.ipp +// ~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_READ_IPP +#define ASIO_READ_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error_handler.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { + +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + asio::detail::consuming_buffers< + mutable_buffer, Mutable_Buffers> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + typename Sync_Read_Stream::error_type e; + std::size_t bytes_transferred = s.read_some(tmp, assign_error(e)); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(e, total_transferred)) + { + error_handler(e); + return total_transferred; + } + } + typename Sync_Read_Stream::error_type e; + error_handler(e); + return total_transferred; +} + +template +inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers) +{ + return read(s, buffers, transfer_all(), throw_error()); +} + +template +inline std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition) +{ + return read(s, buffers, completion_condition, throw_error()); +} + +template +std::size_t read(Sync_Read_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + std::size_t total_transferred = 0; + for (;;) + { + typename Sync_Read_Stream::error_type e; + std::size_t bytes_transferred = s.read_some( + b.prepare(512), assign_error(e)); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(e, total_transferred)) + { + error_handler(e); + return total_transferred; + } + } +} + +template +inline std::size_t read(Sync_Read_Stream& s, + asio::basic_streambuf& b) +{ + return read(s, b, transfer_all(), throw_error()); +} + +template +inline std::size_t read(Sync_Read_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition) +{ + return read(s, b, completion_condition, throw_error()); +} + +namespace detail +{ + template + class read_handler + { + public: + read_handler(Async_Read_Stream& stream, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(e, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(e, total_transferred_); + } + else + { + stream_.async_read_some(buffers_, *this); + } + } + + //private: + Async_Read_Stream& stream_; + asio::detail::consuming_buffers< + mutable_buffer, Mutable_Buffers> buffers_; + std::size_t total_transferred_; + Completion_Condition completion_condition_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) +{ + s.async_read_some(buffers, + detail::read_handler( + s, buffers, completion_condition, handler)); +} + +template +inline void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Handler handler) +{ + async_read(s, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class read_streambuf_handler + { + public: + read_streambuf_handler(Async_Read_Stream& stream, + basic_streambuf& streambuf, + Completion_Condition completion_condition, Handler handler) + : stream_(stream), + streambuf_(streambuf), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + if (completion_condition_(e, total_transferred_)) + { + handler_(e, total_transferred_); + } + else + { + stream_.async_read_some(streambuf_.prepare(512), *this); + } + } + + //private: + Async_Read_Stream& stream_; + asio::basic_streambuf& streambuf_; + std::size_t total_transferred_; + Completion_Condition completion_condition_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(Async_Read_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition, Handler handler) +{ + s.async_read_some(b.prepare(512), + detail::read_streambuf_handler( + s, b, completion_condition, handler)); +} + +template +inline void async_read(Async_Read_Stream& s, + asio::basic_streambuf& b, Handler handler) +{ + async_read(s, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_IPP diff --git a/library/include/libtorrent/asio/impl/read_until.ipp b/library/include/libtorrent/asio/impl/read_until.ipp new file mode 100644 index 000000000..accb8d955 --- /dev/null +++ b/library/include/libtorrent/asio/impl/read_until.ipp @@ -0,0 +1,664 @@ +// +// read_until.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_READ_UNTIL_IPP +#define ASIO_READ_UNTIL_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error_handler.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/const_buffers_iterator.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { + +template +inline std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, char delim) +{ + return read_until(s, b, delim, throw_error()); +} + +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, char delim, + Error_Handler error_handler) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim); + if (iter != end) + { + // Found a match. We're done. + return iter.position() + 1; + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Need more data. + typename Sync_Read_Stream::error_type error; + b.commit(s.read_some(b.prepare(512), asio::assign_error(error))); + if (error) + { + error_handler(error); + return 0; + } + } +} + +template +inline std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim) +{ + return read_until(s, b, delim, throw_error()); +} + +namespace detail +{ + // Algorithm that finds a subsequence of equal values in a sequence. Returns + // (iterator,true) if a full match was found, in which case the iterator + // points to the beginning of the match. Returns (iterator,false) if a + // partial match was found at the end of the first sequence, in which case + // the iterator points to the beginning of the partial match. Returns + // (last1,false) if no full or partial match was found. + template + std::pair partial_search( + Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) + { + Iterator1 test_iter1 = iter1; + Iterator2 test_iter2 = first2; + for (;; ++test_iter1, ++test_iter2) + { + if (test_iter2 == last2) + return std::make_pair(iter1, true); + if (test_iter1 == last1) + { + if (test_iter2 != first2) + return std::make_pair(iter1, false); + else + break; + } + if (*test_iter1 != *test_iter2) + break; + } + } + return std::make_pair(last1, false); + } +} // namespace detail + +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim, + Error_Handler error_handler) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::pair result = asio::detail::partial_search( + begin, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + return result.first.position() + delim.length(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Need more data. + typename Sync_Read_Stream::error_type error; + b.commit(s.read_some(b.prepare(512), asio::assign_error(error))); + if (error) + { + error_handler(error); + return 0; + } + } +} + +template +inline std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr) +{ + return read_until(s, b, expr, throw_error()); +} + +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr, + Error_Handler error_handler) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + return match_results[0].second.position(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Need more data. + typename Sync_Read_Stream::error_type error; + b.commit(s.read_some(b.prepare(512), asio::assign_error(error))); + if (error) + { + error_handler(error); + return 0; + } + } +} + +namespace detail +{ + template + class read_until_delim_handler + { + public: + read_until_delim_handler(Async_Read_Stream& stream, + asio::basic_streambuf& streambuf, char delim, + std::size_t next_search_start, Handler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + // Check for errors. + if (e) + { + std::size_t bytes = 0; + handler_(e, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim_); + if (iter != end) + { + // Found a match. We're done. + std::size_t bytes = iter.position() + 1; + handler_(e, bytes); + return; + } + + // No match. Start a new asynchronous read operation to obtain more data. + next_search_start_ = end.position(); + stream_.async_read_some(streambuf_.prepare(512), *this); + } + + //private: + Async_Read_Stream& stream_; + asio::basic_streambuf& streambuf_; + char delim_; + std::size_t next_search_start_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, char delim, Handler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim); + if (iter != end) + { + // Found a match. We're done. + typename Async_Read_Stream::error_type error; + std::size_t bytes = iter.position() + 1; + s.io_service().post(detail::bind_handler(handler, error, bytes)); + return; + } + + // No match. Start a new asynchronous read operation to obtain more data. + s.async_read_some(b.prepare(512), + detail::read_until_delim_handler( + s, b, delim, end.position(), handler)); +} + +namespace detail +{ + template + class read_until_delim_string_handler + { + public: + read_until_delim_string_handler(Async_Read_Stream& stream, + asio::basic_streambuf& streambuf, + const std::string& delim, std::size_t next_search_start, + Handler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + // Check for errors. + if (e) + { + std::size_t bytes = 0; + handler_(e, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::pair result = asio::detail::partial_search( + begin, end, delim_.begin(), delim_.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + std::size_t bytes = result.first.position() + delim_.length(); + handler_(e, bytes); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start_ = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start_ = end.position(); + } + + // No match. Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(512), *this); + } + + //private: + Async_Read_Stream& stream_; + asio::basic_streambuf& streambuf_; + std::string delim_; + std::size_t next_search_start_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_string_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_string_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_string_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim, + Handler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::size_t next_search_start; + std::pair result = asio::detail::partial_search( + begin, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + typename Async_Read_Stream::error_type error; + std::size_t bytes = result.first.position() + delim.length(); + s.io_service().post(detail::bind_handler(handler, error, bytes)); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // No match. Start a new asynchronous read operation to obtain more data. + s.async_read_some(b.prepare(512), + detail::read_until_delim_string_handler< + Async_Read_Stream, Allocator, Handler>( + s, b, delim, next_search_start, handler)); +} + +namespace detail +{ + template + class read_until_expr_handler + { + public: + read_until_expr_handler(Async_Read_Stream& stream, + asio::basic_streambuf& streambuf, + const boost::regex& expr, std::size_t next_search_start, + Handler handler) + : stream_(stream), + streambuf_(streambuf), + expr_(expr), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const typename Async_Read_Stream::error_type& e, + std::size_t bytes_transferred) + { + // Check for errors. + if (e) + { + std::size_t bytes = 0; + handler_(e, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr_, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + std::size_t bytes = match_results[0].second.position(); + handler_(e, bytes); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start_ = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start_ = end.position(); + } + + // No match. Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(512), *this); + } + + //private: + Async_Read_Stream& stream_; + asio::basic_streambuf& streambuf_; + boost::regex expr_; + std::size_t next_search_start_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_expr_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_expr_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_expr_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr, + Handler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::size_t next_search_start; + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + typename Async_Read_Stream::error_type error; + std::size_t bytes = match_results[0].second.position(); + s.io_service().post(detail::bind_handler(handler, error, bytes)); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // No match. Start a new asynchronous read operation to obtain more data. + s.async_read_some(b.prepare(512), + detail::read_until_expr_handler( + s, b, expr, next_search_start, handler)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_UNTIL_IPP diff --git a/library/include/libtorrent/asio/impl/write.ipp b/library/include/libtorrent/asio/impl/write.ipp new file mode 100644 index 000000000..88b14b239 --- /dev/null +++ b/library/include/libtorrent/asio/impl/write.ipp @@ -0,0 +1,266 @@ +// +// write.ipp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_WRITE_IPP +#define ASIO_WRITE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error_handler.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { + +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + asio::detail::consuming_buffers< + const_buffer, Const_Buffers> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + typename Sync_Write_Stream::error_type e; + std::size_t bytes_transferred = s.write_some(tmp, assign_error(e)); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(e, total_transferred)) + { + error_handler(e); + return total_transferred; + } + } + typename Sync_Write_Stream::error_type e; + error_handler(e); + return total_transferred; +} + +template +inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers) +{ + return write(s, buffers, transfer_all(), throw_error()); +} + +template +inline std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition) +{ + return write(s, buffers, completion_condition, throw_error()); +} + +template +std::size_t write(Sync_Write_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition, Error_Handler error_handler) +{ + typename Sync_Write_Stream::error_type error; + std::size_t bytes_transferred = write(s, b.data(), + completion_condition, asio::assign_error(error)); + b.consume(bytes_transferred); + error_handler(error); + return bytes_transferred; +} + +template +inline std::size_t write(Sync_Write_Stream& s, + asio::basic_streambuf& b) +{ + return write(s, b, transfer_all(), throw_error()); +} + +template +inline std::size_t write(Sync_Write_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition) +{ + return write(s, b, completion_condition, throw_error()); +} + +namespace detail +{ + template + class write_handler + { + public: + write_handler(Async_Write_Stream& stream, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const typename Async_Write_Stream::error_type& e, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(e, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(e, total_transferred_); + } + else + { + stream_.async_write_some(buffers_, *this); + } + } + + //private: + Async_Write_Stream& stream_; + asio::detail::consuming_buffers< + const_buffer, Const_Buffers> buffers_; + std::size_t total_transferred_; + Completion_Condition completion_condition_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler) +{ + s.async_write_some(buffers, + detail::write_handler( + s, buffers, completion_condition, handler)); +} + +template +inline void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Handler handler) +{ + async_write(s, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class write_streambuf_handler + { + public: + write_streambuf_handler(asio::basic_streambuf& streambuf, + Handler handler) + : streambuf_(streambuf), + handler_(handler) + { + } + + void operator()(const typename Async_Write_Stream::error_type& e, + std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(e, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + Handler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(Async_Write_Stream& s, + asio::basic_streambuf& b, + Completion_Condition completion_condition, Handler handler) +{ + async_write(s, b.data(), completion_condition, + detail::write_streambuf_handler( + b, handler)); +} + +template +inline void async_write(Async_Write_Stream& s, + asio::basic_streambuf& b, Handler handler) +{ + async_write(s, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_IPP diff --git a/library/include/libtorrent/asio/io_service.hpp b/library/include/libtorrent/asio/io_service.hpp new file mode 100644 index 000000000..6808f3d45 --- /dev/null +++ b/library/include/libtorrent/asio/io_service.hpp @@ -0,0 +1,424 @@ +// +// io_service.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IO_SERVICE_HPP +#define ASIO_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/epoll_reactor_fwd.hpp" +#include "asio/detail/kqueue_reactor_fwd.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/service_registry.hpp" +#include "asio/detail/signal_init.hpp" +#include "asio/detail/task_io_service_fwd.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/detail/wrapped_handler.hpp" + +namespace asio { + +/// Provides core I/O functionality. +/** + * The io_service class provides the core I/O functionality for users of the + * asynchronous I/O objects, including: + * + * @li asio::ip::tcp::socket + * @li asio::ip::tcp::acceptor + * @li asio::ip::udp::socket + * @li asio::deadline_timer. + * + * The io_service class also includes facilities intended for developers of + * custom asynchronous services. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe, with the exception that calling reset() + * while there are unfinished run() calls results in undefined behaviour. + * + * @par Concepts: + * Dispatcher. + * + * @sa @ref io_service_handler_exception + */ +class io_service + : private noncopyable +{ +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_io_service impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::task_io_service > impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::task_io_service > impl_type; +#else + typedef detail::task_io_service > impl_type; +#endif + +public: + class work; + friend class work; + + class service; + + class strand; + + /// Default constructor. + io_service(); + + /// Run the io_service's event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the io_service has been + * interrupted. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the io_service may execute handlers. + * + * The run() function may be safely called again once it has completed only + * after a call to reset(). + * + * @return The number of handlers that were executed. + */ + size_t run(); + + /// Run the io_service's event processing loop to execute at most one handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_service has been interrupted. + * + * @return The number of handlers that were executed. + */ + size_t run_one(); + + /// Run the io_service's event processing loop to execute ready handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_service has been interrupted or there are no more ready + * handlers. + * + * @return The number of handlers that were executed. + */ + size_t poll(); + + /// Run the io_service's event processing loop to execute one ready handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @return The number of handlers that were executed. + */ + size_t poll_one(); + + /// Interrupt the io_service's event processing loop. + /** + * This function does not block, but instead simply signals to the io_service + * that all invocations of its run() or run_one() member functions should + * return as soon as possible. + * + * Note that if the run() function is interrupted and is not called again + * later then its work may not have finished and handlers may not be + * delivered. In this case an io_service implementation is not required to + * make any guarantee that the resources associated with unfinished work will + * be cleaned up. + */ + void interrupt(); + + /// Reset the io_service in preparation for a subsequent run() invocation. + /** + * This function must be called prior to any second or later set of + * invocations of the run(), run_one(), poll() or poll_one() functions. It + * allows the io_service to reset any internal state, such as an interrupt + * flag. + * + * This function must not be called while there are any unfinished calls to + * the run(), run_one(), poll() or poll_one() functions. + */ + void reset(); + + /// Request the io_service to invoke the given handler. + /** + * This function is used to ask the io_service to execute the given handler. + * + * The io_service guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. The handler may be executed inside this function + * if the guarantee can be met. + * + * @param handler The handler to be called. The io_service will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void dispatch(Handler handler); + + /// Request the io_service to invoke the given handler and return immediately. + /** + * This function is used to ask the io_service to execute the given handler, + * but without allowing the io_service to call the handler from inside this + * function. + * + * The io_service guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. + * + * @param handler The handler to be called. The io_service will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void post(Handler handler); + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the io_service. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the io_service's + * dispatch function. + * + * @param handler The handler to be wrapped. The io_service will make a copy + * of the handler object as required. The function signature of the handler + * must be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the io_service's dispatch function. Given a function object with the + * signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code io_service.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code io_service.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler); + + /// Obtain the service object corresponding to the given type. + /** + * This function is used to locate a service object that corresponds to + * the given service type. If there is no existing implementation of the + * service, then the io_service will create a new instance of the service. + * + * @param ios The io_service object that owns the service. + * + * @return The service interface implementing the specified service type. + * Ownership of the service interface is not transferred to the caller. + */ + template + friend Service& use_service(io_service& ios); + + /// Add a service object to the io_service. + /** + * This function is used to add a service to the io_service. + * + * @param ios The io_service object that owns the service. + * + * @param svc The service object. On success, ownership of the service object + * is transferred to the io_service. When the io_service object is destroyed, + * it will destroy the service object by performing: + * @code delete static_cast(svc) @endcode + * + * @throws asio::service_already_exists Thrown if a service of the + * given type is already present in the io_service. + * + * @throws asio::invalid_service_owner Thrown if the service's owning + * io_service is not the io_service object specified by the ios parameter. + */ + template + friend void add_service(io_service& ios, Service* svc); + + /// Determine if an io_service contains a specified service type. + /** + * This function is used to determine whether the io_service contains a + * service object corresponding to the given service type. + * + * @param ios The io_service object that owns the service. + * + * @return A boolean indicating whether the io_service contains the service. + */ + template + friend bool has_service(io_service& ios); + +private: +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + detail::winsock_init<> init_; +#elif defined(__sun) || defined(__QNX__) + detail::signal_init<> init_; +#endif + + // The service registry. + detail::service_registry service_registry_; + + // The implementation. + impl_type& impl_; +}; + +/// Class to inform the io_service when it has work to do. +/** + * The work class is used to inform the io_service when work starts and + * finishes. This ensures that the io_service's run() function will not exit + * while work is underway, and that it does exit when there is no unfinished + * work remaining. + * + * The work class is copy-constructible so that it may be used as a data member + * in a handler class. It is not assignable. + */ +class io_service::work +{ +public: + /// Constructor notifies the io_service that work is starting. + /** + * The constructor is used to inform the io_service that some work has begun. + * This ensures that the io_service's run() function will not exit while the + * work is underway. + */ + explicit work(asio::io_service& io_service); + + /// Copy constructor notifies the io_service that work is starting. + /** + * The constructor is used to inform the io_service that some work has begun. + * This ensures that the io_service's run() function will not exit while the + * work is underway. + */ + work(const work& other); + + /// Destructor notifies the io_service that the work is complete. + /** + * The destructor is used to inform the io_service that some work has + * finished. Once the count of unfinished work reaches zero, the io_service's + * run() function is permitted to exit. + */ + ~work(); + + /// Get the io_service associated with the work. + asio::io_service& io_service(); + +private: + // Prevent assignment. + void operator=(const work& other); + + // The io_service. + asio::io_service& io_service_; +}; + +/// Base class for all io_service services. +class io_service::service + : private noncopyable +{ +public: + /// Get the io_service object that owns the service. + asio::io_service& io_service(); + +protected: + /// Constructor. + /** + * @param owner The io_service object that owns the service. + */ + service(asio::io_service& owner); + + /// Destructor. + virtual ~service(); + +private: + /// Destroy all user-defined handler objects owned by the service. + virtual void shutdown_service() = 0; + + friend class detail::service_registry; + asio::io_service& owner_; + const std::type_info* type_info_; + service* next_; +}; + +/// Exception thrown when trying to add a duplicate service to an io_service. +class service_already_exists + : public std::logic_error +{ +public: + service_already_exists() + : std::logic_error("Service already exists.") + { + } +}; + +/// Exception thrown when trying to add a service object to an io_service where +/// the service has a different owner. +class invalid_service_owner + : public std::logic_error +{ +public: + invalid_service_owner() + : std::logic_error("Invalid service owner.") + { + } +}; + +/** + * @page io_service_handler_exception Effect of exceptions thrown from handlers + * + * If an exception is thrown from a handler, the exception is allowed to + * propagate through the throwing thread's invocation of + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one(). + * No other threads that are calling any of these functions are affected. It is + * then the responsibility of the application to catch the exception. + * + * After the exception has been caught, the + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one() + * call may be restarted @em without the need for an intervening call to + * asio::io_service::reset(). This allows the thread to rejoin the io_service's + * thread pool without impacting any other threads in the pool. + * + * @par Example: + * @code + * asio::io_service io_service; + * ... + * for (;;) + * { + * try + * { + * io_service.run(); + * break; // run() exited normally + * } + * catch (my_exception& e) + * { + * // Deal with exception as appropriate. + * } + * } + * @endcode + */ + +} // namespace asio + +#include "asio/impl/io_service.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IO_SERVICE_HPP diff --git a/library/include/libtorrent/asio/ip/address.hpp b/library/include/libtorrent/asio/ip/address.hpp new file mode 100644 index 000000000..5d9a6e3a1 --- /dev/null +++ b/library/include/libtorrent/asio/ip/address.hpp @@ -0,0 +1,284 @@ +// +// address.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_ADDRESS_HPP +#define ASIO_IP_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" + +namespace asio { +namespace ip { + +/// Implements version-independent IP addresses. +/** + * The asio::ip::address class provides the ability to use either IP + * version 4 or version 6 addresses. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address +{ +public: + /// Default constructor. + address() + : type_(ipv4), + ipv4_address_(), + ipv6_address_() + { + } + + /// Construct an address from an IPv4 address. + address(const asio::ip::address_v4& ipv4_address) + : type_(ipv4), + ipv4_address_(ipv4_address), + ipv6_address_() + { + } + + /// Construct an address from an IPv6 address. + address(const asio::ip::address_v6& ipv6_address) + : type_(ipv6), + ipv4_address_(), + ipv6_address_(ipv6_address) + { + } + + /// Copy constructor. + address(const address& other) + : type_(other.type_), + ipv4_address_(other.ipv4_address_), + ipv6_address_(other.ipv6_address_) + { + } + + /// Assign from another address. + address& operator=(const address& other) + { + type_ = other.type_; + ipv4_address_ = other.ipv4_address_; + ipv6_address_ = other.ipv6_address_; + return *this; + } + + /// Assign from an IPv4 address. + address& operator=(const asio::ip::address_v4& ipv4_address) + { + type_ = ipv4; + ipv4_address_ = ipv4_address; + ipv6_address_ = asio::ip::address_v6(); + return *this; + } + + /// Assign from an IPv6 address. + address& operator=(const asio::ip::address_v6& ipv6_address) + { + type_ = ipv6; + ipv4_address_ = asio::ip::address_v4(); + ipv6_address_ = ipv6_address; + return *this; + } + + /// Get whether the address is an IP version 4 address. + bool is_v4() const + { + return type_ == ipv4; + } + + /// Get whether the address is an IP version 6 address. + bool is_v6() const + { + return type_ == ipv6; + } + + /// Get the address as an IP version 4 address. + asio::ip::address_v4 to_v4() const + { + if (type_ != ipv4) + { + asio::error error( + asio::error::address_family_not_supported); + boost::throw_exception(error); + } + return ipv4_address_; + } + + /// Get the address as an IP version 6 address. + asio::ip::address_v6 to_v6() const + { + if (type_ != ipv6) + { + asio::error error( + asio::error::address_family_not_supported); + boost::throw_exception(error); + } + return ipv6_address_; + } + + /// Get the address as a string in dotted decimal format. + std::string to_string() const + { + if (type_ == ipv6) + return ipv6_address_.to_string(); + return ipv4_address_.to_string(); + } + + /// Get the address as a string in dotted decimal format. + template + std::string to_string(Error_Handler error_handler) const + { + if (type_ == ipv6) + return ipv6_address_.to_string(error_handler); + return ipv4_address_.to_string(error_handler); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const char* str) + { + return from_string(str, asio::throw_error()); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + template + static address from_string(const char* str, Error_Handler error_handler) + { + asio::error error; + asio::ip::address_v6 ipv6_address = + asio::ip::address_v6::from_string(str, + asio::assign_error(error)); + if (!error) + { + address tmp; + tmp.type_ = ipv6; + tmp.ipv6_address_ = ipv6_address; + error_handler(error); + return tmp; + } + + error = asio::error(); + asio::ip::address_v4 ipv4_address = + asio::ip::address_v4::from_string(str, + asio::assign_error(error)); + if (!error) + { + address tmp; + tmp.type_ = ipv4; + tmp.ipv4_address_ = ipv4_address; + error_handler(error); + return tmp; + } + + error_handler(error); + return address(); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const std::string& str) + { + return from_string(str.c_str(), asio::throw_error()); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + template + static address from_string(const std::string& str, + Error_Handler error_handler) + { + return from_string(str.c_str(), error_handler); + } + + /// Compare two addresses for equality. + friend bool operator==(const address& a1, const address& a2) + { + if (a1.type_ != a2.type_) + return false; + if (a1.type_ == ipv6) + return a1.ipv6_address_ == a2.ipv6_address_; + return a1.ipv4_address_ == a2.ipv4_address_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address& a1, const address& a2) + { + if (a1.type_ != a2.type_) + return true; + if (a1.type_ == ipv6) + return a1.ipv6_address_ != a2.ipv6_address_; + return a1.ipv4_address_ != a2.ipv4_address_; + } + + /// Compare addresses for ordering. + friend bool operator<(const address& a1, const address& a2) + { + if (a1.type_ < a2.type_) + return true; + if (a1.type_ > a2.type_) + return false; + if (a1.type_ == ipv6) + return a1.ipv6_address_ < a2.ipv6_address_; + return a1.ipv4_address_ < a2.ipv4_address_; + } + +private: + // The type of the address. + enum { ipv4, ipv6 } type_; + + // The underlying IPv4 address. + asio::ip::address_v4 ipv4_address_; + + // The underlying IPv6 address. + asio::ip::address_v6 ipv6_address_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address& addr) +{ + os << addr.to_string(); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_HPP diff --git a/library/include/libtorrent/asio/ip/address_v4.hpp b/library/include/libtorrent/asio/ip/address_v4.hpp new file mode 100644 index 000000000..eea9919b4 --- /dev/null +++ b/library/include/libtorrent/asio/ip/address_v4.hpp @@ -0,0 +1,292 @@ +// +// address_v4.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_ADDRESS_V4_HPP +#define ASIO_IP_ADDRESS_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Implements IP version 4 style addresses. +/** + * The asio::ip::address_v4 class provides the ability to use and + * manipulate IP version 4 addresses. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v4 +{ +public: + /// The type used to represent an address as an array of bytes. + typedef boost::array bytes_type; + + /// Default constructor. + address_v4() + { + addr_.s_addr = 0; + } + + /// Construct an address from raw bytes. + explicit address_v4(const bytes_type& bytes) + { + using namespace std; // For memcpy. + memcpy(&addr_.s_addr, bytes.elems, 4); + } + + /// Construct an address from a unsigned long in host byte order. + explicit address_v4(unsigned long addr) + { + addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr); + } + + /// Copy constructor. + address_v4(const address_v4& other) + : addr_(other.addr_) + { + } + + /// Assign from another address. + address_v4& operator=(const address_v4& other) + { + addr_ = other.addr_; + return *this; + } + + /// Get the address in bytes. + bytes_type to_bytes() const + { + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, &addr_.s_addr, 4); + return bytes; + } + + /// Get the address as an unsigned long in host byte order + unsigned long to_ulong() const + { + return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); + } + + /// Get the address as a string in dotted decimal format. + std::string to_string() const + { + return to_string(asio::throw_error()); + } + + /// Get the address as a string in dotted decimal format. + template + std::string to_string(Error_Handler error_handler) const + { + char addr_str[asio::detail::max_addr_v4_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, + asio::detail::max_addr_v4_str_len); + if (addr == 0) + { + asio::error e(asio::detail::socket_ops::get_error()); + error_handler(e); + return std::string(); + } + asio::error e; + error_handler(e); + return addr; + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const char* str) + { + return from_string(str, asio::throw_error()); + } + + /// Create an address from an IP address string in dotted decimal form. + template + static address_v4 from_string(const char* str, Error_Handler error_handler) + { + address_v4 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET, str, &tmp.addr_) <= 0) + { + asio::error e(asio::detail::socket_ops::get_error()); + error_handler(e); + return address_v4(); + } + asio::error e; + error_handler(e); + return tmp; + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const std::string& str) + { + return from_string(str.c_str(), asio::throw_error()); + } + + /// Create an address from an IP address string in dotted decimal form. + template + static address_v4 from_string(const std::string& str, + Error_Handler error_handler) + { + return from_string(str.c_str(), error_handler); + } + + /// Determine whether the address is a class A address. + bool is_class_a() const + { + return IN_CLASSA(to_ulong()); + } + + /// Determine whether the address is a class B address. + bool is_class_b() const + { + return IN_CLASSB(to_ulong()); + } + + /// Determine whether the address is a class C address. + bool is_class_c() const + { + return IN_CLASSC(to_ulong()); + } + + /// Determine whether the address is a multicast address. + bool is_multicast() const + { + return IN_MULTICAST(to_ulong()); + } + + /// Compare two addresses for equality. + friend bool operator==(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr == a2.addr_.s_addr; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr != a2.addr_.s_addr; + } + + /// Compare addresses for ordering. + friend bool operator<(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() < a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator>(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() > a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() <= a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() >= a2.to_ulong(); + } + + /// Obtain an address object that represents any address. + static address_v4 any() + { + return address_v4(static_cast(INADDR_ANY)); + } + + /// Obtain an address object that represents the loopback address. + static address_v4 loopback() + { + return address_v4(static_cast(INADDR_LOOPBACK)); + } + + /// Obtain an address object that represents the broadcast address. + static address_v4 broadcast() + { + return address_v4(static_cast(INADDR_BROADCAST)); + } + + /// Obtain an address object that represents the broadcast address that + /// corresponds to the specified address and netmask. + static address_v4 broadcast(const address_v4& addr, const address_v4& mask) + { + return address_v4(addr.to_ulong() | ~mask.to_ulong()); + } + + /// Obtain the netmask that corresponds to the address, based on its address + /// class. + static address_v4 netmask(const address_v4& addr) + { + if (addr.is_class_a()) + return address_v4(0xFF000000); + if (addr.is_class_b()) + return address_v4(0xFFFF0000); + if (addr.is_class_c()) + return address_v4(0xFFFFFF00); + return address_v4(0xFFFFFFFF); + } + +private: + // The underlying IPv4 address. + asio::detail::in4_addr_type addr_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v4 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v4& addr) +{ + asio::error e; + std::string s = addr.to_string(asio::assign_error(e)); + if (e) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V4_HPP diff --git a/library/include/libtorrent/asio/ip/address_v6.hpp b/library/include/libtorrent/asio/ip/address_v6.hpp new file mode 100644 index 000000000..85f0ebc1a --- /dev/null +++ b/library/include/libtorrent/asio/ip/address_v6.hpp @@ -0,0 +1,381 @@ +// +// address_v6.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_ADDRESS_V6_HPP +#define ASIO_IP_ADDRESS_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/address_v4.hpp" + +namespace asio { +namespace ip { + +/// Implements IP version 6 style addresses. +/** + * The asio::ip::address_v6 class provides the ability to use and + * manipulate IP version 6 addresses. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v6 +{ +public: + /// The type used to represent an address as an array of bytes. + typedef boost::array bytes_type; + + /// Default constructor. + address_v6() + : scope_id_(0) + { + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + addr_ = tmp_addr; + } + + /// Construct an address from raw bytes and scope ID. + explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0) + : scope_id_(scope_id) + { + using namespace std; // For memcpy. + memcpy(addr_.s6_addr, bytes.elems, 16); + } + + /// Copy constructor. + address_v6(const address_v6& other) + : addr_(other.addr_), + scope_id_(other.scope_id_) + { + } + + /// Assign from another address. + address_v6& operator=(const address_v6& other) + { + addr_ = other.addr_; + scope_id_ = other.scope_id_; + return *this; + } + + /// Get the scope ID of the address. + unsigned long scope_id() const + { + return scope_id_; + } + + /// Set the scope ID of the address. + void scope_id(unsigned long id) + { + scope_id_ = id; + } + + /// Get the address in bytes. + bytes_type to_bytes() const + { + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, addr_.s6_addr, 16); + return bytes; + } + + /// Get the address as a string. + std::string to_string() const + { + return to_string(asio::throw_error()); + } + + /// Get the address as a string. + template + std::string to_string(Error_Handler error_handler) const + { + char addr_str[asio::detail::max_addr_v6_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str, + asio::detail::max_addr_v6_str_len, scope_id_); + if (addr == 0) + { + asio::error e(asio::detail::socket_ops::get_error()); + error_handler(e); + return std::string(); + } + asio::error e; + error_handler(e); + return addr; + } + + /// Create an address from an IP address string. + static address_v6 from_string(const char* str) + { + return from_string(str, asio::throw_error()); + } + + /// Create an address from an IP address string. + template + static address_v6 from_string(const char* str, Error_Handler error_handler) + { + address_v6 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET6, str, &tmp.addr_, &tmp.scope_id_) <= 0) + { + asio::error e(asio::detail::socket_ops::get_error()); + error_handler(e); + return address_v6(); + } + asio::error e; + error_handler(e); + return tmp; + } + + /// Create an address from an IP address string. + static address_v6 from_string(const std::string& str) + { + return from_string(str.c_str(), asio::throw_error()); + } + + /// Create an address from an IP address string. + template + static address_v6 from_string(const std::string& str, + Error_Handler error_handler) + { + return from_string(str.c_str(), error_handler); + } + + /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address. + address_v4 to_v4() const + { + if (!is_v4_mapped() && !is_v4_compatible()) + throw std::bad_cast(); + address_v4::bytes_type v4_bytes = { addr_.s6_addr[12], + addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] }; + return address_v4(v4_bytes); + } + + /// Determine whether the address is a loopback address. + bool is_loopback() const + { + using namespace asio::detail; + return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; + } + + /// Determine whether the address is unspecified. + bool is_unspecified() const + { + using namespace asio::detail; + return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; + } + + /// Determine whether the address is link local. + bool is_link_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0; + } + + /// Determine whether the address is site local. + bool is_site_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_SITELOCAL(&addr_) != 0; + } + + /// Determine whether the address is a mapped IPv4 address. + bool is_v4_mapped() const + { + using namespace asio::detail; + return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; + } + + /// Determine whether the address is an IPv4-compatible address. + bool is_v4_compatible() const + { + using namespace asio::detail; + return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; + } + + /// Determine whether the address is a multicast address. + bool is_multicast() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MULTICAST(&addr_) != 0; + } + + /// Determine whether the address is a global multicast address. + bool is_multicast_global() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0; + } + + /// Determine whether the address is a link-local multicast address. + bool is_multicast_link_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0; + } + + /// Determine whether the address is a node-local multicast address. + bool is_multicast_node_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0; + } + + /// Determine whether the address is a org-local multicast address. + bool is_multicast_org_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0; + } + + /// Determine whether the address is a site-local multicast address. + bool is_multicast_site_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0; + } + + /// Compare two addresses for equality. + friend bool operator==(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) == 0 + && a1.scope_id_ == a2.scope_id_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) != 0 + || a1.scope_id_ != a2.scope_id_; + } + + /// Compare addresses for ordering. + friend bool operator<(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + int memcmp_result = memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) < 0; + if (memcmp_result < 0) + return true; + if (memcmp_result > 0) + return false; + return a1.scope_id_ < a2.scope_id_; + } + + /// Compare addresses for ordering. + friend bool operator>(const address_v6& a1, const address_v6& a2) + { + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v6& a1, const address_v6& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v6& a1, const address_v6& a2) + { + return !(a1 < a2); + } + + /// Obtain an address object that represents any address. + static address_v6 any() + { + return address_v6(); + } + + /// Obtain an address object that represents the loopback address. + static address_v6 loopback() + { + address_v6 tmp; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT; + tmp.addr_ = tmp_addr; + return tmp; + } + + /// Create an IPv4-mapped IPv6 address. + static address_v6 v4_mapped(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] }; + return address_v6(v6_bytes); + } + + /// Create an IPv4-compatible IPv6 address. + static address_v6 v4_compatible(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] }; + return address_v6(v6_bytes); + } + +private: + // The underlying IPv6 address. + asio::detail::in6_addr_type addr_; + + // The scope ID associated with the address. + unsigned long scope_id_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v6 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v6& addr) +{ + asio::error e; + std::string s = addr.to_string(asio::assign_error(e)); + if (e) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V6_HPP diff --git a/library/include/libtorrent/asio/ip/basic_endpoint.hpp b/library/include/libtorrent/asio/ip/basic_endpoint.hpp new file mode 100644 index 000000000..56ce99eca --- /dev/null +++ b/library/include/libtorrent/asio/ip/basic_endpoint.hpp @@ -0,0 +1,358 @@ +// +// basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_BASIC_ENDPOINT_HPP +#define ASIO_IP_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/ip/address.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Describes an endpoint for a version-independent IP socket. +/** + * The asio::ip::basic_endpoint class template describes an endpoint that + * may be associated with a particular socket. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef Protocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// The type for the size of the endpoint structure. This type is dependent on + /// the underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined size_type; +#else + typedef asio::detail::socket_addr_len_type size_type; +#endif + + /// Default constructor. + basic_endpoint() + : data_() + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = 0; + data.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number, specified in the host's byte + /// order. The IP address will be the any address (i.e. INADDR_ANY or + /// in6addr_any). This constructor would typically be used for accepting new + /// connections. + /** + * @par Examples: + * To initialise an IPv4 TCP endpoint for port 1234, use: + * @code + * asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 1234); + * @endcode + * + * To specify an IPv6 UDP endpoint for port 9876, use: + * @code + * asio::ip::udp::endpoint ep(asio::ip::udp::v6(), 9876); + * @endcode + */ + basic_endpoint(const Protocol& protocol, unsigned short port_num) + : data_() + { + using namespace std; // For memcpy. + if (protocol.family() == PF_INET) + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin_addr.s_addr = INADDR_ANY; + } + else + { + asio::detail::sockaddr_in6_type& data + = reinterpret_cast(data_); + data.sin6_family = AF_INET6; + data.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin6_flowinfo = 0; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + data.sin6_addr = tmp_addr; + data.sin6_scope_id = 0; + } + } + + /// Construct an endpoint using a port number and an IP address. This + /// constructor may be used for accepting connections on a specific interface + /// or for making a connection to a remote endpoint. + basic_endpoint(const asio::ip::address& addr, unsigned short port_num) + : data_() + { + using namespace std; // For memcpy. + if (addr.is_v4()) + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin_addr.s_addr = + asio::detail::socket_ops::host_to_network_long( + addr.to_v4().to_ulong()); + } + else + { + asio::detail::sockaddr_in6_type& data + = reinterpret_cast(data_); + data.sin6_family = AF_INET6; + data.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin6_flowinfo = 0; + asio::ip::address_v6 v6_addr = addr.to_v6(); + asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); + memcpy(data.sin6_addr.s6_addr, bytes.elems, 16); + data.sin6_scope_id = v6_addr.scope_id(); + } + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : data_(other.data_) + { + } + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + data_ = other.data_; + return *this; + } + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + if (data_.ss_family == AF_INET) + return Protocol::v4(); + return Protocol::v6(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return reinterpret_cast(&data_); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return reinterpret_cast(&data_); + } + + /// Get the underlying size of the endpoint in the native type. + size_type size() const + { + if (data_.ss_family == AF_INET) + return sizeof(asio::detail::sockaddr_in4_type); + else + return sizeof(asio::detail::sockaddr_in6_type); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(size_type size) + { + if (size > size_type(sizeof(data_))) + { + asio::error e(asio::error::invalid_argument); + boost::throw_exception(e); + } + } + + /// Get the capacity of the endpoint in the native type. + size_type capacity() const + { + return sizeof(data_); + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + if (data_.ss_family == AF_INET) + { + return asio::detail::socket_ops::network_to_host_short( + reinterpret_cast( + data_).sin_port); + } + else + { + return asio::detail::socket_ops::network_to_host_short( + reinterpret_cast( + data_).sin6_port); + } + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port(unsigned short port_num) + { + if (data_.ss_family == AF_INET) + { + reinterpret_cast(data_).sin_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + else + { + reinterpret_cast(data_).sin6_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + } + + /// Get the IP address associated with the endpoint. + asio::ip::address address() const + { + using namespace std; // For memcpy. + if (data_.ss_family == AF_INET) + { + const asio::detail::sockaddr_in4_type& data + = reinterpret_cast( + data_); + return asio::ip::address_v4( + asio::detail::socket_ops::network_to_host_long( + data.sin_addr.s_addr)); + } + else + { + const asio::detail::sockaddr_in6_type& data + = reinterpret_cast( + data_); + asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.elems, data.sin6_addr.s6_addr, 16); + return asio::ip::address_v6(bytes, data.sin6_scope_id); + } + } + + /// Set the IP address associated with the endpoint. + void address(const asio::ip::address& addr) + { + basic_endpoint tmp_endpoint(addr, port()); + data_ = tmp_endpoint.data_; + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // The underlying IP socket address. + asio::detail::sockaddr_storage_type data_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates asio::ip::basic_endpoint + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +std::ostream& operator<<(std::ostream& os, + const basic_endpoint& endpoint) +{ + const address& addr = endpoint.address(); + if (addr.is_v4()) + os << addr.to_string(); + else + os << '[' << addr.to_string() << ']'; + os << ':' << endpoint.port(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + const address& addr = endpoint.address(); + if (addr.is_v4()) + os << addr.to_string(); + else + os << '[' << addr.to_string() << ']'; + os << ':' << endpoint.port(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_ENDPOINT_HPP diff --git a/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp b/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp new file mode 100644 index 000000000..6198425ab --- /dev/null +++ b/library/include/libtorrent/asio/ip/basic_resolver_entry.hpp @@ -0,0 +1,98 @@ +// +// basic_resolver_entry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_BASIC_RESOLVER_ENTRY_HPP +#define ASIO_IP_BASIC_RESOLVER_ENTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace ip { + +/// An entry produced by a resolver. +/** + * The asio::ip::basic_resolver_entry class template describes an entry + * as returned by a resolver. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_resolver_entry +{ +public: + /// The protocol type associated with the endpoint entry. + typedef Protocol protocol_type; + + /// The endpoint type associated with the endpoint entry. + typedef typename Protocol::endpoint endpoint_type; + + /// Default constructor. + basic_resolver_entry() + { + } + + /// Construct with specified endpoint, host name and service name. + basic_resolver_entry(const endpoint_type& endpoint, + const std::string& host_name, const std::string& service_name) + : endpoint_(endpoint), + host_name_(host_name), + service_name_(service_name) + { + } + + /// Get the endpoint associated with the entry. + endpoint_type endpoint() const + { + return endpoint_; + } + + /// Convert to the endpoint associated with the entry. + operator endpoint_type() const + { + return endpoint_; + } + + /// Get the host name associated with the entry. + std::string host_name() const + { + return host_name_; + } + + /// Get the service name associated with the entry. + std::string service_name() const + { + return service_name_; + } + +private: + endpoint_type endpoint_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ENTRY_HPP diff --git a/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp b/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp new file mode 100644 index 000000000..12984ee79 --- /dev/null +++ b/library/include/libtorrent/asio/ip/basic_resolver_iterator.hpp @@ -0,0 +1,151 @@ +// +// basic_resolver_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP +#define ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_resolver_entry.hpp" + +namespace asio { +namespace ip { + +/// An iterator over the entries produced by a resolver. +/** + * The asio::ip::basic_resolver_iterator class template is used to define + * iterators over the results returned by a resolver. + * + * The iterator's value_type, obtained when the iterator is dereferenced, is: + * @code const basic_resolver_entry @endcode + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_iterator + : public boost::iterator_facade< + basic_resolver_iterator, + const basic_resolver_entry, + boost::forward_traversal_tag> +{ +public: + /// Default constructor creates an end iterator. + basic_resolver_iterator() + { + } + + /// Create an iterator from an addrinfo list returned by getaddrinfo. + static basic_resolver_iterator create( + asio::detail::addrinfo_type* address_info, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_iterator iter; + if (!address_info) + return iter; + + std::string actual_host_name = host_name; + if (address_info->ai_canonname) + actual_host_name = address_info->ai_canonname; + + iter.values_.reset(new values_type); + + while (address_info) + { + if (address_info->ai_family == PF_INET + || address_info->ai_family == PF_INET6) + { + using namespace std; // For memcpy. + typename Protocol::endpoint endpoint; + endpoint.resize(address_info->ai_addrlen); + memcpy(endpoint.data(), address_info->ai_addr, + address_info->ai_addrlen); + iter.values_->push_back( + basic_resolver_entry(endpoint, + actual_host_name, service_name)); + } + address_info = address_info->ai_next; + } + + if (iter.values_->size()) + iter.iter_ = iter.values_->begin(); + else + iter.values_.reset(); + + return iter; + } + + /// Create an iterator from an endpoint, host name and service name. + static basic_resolver_iterator create( + const typename Protocol::endpoint& endpoint, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_iterator iter; + iter.values_.reset(new values_type); + iter.values_->push_back( + basic_resolver_entry(endpoint, host_name, service_name)); + iter.iter_ = iter.values_->begin(); + return iter; + } + +private: + friend class boost::iterator_core_access; + + void increment() + { + if (++iter_ == values_->end()) + { + // Reset state to match a default constructed end iterator. + values_.reset(); + typedef typename values_type::const_iterator values_iterator_type; + iter_ = values_iterator_type(); + } + } + + bool equal(const basic_resolver_iterator& other) const + { + if (!values_ && !other.values_) + return true; + if (values_ != other.values_) + return false; + return iter_ == other.iter_; + } + + const basic_resolver_entry& dereference() const + { + return *iter_; + } + + typedef std::vector > values_type; + boost::shared_ptr values_; + typename values_type::const_iterator iter_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP diff --git a/library/include/libtorrent/asio/ip/basic_resolver_query.hpp b/library/include/libtorrent/asio/ip/basic_resolver_query.hpp new file mode 100644 index 000000000..ddee4b1c3 --- /dev/null +++ b/library/include/libtorrent/asio/ip/basic_resolver_query.hpp @@ -0,0 +1,152 @@ +// +// basic_resolver_query.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_BASIC_RESOLVER_QUERY_HPP +#define ASIO_IP_BASIC_RESOLVER_QUERY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_ops.hpp" +#include "asio/ip/resolver_query_base.hpp" + +namespace asio { +namespace ip { + +/// An query to be passed to a resolver. +/** + * The asio::ip::basic_resolver_query class template describes a query + * that can be passed to a resolver. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_resolver_query + : public resolver_query_base +{ +public: + /// The protocol type associated with the endpoint query. + typedef Protocol protocol_type; + + /// Construct with specified service name for any protocol. + basic_resolver_query(const std::string& service_name, + int flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service_name) + { + typename Protocol::endpoint endpoint; + hints_.ai_flags = flags; + hints_.ai_family = PF_UNSPEC; + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified service name for a given protocol. + basic_resolver_query(const protocol_type& protocol, + const std::string& service_name, + int flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service_name) + { + hints_.ai_flags = flags; + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for any protocol. + basic_resolver_query(const std::string& host_name, + const std::string& service_name, int flags = address_configured) + : hints_(), + host_name_(host_name), + service_name_(service_name) + { + typename Protocol::endpoint endpoint; + hints_.ai_flags = flags; + hints_.ai_family = PF_UNSPEC; + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for a given protocol. + basic_resolver_query(const protocol_type& protocol, + const std::string& host_name, const std::string& service_name, + int flags = address_configured) + : hints_(), + host_name_(host_name), + service_name_(service_name) + { + hints_.ai_flags = flags; + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Get the hints associated with the query. + const asio::detail::addrinfo_type& hints() const + { + return hints_; + } + + /// Get the host name associated with the query. + std::string host_name() const + { + return host_name_; + } + + /// Get the service name associated with the query. + std::string service_name() const + { + return service_name_; + } + +private: + asio::detail::addrinfo_type hints_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_QUERY_HPP diff --git a/library/include/libtorrent/asio/ip/detail/socket_option.hpp b/library/include/libtorrent/asio/ip/detail/socket_option.hpp new file mode 100644 index 000000000..ae733e987 --- /dev/null +++ b/library/include/libtorrent/asio/ip/detail/socket_option.hpp @@ -0,0 +1,406 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_DETAIL_SOCKET_OPTION_HPP +#define ASIO_IP_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/address.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + boolean(bool value) + : value_(value ? 1 : 0) + { + } + + // Set the value of the boolean. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the boolean. + bool get() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + integer(int value) + : value_(value) + { + } + + // Set the value of the int option. + void set(int value) + { + value_ = value; + } + + // Get the current value of the int option. + int get() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the int data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + +private: + int value_; +}; + +// Helper template for implementing ip_mreq-based options. +template +class multicast_request +{ +public: + // Default constructor. + multicast_request() + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + + // Construct with multicast address only. + multicast_request(const asio::ip::address& multicast_address) + { + if (multicast_address.is_v6()) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + using namespace std; // For memcpy. + asio::ip::address_v6 ipv6_address = multicast_address.to_v6(); + asio::ip::address_v6::bytes_type bytes = ipv6_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); + ipv6_value_.ipv6mr_interface = 0; + } + else + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_v4().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + } + + // Construct with multicast address and IPv4 address specifying an interface. + multicast_request(const asio::ip::address_v4& multicast_address, + const asio::ip::address_v4& network_interface + = asio::ip::address_v4::any()) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + network_interface.to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + + // Construct with multicast address and IPv6 network interface index. + multicast_request(const asio::ip::address_v6& multicast_address, + unsigned long network_interface = 0) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + using namespace std; // For memcpy. + asio::ip::address_v6::bytes_type bytes = + multicast_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); + ipv6_value_.ipv6mr_interface = network_interface; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + void* data(const Protocol& protocol) + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_mreq_type ipv4_value_; + asio::detail::in6_mreq_type ipv6_value_; +}; + +// Helper template for implementing options that specify a network interface. +template +class network_interface +{ +public: + // Default constructor. + network_interface() + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv6_value_ = 0; + } + + // Construct with IPv4 interface. + network_interface(const asio::ip::address_v4& ipv4_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + ipv4_interface.to_ulong()); + ipv6_value_ = 0; + } + + // Construct with IPv6 interface. + network_interface(unsigned long ipv6_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv6_value_ = ipv6_interface; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + void* data(const Protocol& protocol) + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_addr_type ipv4_value_; + unsigned long ipv6_value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_DETAIL_SOCKET_OPTION_HPP diff --git a/library/include/libtorrent/asio/ip/host_name.hpp b/library/include/libtorrent/asio/ip/host_name.hpp new file mode 100644 index 000000000..23c906a6a --- /dev/null +++ b/library/include/libtorrent/asio/ip/host_name.hpp @@ -0,0 +1,60 @@ +// +// host_name.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_HOST_NAME_HPP +#define ASIO_IP_HOST_NAME_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_handler.hpp" +#include "asio/detail/socket_ops.hpp" + +namespace asio { +namespace ip { + +/// Get the current host name. +std::string host_name(); + +/// Get the current host name. +template +std::string host_name(Error_Handler error_handler); + +inline std::string host_name() +{ + return host_name(asio::throw_error()); +} + +template +std::string host_name(Error_Handler error_handler) +{ + char name[1024]; + if (asio::detail::socket_ops::gethostname(name, sizeof(name)) != 0) + { + asio::error error(asio::detail::socket_ops::get_error()); + error_handler(error); + return std::string(); + } + return std::string(name); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_HOST_NAME_HPP diff --git a/library/include/libtorrent/asio/ip/multicast.hpp b/library/include/libtorrent/asio/ip/multicast.hpp new file mode 100644 index 000000000..616e9020c --- /dev/null +++ b/library/include/libtorrent/asio/ip/multicast.hpp @@ -0,0 +1,181 @@ +// +// multicast.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_MULTICAST_HPP +#define ASIO_IP_MULTICAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/detail/socket_option.hpp" + +namespace asio { +namespace ip { +namespace multicast { + +/// Socket option to join a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option. + * + * @par Examples: + * Setting the option to join a multicast group: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::join_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * Socket_Option, IP_MReq_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined join_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + IPPROTO_IP, IP_ADD_MEMBERSHIP, IPPROTO_IPV6, IPV6_JOIN_GROUP> join_group; +#endif + +/// Socket option to leave a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option. + * + * @par Examples: + * Setting the option to leave a multicast group: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::leave_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * Socket_Option, IP_MReq_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined leave_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + IPPROTO_IP, IP_DROP_MEMBERSHIP, IPPROTO_IPV6, IPV6_LEAVE_GROUP> leave_group; +#endif + +/// Socket option for local interface to use for outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address_v4 local_interface = + * asio::ip::address_v4::from_string("1.2.3.4"); + * asio::ip::multicast::outbound_interface option(local_interface); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * Socket_Option, IP_Network_Interface_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined outbound_interface; +#else +typedef asio::ip::detail::socket_option::network_interface< + IPPROTO_IP, IP_MULTICAST_IF, IPPROTO_IPV6, IPV6_MULTICAST_IF> + outbound_interface; +#endif + +/// Socket option for time-to-live associated with outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::hops option(4); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::hops option; + * socket.get_option(option); + * int ttl = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined hops; +#else +typedef asio::ip::detail::socket_option::integer< + IPPROTO_IP, IP_MULTICAST_TTL, IPPROTO_IPV6, IPV6_MULTICAST_HOPS> hops; +#endif + +/// Socket option determining whether outgoing multicast packets will be +/// received on the same socket if it is a member of the multicast group. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::enable_loopback option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::enable_loopback option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined enable_loopback; +#else +typedef asio::ip::detail::socket_option::boolean< + IPPROTO_IP, IP_MULTICAST_LOOP, IPPROTO_IPV6, IPV6_MULTICAST_LOOP> + enable_loopback; +#endif + +} // namespace multicast +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_MULTICAST_HPP diff --git a/library/include/libtorrent/asio/ip/resolver_query_base.hpp b/library/include/libtorrent/asio/ip/resolver_query_base.hpp new file mode 100644 index 000000000..a8c7ad6dc --- /dev/null +++ b/library/include/libtorrent/asio/ip/resolver_query_base.hpp @@ -0,0 +1,107 @@ +// +// resolver_query_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_RESOLVER_QUERY_BASE_HPP +#define ASIO_IP_RESOLVER_QUERY_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// The resolver_query_base class is used as a base for the +/// basic_resolver_query class templates to provide a common place to define +/// the flag constants. +class resolver_query_base +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// Determine the canonical name of the host specified in the query. + static const int canonical_name = implementation_defined; + + /// Indicate that returned endpoint is intended for use as a locally bound + /// socket endpoint. + static const int passive = implementation_defined; + + /// Host name should be treated as a numeric string defining an IPv4 or IPv6 + /// address and no name resolution should be attempted. + static const int numeric_host = implementation_defined; + + /// Service name should be treated as a numeric string defining a port number + /// and no name resolution should be attempted. + static const int numeric_service = implementation_defined; + + /// If the query protocol family is specified as IPv6, return IPv4-mapped + /// IPv6 addresses on finding no IPv6 addresses. + static const int v4_mapped = implementation_defined; + + /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses. + static const int all_matching = implementation_defined; + + /// Only return IPv4 addresses if a non-loopback IPv4 address is configured + /// for the system. Only return IPv6 addresses if a non-loopback IPv6 address + /// is configured for the system. + static const int address_configured = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, canonical_name = AI_CANONNAME); + BOOST_STATIC_CONSTANT(int, passive = AI_PASSIVE); + BOOST_STATIC_CONSTANT(int, numeric_host = AI_NUMERICHOST); +# if defined(AI_NUMERICSERV) + BOOST_STATIC_CONSTANT(int, numeric_service = AI_NUMERICSERV); +# else + BOOST_STATIC_CONSTANT(int, numeric_service = 0); +# endif +# if defined(AI_V4MAPPED) + BOOST_STATIC_CONSTANT(int, v4_mapped = AI_V4MAPPED); +# else + BOOST_STATIC_CONSTANT(int, v4_mapped = 0); +# endif +# if defined(AI_ALL) + BOOST_STATIC_CONSTANT(int, all_matching = AI_ALL); +# else + BOOST_STATIC_CONSTANT(int, all_matching = 0); +# endif +# if defined(AI_ADDRCONFIG) + BOOST_STATIC_CONSTANT(int, address_configured = AI_ADDRCONFIG); +# else + BOOST_STATIC_CONSTANT(int, address_configured = 0); +# endif +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~resolver_query_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_RESOLVER_QUERY_BASE_HPP diff --git a/library/include/libtorrent/asio/ip/tcp.hpp b/library/include/libtorrent/asio/ip/tcp.hpp new file mode 100644 index 000000000..92532f12e --- /dev/null +++ b/library/include/libtorrent/asio/ip/tcp.hpp @@ -0,0 +1,146 @@ +// +// tcp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_TCP_HPP +#define ASIO_IP_TCP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_resolver.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for TCP. +/** + * The asio::ip::tcp class contains flags necessary for TCP sockets. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class tcp +{ +public: + /// The type of a TCP endpoint. + typedef basic_endpoint endpoint; + + /// The type of a resolver query. + typedef basic_resolver_query resolver_query; + + /// The type of a resolver iterator. + typedef basic_resolver_iterator resolver_iterator; + + /// Construct to represent the IPv4 TCP protocol. + static tcp v4() + { + return tcp(PF_INET); + } + + /// Construct to represent the IPv4 TCP protocol. + static tcp v6() + { + return tcp(PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_TCP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The TCP socket type. + typedef basic_stream_socket socket; + + /// The TCP acceptor type. + typedef basic_socket_acceptor acceptor; + + /// The TCP resolver type. + typedef basic_resolver resolver; + + /// The TCP iostream type. + typedef basic_socket_iostream iostream; + + /// Socket option for disabling the Nagle algorithm. + /** + * Implements the IPPROTO_TCP/TCP_NODELAY socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ipv6::tcp::socket socket(io_service); + * ... + * asio::ipv6::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ipv6::tcp::socket socket(io_service); + * ... + * asio::ipv6::tcp::no_delay option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined no_delay; +#else + typedef asio::detail::socket_option::boolean< + IPPROTO_TCP, TCP_NODELAY> no_delay; +#endif + +private: + // Construct with a specific family. + explicit tcp(int family) + : family_(family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_TCP_HPP diff --git a/library/include/libtorrent/asio/ip/udp.hpp b/library/include/libtorrent/asio/ip/udp.hpp new file mode 100644 index 000000000..7cc822615 --- /dev/null +++ b/library/include/libtorrent/asio/ip/udp.hpp @@ -0,0 +1,104 @@ +// +// udp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IP_UDP_HPP +#define ASIO_IP_UDP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_resolver.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for UDP. +/** + * The asio::ip::udp class contains flags necessary for UDP sockets. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class udp +{ +public: + /// The type of a UDP endpoint. + typedef basic_endpoint endpoint; + + /// The type of a resolver query. + typedef basic_resolver_query resolver_query; + + /// The type of a resolver iterator. + typedef basic_resolver_iterator resolver_iterator; + + /// Construct to represent the IPv4 UDP protocol. + static udp v4() + { + return udp(PF_INET); + } + + /// Construct to represent the IPv4 UDP protocol. + static udp v6() + { + return udp(PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_UDP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The IPv4 UDP socket type. + typedef basic_datagram_socket socket; + + /// The UDP resolver type. + typedef basic_resolver resolver; + +private: + // Construct with a specific family. + explicit udp(int family) + : family_(family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_UDP_HPP diff --git a/library/include/libtorrent/asio/is_read_buffered.hpp b/library/include/libtorrent/asio/is_read_buffered.hpp new file mode 100644 index 000000000..7c9c2b70f --- /dev/null +++ b/library/include/libtorrent/asio/is_read_buffered.hpp @@ -0,0 +1,62 @@ +// +// is_read_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IS_READ_BUFFERED_HPP +#define ASIO_IS_READ_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_stream_fwd.hpp" + +namespace asio { + +namespace detail { + +template +char is_read_buffered_helper(buffered_stream* s); + +template +char is_read_buffered_helper(buffered_read_stream* s); + +struct is_read_buffered_big_type { char data[10]; }; +is_read_buffered_big_type is_read_buffered_helper(...); + +} // namespace detail + +/// The is_read_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of read data. +template +class is_read_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// read data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_READ_BUFFERED_HPP diff --git a/library/include/libtorrent/asio/is_write_buffered.hpp b/library/include/libtorrent/asio/is_write_buffered.hpp new file mode 100644 index 000000000..d0221180d --- /dev/null +++ b/library/include/libtorrent/asio/is_write_buffered.hpp @@ -0,0 +1,62 @@ +// +// is_write_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_IS_WRITE_BUFFERED_HPP +#define ASIO_IS_WRITE_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_write_stream_fwd.hpp" + +namespace asio { + +namespace detail { + +template +char is_write_buffered_helper(buffered_stream* s); + +template +char is_write_buffered_helper(buffered_write_stream* s); + +struct is_write_buffered_big_type { char data[10]; }; +is_write_buffered_big_type is_write_buffered_helper(...); + +} // namespace detail + +/// The is_write_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of written data. +template +class is_write_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// written data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_WRITE_BUFFERED_HPP diff --git a/library/include/libtorrent/asio/placeholders.hpp b/library/include/libtorrent/asio/placeholders.hpp new file mode 100644 index 000000000..04727d786 --- /dev/null +++ b/library/include/libtorrent/asio/placeholders.hpp @@ -0,0 +1,80 @@ +// +// placeholders.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_ARG_HPP +#define ASIO_ARG_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +namespace placeholders { + +namespace { + +#if defined(__BORLANDC__) + +static inline boost::arg<1> error() +{ + return boost::arg<1>(); +} + +static inline boost::arg<2> bytes_transferred() +{ + return boost::arg<2>(); +} + +static inline boost::arg<2> iterator() +{ + return boost::arg<2>(); +} + +#elif defined(_MSC_VER) && (_MSC_VER < 1400) + +static boost::arg<1> error; +static boost::arg<2> bytes_transferred; +static boost::arg<2> iterator; + +#else + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the error argument of a handler for any of the asynchronous functions. +boost::arg<1> error; + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the bytes_transferred argument of a handler for asynchronous functions such +/// as asio::basic_stream_socket::async_write_some or +/// asio::async_write. +boost::arg<2> bytes_transferred; + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the iterator argument of a handler for asynchronous functions such as +/// asio::basic_resolver::resolve. +boost::arg<2> iterator; + +#endif + +} // namespace + +} // namespace placeholders + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ARG_HPP diff --git a/library/include/libtorrent/asio/read.hpp b/library/include/libtorrent/asio/read.hpp new file mode 100644 index 000000000..612dfcae8 --- /dev/null +++ b/library/include/libtorrent/asio/read.hpp @@ -0,0 +1,547 @@ +// +// read.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_READ_HPP +#define ASIO_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/** + * @defgroup read asio::read + */ +/*@{*/ + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all(), + * asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, completion_condition, + * asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes read. If an error occurs, and the error handler + * does not throw an exception, returns the total number of bytes successfully + * transferred prior to the error. + */ +template +std::size_t read(Sync_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, b, + * asio::transfer_all(), + * asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, basic_streambuf& b); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, b, completion_condition, + * asio::throw_error()); @endcode + */ +template +std::size_t read(Sync_Read_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes read. If an error occurs, and the error handler + * does not throw an exception, returns the total number of bytes successfully + * transferred prior to the error. + */ +template +std::size_t read(Sync_Read_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition, Error_Handler error_handler); + +/*@}*/ +/** + * @defgroup async_read asio::async_read + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_read(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Handler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's async_read_some function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::async_read(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_read(Async_Read_Stream& s, const Mutable_Buffers& buffers, + Completion_Condition completion_condition, Handler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li An error occurred. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, b, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read(Async_Read_Stream& s, basic_streambuf& b, + Handler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Read_Stream::error_type& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's async_read_some function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into + * // the buffers. If an error + * // occurred, this will be the + * // number of bytes successfully + * // transferred prior to the + * // error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_read(Async_Read_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition, Handler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/read.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_HPP diff --git a/library/include/libtorrent/asio/read_until.hpp b/library/include/libtorrent/asio/read_until.hpp new file mode 100644 index 000000000..473b02613 --- /dev/null +++ b/library/include/libtorrent/asio/read_until.hpp @@ -0,0 +1,487 @@ +// +// read_until.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_READ_UNTIL_HPP +#define ASIO_READ_UNTIL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/** + * @defgroup read_until asio::read_until + */ +/*@{*/ + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, '\n'); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * + * @note This overload is equivalent to calling: + * @code asio::read_until( + * s, b, delim, + * asio::throw_error()); @endcode + */ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, char delim); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred and the error handler did not + * throw an exception. + */ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, char delim, + Error_Handler error_handler); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, "\r\n"); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * + * @note This overload is equivalent to calling: + * @code asio::read_until( + * s, b, delim, + * asio::throw_error()); @endcode + */ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred and the error handler did not + * throw an exception. + */ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim, + Error_Handler error_handler); + +/// Read data into a streambuf until a regular expression is located. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the substring that matches the regular expression. + * + * @throws Sync_Read_Stream::error_type Thrown on failure. + * + * @par Example: + * To read data into a streambuf until a CR-LF sequence is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, boost::regex("\r\n")); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + * + * @note This overload is equivalent to calling: + * @code asio::read_until( + * s, b, expr, + * asio::throw_error()); @endcode + */ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr); + +/// Read data into a streambuf until a regular expression is located. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Sync_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Read_Stream::error_type& error // Result of operation. + * ); @endcode + * The error handler is only called if the completion_condition indicates that + * the operation is complete. + * + * @returns The number of bytes in the streambuf's get area up to and including +* the substring that matches the regular expression. +*/ +template +std::size_t read_until(Sync_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr, + Error_Handler error_handler); + +/*@}*/ +/** +* @defgroup async_read_until asio::async_read_until +*/ +/*@{*/ + +/// Start an asynchronous operation to read data into a streambuf until a +/// delimiter is encountered. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains the + * delimiter, the asynchronous operation completes immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter character. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the delimiter. + * // 0 if an error occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, '\n', handler); @endcode + */ +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, char delim, Handler handler); + +/// Start an asynchronous operation to read data into a streambuf until a +/// delimiter is encountered. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains the + * delimiter, the asynchronous operation completes immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter string. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the delimiter. + * // 0 if an error occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, "\r\n", handler); @endcode + */ +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, const std::string& delim, + Handler handler); + +/// Start an asynchronous operation to read data into a streambuf until a +/// regular expression is located. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains some data that matches a + * regular expression. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains data + * that matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the Async_Read_Stream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param expr The regular expression. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Read_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the substring + * // that matches the regular + * // expression. 0 if an error + * // occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To asynchronously read data into a streambuf until a CR-LF sequence is + * encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, boost::regex("\r\n"), handler); @endcode + */ +template +void async_read_until(Async_Read_Stream& s, + asio::basic_streambuf& b, const boost::regex& expr, + Handler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/read_until.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_UNTIL_HPP diff --git a/library/include/libtorrent/asio/resolver_service.hpp b/library/include/libtorrent/asio/resolver_service.hpp new file mode 100644 index 000000000..bdd8dbfbd --- /dev/null +++ b/library/include/libtorrent/asio/resolver_service.hpp @@ -0,0 +1,126 @@ +// +// resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_RESOLVER_SERVICE_HPP +#define ASIO_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/resolver_service.hpp" + +namespace asio { + +/// Default service implementation for a resolver. +template +class resolver_service + : public asio::io_service::service +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The query type. + typedef typename Protocol::resolver_query query_type; + + /// The iterator type. + typedef typename Protocol::resolver_iterator iterator_type; + +private: + // The type of the platform-specific implementation. + typedef detail::resolver_service service_impl_type; + +public: + /// The type of a resolver implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new resolver service for the specified io_service. + explicit resolver_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new resolver implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a resolver implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + service_impl_.cancel(impl); + } + + /// Resolve a query to a list of entries. + template + iterator_type resolve(implementation_type& impl, const query_type& query, + Error_Handler error_handler) + { + return service_impl_.resolve(impl, query, error_handler); + } + + /// Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler handler) + { + service_impl_.async_resolve(impl, query, handler); + } + + /// Resolve an endpoint to a list of entries. + template + iterator_type resolve(implementation_type& impl, + const endpoint_type& endpoint, Error_Handler error_handler) + { + return service_impl_.resolve(impl, endpoint, error_handler); + } + + /// Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + Handler handler) + { + return service_impl_.async_resolve(impl, endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_RESOLVER_SERVICE_HPP diff --git a/library/include/libtorrent/asio/socket_acceptor_service.hpp b/library/include/libtorrent/asio/socket_acceptor_service.hpp new file mode 100644 index 000000000..8c7eb43c9 --- /dev/null +++ b/library/include/libtorrent/asio/socket_acceptor_service.hpp @@ -0,0 +1,219 @@ +// +// socket_acceptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SOCKET_ACCEPTOR_SERVICE_HPP +#define ASIO_SOCKET_ACCEPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/reactive_socket_service.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a socket acceptor. +template +class socket_acceptor_service + : public asio::io_service::service +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The native type of the socket acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native acceptor type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new socket acceptor service for the specified io_service. + explicit socket_acceptor_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new socket acceptor implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a socket acceptor implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a new socket acceptor implementation. + template + void open(implementation_type& impl, const protocol_type& protocol, + Error_Handler error_handler) + { + service_impl_.open(impl, protocol, error_handler); + } + + /// Assign an existing native acceptor to a socket acceptor. + template + void assign(implementation_type& impl, const protocol_type& protocol, + const native_type& native_acceptor, Error_Handler error_handler) + { + service_impl_.assign(impl, protocol, native_acceptor, error_handler); + } + + /// Cancel all asynchronous operations associated with the acceptor. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + + /// Bind the socket acceptor to the specified local endpoint. + template + void bind(implementation_type& impl, const endpoint_type& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Place the socket acceptor into the state where it will listen for new + /// connections. + template + void listen(implementation_type& impl, int backlog, + Error_Handler error_handler) + { + service_impl_.listen(impl, backlog, error_handler); + } + + /// Close a socket acceptor implementation. + template + void close(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + /// Get the native acceptor implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Set a socket option. + template + void set_option(implementation_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Set a socket option. + template + void get_option(implementation_type& impl, Option& option, + Error_Handler error_handler) + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Get the local endpoint. + template + endpoint_type local_endpoint(const implementation_type& impl, + Error_Handler error_handler) const + { + endpoint_type endpoint; + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + return endpoint; + } + + /// Accept a new connection. + template + void accept(implementation_type& impl, + basic_socket& peer, + Error_Handler error_handler) + { + service_impl_.accept(impl, peer, error_handler); + } + + /// Accept a new connection. + template + void accept_endpoint(implementation_type& impl, + basic_socket& peer, + endpoint_type& peer_endpoint, Error_Handler error_handler) + { + service_impl_.accept_endpoint(impl, peer, peer_endpoint, error_handler); + } + + /// Start an asynchronous accept. + template + void async_accept(implementation_type& impl, + basic_socket& peer, Handler handler) + { + service_impl_.async_accept(impl, peer, handler); + } + + /// Start an asynchronous accept. + template + void async_accept_endpoint(implementation_type& impl, + basic_socket& peer, + endpoint_type& peer_endpoint, Handler handler) + { + service_impl_.async_accept_endpoint(impl, peer, peer_endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SOCKET_ACCEPTOR_SERVICE_HPP diff --git a/library/include/libtorrent/asio/socket_base.hpp b/library/include/libtorrent/asio/socket_base.hpp new file mode 100644 index 000000000..48e37357c --- /dev/null +++ b/library/include/libtorrent/asio/socket_base.hpp @@ -0,0 +1,482 @@ +// +// socket_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SOCKET_BASE_HPP +#define ASIO_SOCKET_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/io_control.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { + +/// The socket_base class is used as a base for the basic_stream_socket and +/// basic_datagram_socket class templates so that we have a common place to +/// define the shutdown_type and enum. +class socket_base +{ +public: + /// Different ways a socket may be shutdown. + enum shutdown_type + { +#if defined(GENERATING_DOCUMENTATION) + /// Shutdown the receive side of the socket. + shutdown_receive = implementation_defined, + + /// Shutdown the send side of the socket. + shutdown_send = implementation_defined, + + /// Shutdown both send and receive on the socket. + shutdown_both = implementation_defined +#else + shutdown_receive = asio::detail::shutdown_receive, + shutdown_send = asio::detail::shutdown_send, + shutdown_both = asio::detail::shutdown_both +#endif + }; + + /// Bitmask type for flags that can be passed to send and receive operations. + typedef int message_flags; + +#if defined(GENERATING_DOCUMENTATION) + /// Peek at incoming data without removing it from the input queue. + static const int message_peek = implementation_defined; + + /// Process out-of-band data. + static const int message_out_of_band = implementation_defined; + + /// Specify that the data should not be subject to routing. + static const int message_do_not_route = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, + message_peek = asio::detail::message_peek); + BOOST_STATIC_CONSTANT(int, + message_out_of_band = asio::detail::message_out_of_band); + BOOST_STATIC_CONSTANT(int, + message_do_not_route = asio::detail::message_do_not_route); +#endif + + /// Socket option to permit sending of broadcast messages. + /** + * Implements the SOL_SOCKET/SO_BROADCAST socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::broadcast option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::broadcast option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined broadcast; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_BROADCAST> broadcast; +#endif + + /// Socket option to prevent routing, use local interfaces only. + /** + * Implements the SOL_SOCKET/SO_DONTROUTE socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::do_not_route option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::do_not_route option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined do_not_route; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_DONTROUTE> do_not_route; +#endif + + /// Socket option to send keep-alives. + /** + * Implements the SOL_SOCKET/SO_KEEPALIVE socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::keep_alive option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::keep_alive option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined keep_alive; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_KEEPALIVE> keep_alive; +#endif + + /// Socket option for the send buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_SNDBUF socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_buffer_size option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_buffer_size; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDBUF> send_buffer_size; +#endif + + /// Socket option for the send low watermark. + /** + * Implements the SOL_SOCKET/SO_SNDLOWAT socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_low_watermark option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_low_watermark; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDLOWAT> send_low_watermark; +#endif + + /// Socket option for the receive buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_RCVBUF socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_buffer_size option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_buffer_size; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVBUF> receive_buffer_size; +#endif + + /// Socket option for the receive low watermark. + /** + * Implements the SOL_SOCKET/SO_RCVLOWAT socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_low_watermark option; + * socket.get_option(option); + * int size = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_low_watermark; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVLOWAT> receive_low_watermark; +#endif + + /// Socket option to allow the socket to be bound to an address that is + /// already in use. + /** + * Implements the SOL_SOCKET/SO_REUSEADDR socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined reuse_address; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_REUSEADDR> reuse_address; +#endif + + /// Socket option to specify whether the socket lingers on close if unsent + /// data is present. + /** + * Implements the SOL_SOCKET/SO_LINGER socket option. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::linger option(true, 30); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::linger option; + * socket.get_option(option); + * bool is_set = option.enabled(); + * unsigned short timeout = option.timeout(); + * @endcode + * + * @par Concepts: + * Socket_Option, Linger_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined linger; +#else + typedef asio::detail::socket_option::linger< + SOL_SOCKET, SO_LINGER> linger; +#endif + + /// Socket option to report aborted connections on accept. + /** + * Implements a custom socket option that determines whether or not an accept + * operation is permitted to fail with asio::error::connection_aborted. + * By default the option is false. + * + * @par Examples: + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::enable_connection_aborted option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::enable_connection_aborted option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined enable_connection_aborted; +#else + typedef asio::detail::socket_option::boolean< + asio::detail::custom_socket_option_level, + asio::detail::enable_connection_aborted_option> + enable_connection_aborted; +#endif + + /// IO control command to set the blocking mode of the socket. + /** + * Implements the FIONBIO IO control command. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Boolean_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined non_blocking_io; +#else + typedef asio::detail::io_control::non_blocking_io non_blocking_io; +#endif + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::bytes_readable command(true); + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Size_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef asio::detail::io_control::bytes_readable bytes_readable; +#endif + + /// The maximum length of the queue of pending incoming connections. +#if defined(GENERATING_DOCUMENTATION) + static const int max_connections = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, max_connections = SOMAXCONN); +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~socket_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SOCKET_BASE_HPP diff --git a/library/include/libtorrent/asio/ssl.hpp b/library/include/libtorrent/asio/ssl.hpp new file mode 100644 index 000000000..2d4621340 --- /dev/null +++ b/library/include/libtorrent/asio/ssl.hpp @@ -0,0 +1,26 @@ +// +// ssl.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_HPP +#define ASIO_SSL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/context.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/context_service.hpp" +#include "asio/ssl/stream.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/stream_service.hpp" + +#endif // ASIO_SSL_HPP diff --git a/library/include/libtorrent/asio/ssl/basic_context.hpp b/library/include/libtorrent/asio/ssl/basic_context.hpp new file mode 100755 index 000000000..2c25e81a8 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/basic_context.hpp @@ -0,0 +1,467 @@ +// +// basic_context.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_BASIC_CONTEXT_HPP +#define ASIO_SSL_BASIC_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_handler.hpp" +#include "asio/io_service.hpp" +#include "asio/ssl/context_base.hpp" + +namespace asio { +namespace ssl { + +/// SSL context. +template +class basic_context + : public context_base, + private boost::noncopyable +{ +public: + /// The type of the service that will be used to provide context operations. + typedef Service service_type; + + /// The native implementation type of the locking dispatcher. + typedef typename service_type::impl_type impl_type; + + /// Constructor. + basic_context(asio::io_service& io_service, method m) + : service_(asio::use_service(io_service)), + impl_(service_.null()) + { + service_.create(impl_, m); + } + + /// Destructor. + ~basic_context() + { + service_.destroy(impl_); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @throws asio::error Thrown on failure. + */ + void set_options(options o) + { + service_.set_options(impl_, o, throw_error()); + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void set_options(options o, Error_Handler error_handler) + { + service_.set_options(impl_, o, error_handler); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @throws asio::error Thrown on failure. + */ + void set_verify_mode(verify_mode v) + { + service_.set_verify_mode(impl_, v, throw_error()); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void set_verify_mode(verify_mode v, Error_Handler error_handler) + { + service_.set_verify_mode(impl_, v, error_handler); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load one or more trusted certification authorities + * from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @throws asio::error Thrown on failure. + */ + void load_verify_file(const std::string& filename) + { + service_.load_verify_file(impl_, filename, throw_error()); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load the certificates for one or more trusted + * certification authorities from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void load_verify_file(const std::string& filename, + Error_Handler error_handler) + { + service_.load_verify_file(impl_, filename, error_handler); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @throws asio::error Thrown on failure. + */ + void add_verify_path(const std::string& path) + { + service_.add_verify_path(impl_, path, throw_error()); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void add_verify_path(const std::string& path, Error_Handler error_handler) + { + service_.add_verify_path(impl_, path, error_handler); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::error Thrown on failure. + */ + void use_certificate_file(const std::string& filename, file_format format) + { + service_.use_certificate_file(impl_, filename, format, throw_error()); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void use_certificate_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_certificate_file(impl_, filename, format, error_handler); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @throws asio::error Thrown on failure. + */ + void use_certificate_chain_file(const std::string& filename) + { + service_.use_certificate_chain_file(impl_, filename, throw_error()); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void use_certificate_chain_file(const std::string& filename, + Error_Handler error_handler) + { + service_.use_certificate_chain_file(impl_, filename, error_handler); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::error Thrown on failure. + */ + void use_private_key_file(const std::string& filename, file_format format) + { + service_.use_private_key_file(impl_, filename, format, throw_error()); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void use_private_key_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_private_key_file(impl_, filename, format, error_handler); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::error Thrown on failure. + */ + void use_rsa_private_key_file(const std::string& filename, file_format format) + { + service_.use_rsa_private_key_file(impl_, filename, format, throw_error()); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void use_rsa_private_key_file(const std::string& filename, file_format format, + Error_Handler error_handler) + { + service_.use_rsa_private_key_file(impl_, filename, format, error_handler); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @throws asio::error Thrown on failure. + */ + void use_tmp_dh_file(const std::string& filename) + { + service_.use_tmp_dh_file(impl_, filename, throw_error()); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void use_tmp_dh_file(const std::string& filename, Error_Handler error_handler) + { + service_.use_tmp_dh_file(impl_, filename, error_handler); + } + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @throws asio::error Thrown on failure. + */ + template + void set_password_callback(Password_Callback callback) + { + service_.set_password_callback(impl_, callback, throw_error()); + } + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void set_password_callback(Password_Callback callback, + Error_Handler error_handler) + { + service_.set_password_callback(impl_, callback, error_handler); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_BASIC_CONTEXT_HPP diff --git a/library/include/libtorrent/asio/ssl/context.hpp b/library/include/libtorrent/asio/ssl/context.hpp new file mode 100644 index 000000000..86e249cbc --- /dev/null +++ b/library/include/libtorrent/asio/ssl/context.hpp @@ -0,0 +1,35 @@ +// +// context.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_CONTEXT_HPP +#define ASIO_SSL_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/context_service.hpp" + +namespace asio { +namespace ssl { + +/// Typedef for the typical usage of context. +typedef basic_context context; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_HPP diff --git a/library/include/libtorrent/asio/ssl/context_base.hpp b/library/include/libtorrent/asio/ssl/context_base.hpp new file mode 100755 index 000000000..0811d8ba5 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/context_base.hpp @@ -0,0 +1,164 @@ +// +// context_base.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_CONTEXT_BASE_HPP +#define ASIO_SSL_CONTEXT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { + +/// The context_base class is used as a base for the basic_context class +/// template so that we have a common place to define various enums. +class context_base +{ +public: + /// Different methods supported by a context. + enum method + { + /// Generic SSL version 2. + sslv2, + + /// SSL version 2 client. + sslv2_client, + + /// SSL version 2 server. + sslv2_server, + + /// Generic SSL version 3. + sslv3, + + /// SSL version 3 client. + sslv3_client, + + /// SSL version 3 server. + sslv3_server, + + /// Generic TLS version 1. + tlsv1, + + /// TLS version 1 client. + tlsv1_client, + + /// TLS version 1 server. + tlsv1_server, + + /// Generic SSL/TLS. + sslv23, + + /// SSL/TLS client. + sslv23_client, + + /// SSL/TLS server. + sslv23_server + }; + + /// Bitmask type for SSL options. + typedef int options; + +#if defined(GENERATING_DOCUMENTATION) + /// Implement various bug workarounds. + static const int default_workarounds = implementation_defined; + + /// Always create a new key when using tmp_dh parameters. + static const int single_dh_use = implementation_defined; + + /// Disable SSL v2. + static const int no_sslv2 = implementation_defined; + + /// Disable SSL v3. + static const int no_sslv3 = implementation_defined; + + /// Disable TLS v1. + static const int no_tlsv1 = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, default_workarounds = SSL_OP_ALL); + BOOST_STATIC_CONSTANT(int, single_dh_use = SSL_OP_SINGLE_DH_USE); + BOOST_STATIC_CONSTANT(int, no_sslv2 = SSL_OP_NO_SSLv2); + BOOST_STATIC_CONSTANT(int, no_sslv3 = SSL_OP_NO_SSLv3); + BOOST_STATIC_CONSTANT(int, no_tlsv1 = SSL_OP_NO_TLSv1); +#endif + + /// File format types. + enum file_format + { + /// ASN.1 file. + asn1, + + /// PEM file. + pem + }; + + /// Bitmask type for peer verification. + typedef int verify_mode; + +#if defined(GENERATING_DOCUMENTATION) + /// No verification. + static const int verify_none = implementation_defined; + + /// Verify the peer. + static const int verify_peer = implementation_defined; + + /// Fail verification if the peer has no certificate. Ignored unless + /// verify_peer is set. + static const int verify_fail_if_no_peer_cert = implementation_defined; + + /// Do not request client certificate on renegotiation. Ignored unless + /// verify_peer is set. + static const int verify_client_once = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE); + BOOST_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER); + BOOST_STATIC_CONSTANT(int, + verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); +#endif + + /// Purpose of PEM password. + enum password_purpose + { + /// The password is needed for reading/decryption. + for_reading, + + /// The password is needed for writing/encryption. + for_writing + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~context_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_BASE_HPP diff --git a/library/include/libtorrent/asio/ssl/context_service.hpp b/library/include/libtorrent/asio/ssl/context_service.hpp new file mode 100755 index 000000000..636b970c3 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/context_service.hpp @@ -0,0 +1,171 @@ +// +// context_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_CONTEXT_SERVICE_HPP +#define ASIO_SSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/detail/openssl_context_service.hpp" + +namespace asio { +namespace ssl { + +/// Default service implementation for a context. +class context_service + : public asio::io_service::service +{ +private: + // The type of the platform-specific implementation. + typedef detail::openssl_context_service service_impl_type; + +public: + /// The type of the context. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef service_impl_type::impl_type impl_type; +#endif + + /// Constructor. + explicit context_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Return a null context implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + service_impl_.create(impl, m); + } + + /// Destroy a context implementation. + void destroy(impl_type& impl) + { + service_impl_.destroy(impl); + } + + /// Set options on the context. + template + void set_options(impl_type& impl, context_base::options o, + Error_Handler error_handler) + { + service_impl_.set_options(impl, o, error_handler); + } + + /// Set peer verification mode. + template + void set_verify_mode(impl_type& impl, context_base::verify_mode v, + Error_Handler error_handler) + { + service_impl_.set_verify_mode(impl, v, error_handler); + } + + /// Load a certification authority file for performing verification. + template + void load_verify_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.load_verify_file(impl, filename, error_handler); + } + + /// Add a directory containing certification authority files to be used for + /// performing verification. + template + void add_verify_path(impl_type& impl, const std::string& path, + Error_Handler error_handler) + { + service_impl_.add_verify_path(impl, path, error_handler); + } + + /// Use a certificate from a file. + template + void use_certificate_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_certificate_file(impl, filename, format, error_handler); + } + + /// Use a certificate chain from a file. + template + void use_certificate_chain_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.use_certificate_chain_file(impl, filename, error_handler); + } + + /// Use a private key from a file. + template + void use_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_private_key_file(impl, filename, format, error_handler); + } + + /// Use an RSA private key from a file. + template + void use_rsa_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + service_impl_.use_rsa_private_key_file(impl, filename, format, + error_handler); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + template + void use_tmp_dh_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + service_impl_.use_tmp_dh_file(impl, filename, error_handler); + } + + /// Set the password callback. + template + void set_password_callback(impl_type& impl, Password_Callback callback, + Error_Handler error_handler) + { + service_impl_.set_password_callback(impl, callback, error_handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_SERVICE_HPP diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp new file mode 100755 index 000000000..723bcadf2 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp @@ -0,0 +1,396 @@ +// +// openssl_context_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP +#define ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/detail/openssl_init.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_context_service + : public asio::io_service::service +{ +public: + // The native type of the context. + typedef ::SSL_CTX* impl_type; + + // The type for the password callback function object. + typedef boost::function password_callback_type; + + // Constructor. + openssl_context_service(asio::io_service& io_service) + : asio::io_service::service(io_service) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Return a null context implementation. + static impl_type null() + { + return 0; + } + + // Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + ::SSL_METHOD* ssl_method = 0; + switch (m) + { + case context_base::sslv2: + ssl_method = ::SSLv2_method(); + break; + case context_base::sslv2_client: + ssl_method = ::SSLv2_client_method(); + break; + case context_base::sslv2_server: + ssl_method = ::SSLv2_server_method(); + break; + case context_base::sslv3: + ssl_method = ::SSLv3_method(); + break; + case context_base::sslv3_client: + ssl_method = ::SSLv3_client_method(); + break; + case context_base::sslv3_server: + ssl_method = ::SSLv3_server_method(); + break; + case context_base::tlsv1: + ssl_method = ::TLSv1_method(); + break; + case context_base::tlsv1_client: + ssl_method = ::TLSv1_client_method(); + break; + case context_base::tlsv1_server: + ssl_method = ::TLSv1_server_method(); + break; + case context_base::sslv23: + ssl_method = ::SSLv23_method(); + break; + case context_base::sslv23_client: + ssl_method = ::SSLv23_client_method(); + break; + case context_base::sslv23_server: + ssl_method = ::SSLv23_server_method(); + break; + default: + break; + } + impl = ::SSL_CTX_new(ssl_method); + } + + // Destroy a context implementation. + void destroy(impl_type& impl) + { + if (impl != null()) + { + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback = + static_cast( + impl->default_passwd_callback_userdata); + delete callback; + impl->default_passwd_callback_userdata = 0; + } + + ::SSL_CTX_free(impl); + impl = null(); + } + } + + // Set options on the context. + template + void set_options(impl_type& impl, context_base::options o, + Error_Handler error_handler) + { + ::SSL_CTX_set_options(impl, o); + + asio::error e; + error_handler(e); + } + + // Set peer verification mode. + template + void set_verify_mode(impl_type& impl, context_base::verify_mode v, + Error_Handler error_handler) + { + ::SSL_CTX_set_verify(impl, v, 0); + + asio::error e; + error_handler(e); + } + + // Load a certification authority file for performing verification. + template + void load_verify_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Add a directory containing certification authority files to be used for + // performing verification. + template + void add_verify_path(impl_type& impl, const std::string& path, + Error_Handler error_handler) + { + if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Use a certificate from a file. + template + void use_certificate_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Use a certificate chain from a file. + template + void use_certificate_chain_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Use a private key from a file. + template + void use_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Use an RSA private key from a file. + template + void use_rsa_private_key_file(impl_type& impl, const std::string& filename, + context_base::file_format format, Error_Handler error_handler) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + } + + if (::SSL_CTX_use_RSAPrivateKey_file( + impl, filename.c_str(), file_type) != 1) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Use the specified file to obtain the temporary Diffie-Hellman parameters. + template + void use_tmp_dh_file(impl_type& impl, const std::string& filename, + Error_Handler error_handler) + { + ::BIO* bio = ::BIO_new_file(filename.c_str(), "r"); + if (!bio) + { + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0); + if (!dh) + { + ::BIO_free(bio); + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + ::BIO_free(bio); + int result = ::SSL_CTX_set_tmp_dh(impl, dh); + if (result != 1) + { + ::DH_free(dh); + asio::error e(asio::error::invalid_argument); + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + static int password_callback(char* buf, int size, int purpose, void* data) + { + using namespace std; // For strncat and strlen. + + if (data) + { + password_callback_type* callback = + static_cast(data); + std::string passwd = (*callback)(static_cast(size), + purpose ? context_base::for_writing : context_base::for_reading); + *buf = '\0'; + strncat(buf, passwd.c_str(), size); + return strlen(buf); + } + + return 0; + } + + // Set the password callback. + template + void set_password_callback(impl_type& impl, Password_Callback callback, + Error_Handler error_handler) + { + // Allocate callback function object if not already present. + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback_function = + static_cast( + impl->default_passwd_callback_userdata); + *callback_function = callback; + } + else + { + password_callback_type* callback_function = + new password_callback_type(callback); + impl->default_passwd_callback_userdata = callback_function; + } + + // Set the password callback. + SSL_CTX_set_default_passwd_cb(impl, + &openssl_context_service::password_callback); + + asio::error e; + error_handler(e); + } + +private: + // Ensure openssl is initialised. + openssl_init<> init_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp new file mode 100755 index 000000000..60d19b787 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/detail/openssl_init.hpp @@ -0,0 +1,128 @@ +// +// openssl_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_DETAIL_OPENSSL_INIT_HPP +#define ASIO_SSL_DETAIL_OPENSSL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/mutex.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +class openssl_init + : private boost::noncopyable +{ +private: + // Structure to perform the actual initialisation. + class do_init + { + public: + do_init() + { + if (Do_Init) + { + ::SSL_library_init(); + ::SSL_load_error_strings(); + + mutexes_.resize(::CRYPTO_num_locks()); + for (size_t i = 0; i < mutexes_.size(); ++i) + mutexes_[i].reset(new asio::detail::mutex); + ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); + + ::OpenSSL_add_ssl_algorithms(); + } + } + + ~do_init() + { + if (Do_Init) + { + ::CRYPTO_set_locking_callback(0); + ::ERR_free_strings(); + ::ERR_remove_state(0); + ::EVP_cleanup(); + ::CRYPTO_cleanup_all_ex_data(); + ::CONF_modules_unload(1); + ::ENGINE_cleanup(); + } + } + + // Helper function to manage a do_init singleton. The static instance of the + // openssl_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + static void openssl_locking_func(int mode, int n, + const char *file, int line) + { + if (mode & CRYPTO_LOCK) + instance()->mutexes_[n]->lock(); + else + instance()->mutexes_[n]->unlock(); + } + + // Mutexes to be used in locking callbacks. + std::vector > mutexes_; + }; + +public: + // Constructor. + openssl_init() + : ref_(do_init::instance()) + { + while (&instance_ == 0); // Ensure openssl_init::instance_ is linked in. + } + + // Destructor. + ~openssl_init() + { + } + +private: + // Instance to force initialisation of openssl at global scope. + static openssl_init instance_; + + // Reference to singleton do_init object to ensure that openssl does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +openssl_init openssl_init::instance_; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_INIT_HPP diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp new file mode 100755 index 000000000..1497638f1 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/detail/openssl_operation.hpp @@ -0,0 +1,474 @@ +// +// openssl_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// +// Distributed under 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 ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP +#define ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/placeholders.hpp" +#include "asio/write.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +typedef boost::function ssl_primitive_func; +typedef boost::function user_handler_func; + +// Network send_/recv buffer implementation +// +// +class net_buffer +{ + static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare + + unsigned char buf_[NET_BUF_SIZE]; + unsigned char* data_start_; + unsigned char* data_end_; + +public: + net_buffer() + { + data_start_ = data_end_ = buf_; + } + unsigned char* get_unused_start() { return data_end_; } + unsigned char* get_data_start() { return data_start_; } + size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); } + size_t get_data_len() { return (data_end_ - data_start_); } + void data_added(size_t count) + { + data_end_ += count; + data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? + (buf_ + NET_BUF_SIZE): + data_end_; + } + void data_removed(size_t count) + { + data_start_ += count; + if (data_start_ >= data_end_) reset(); + } + void reset() { data_start_ = buf_; data_end_ = buf_; } + bool has_data() { return (data_start_ < data_end_); } +}; // class net_buffer + +// +// Operation class +// +// +template +class openssl_operation +{ +public: + + // Constructor for asynchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + net_buffer& recv_buf, + SSL* session, + BIO* ssl_bio, + user_handler_func handler + ) + : primitive_(primitive) + , user_handler_(handler) + , recv_buf_(recv_buf) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_async_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_= boost::bind( + &openssl_operation::async_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Constructor for synchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + net_buffer& recv_buf, + SSL* session, + BIO* ssl_bio) + : primitive_(primitive) + , recv_buf_(recv_buf) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_sync_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_ = boost::bind( + &openssl_operation::sync_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Start operation + // In case of asynchronous it returns 0, in sync mode returns success code + // or throws an error... + int start() + { + int rc = primitive_( session_ ); + int sys_error_code = ERR_get_error(); + bool is_operation_done = (rc > 0); + // For connect/accept/shutdown, the operation + // is done, when return code is 1 + // for write, it is done, when is retcode > 0 + // for read, is is done when retcode > 0 + + int error_code = !is_operation_done ? + ::SSL_get_error( session_, rc ) : + 0; + bool is_read_needed = (error_code == SSL_ERROR_WANT_READ); + bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE || + ::BIO_ctrl_pending( ssl_bio_ )); + bool is_shut_down_received = + ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) == + SSL_RECEIVED_SHUTDOWN); + bool is_shut_down_sent = + ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) == + SSL_SENT_SHUTDOWN); + + if (is_shut_down_sent && is_shut_down_received && is_operation_done) + // SSL connection is shut down cleanly + return handler_(asio::error(), 1); + + if (is_shut_down_received && !is_write_needed) + return handler_(asio::error(asio::error::eof), 0); + + if (is_shut_down_received) + // Shutdown has been requested, while we were reading or writing... + // abort our action... + return handler_(asio::error(asio::error::shut_down), 0); + + if (!is_operation_done && !is_read_needed && !is_write_needed + && !is_shut_down_sent) + { + // The operation has failed... It is not completed and does + // not want network communication nor does want to send shutdown out... + if (error_code == SSL_ERROR_SYSCALL) + return handler_(asio::error(sys_error_code), rc); + else + return handler_(asio::error(error_code + 1000000), rc); + } + + if (!is_operation_done && !is_write_needed) + { + // We may have left over data that we can pass to SSL immediately + if (recv_buf_.get_data_len() > 0) + { + // Pass the buffered data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + return handler_(asio::error(asio::error::no_recovery), 0); + } + } + + return start(); + } + } + + // Continue with operation, flush any SSL data out to network... + return write_(is_operation_done, rc); + } + +// Private implementation +private: + typedef boost::function + int_handler_func; + typedef boost::function write_func; + + ssl_primitive_func primitive_; + user_handler_func user_handler_; + write_func write_; + int_handler_func handler_; + + net_buffer send_buf_; // buffers for network IO + + // The recv buffer is owned by the stream, not the operation, since there can + // be left over bytes after passing the data up to the application, and these + // bytes need to be kept around for the next read operation issued by the + // application. + net_buffer& recv_buf_; + + Stream& socket_; + BIO* ssl_bio_; + SSL* session_; + + // + int sync_user_handler(const asio::error& error, int rc) + { + if (!error) + return rc; + + throw error; + } + + int async_user_handler(const asio::error& error, int rc) + { + user_handler_(error, rc); + return 0; + } + + // Writes bytes asynchronously from SSL to NET + int do_async_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + if (len == 0) + { + // In case our send buffer is full, we have just to wait until + // previous send to complete... + return 0; + } + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + unsigned char *data_start = send_buf_.get_unused_start(); + send_buf_.data_added(len); + + asio::async_write + ( + socket_, + asio::buffer(data_start, len), + boost::bind + ( + &openssl_operation::async_write_handler, + this, + is_operation_done, + rc, + asio::placeholders::error, + asio::placeholders::bytes_transferred + ) + ); + + return 0; + } + else if (!BIO_should_retry(ssl_bio_)) + { + // Seems like fatal error + // reading from SSL BIO has failed... + handler_(asio::error(asio::error::no_recovery), 0); + return 0; + } + } + + if (is_operation_done) + { + // Finish the operation, with success + handler_(asio::error(), rc); + return 0; + } + + // OPeration is not done and writing to net has been made... + // start reading... + do_async_read(); + + return 0; + } + + void async_write_handler(bool is_operation_done, int rc, + const asio::error& error, size_t bytes_sent) + { + if (!error) + { + // Remove data from send buffer + send_buf_.data_removed(bytes_sent); + + if (is_operation_done) + handler_(asio::error(), rc); + else + // Since the operation was not completed, try it again... + start(); + } + else + handler_(error, rc); + } + + void do_async_read() + { + // Wait for new data + socket_.async_read_some + ( + asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()), + boost::bind + ( + &openssl_operation::async_read_handler, + this, + asio::placeholders::error, + asio::placeholders::bytes_transferred + ) + ); + } + + void async_read_handler(const asio::error& error, size_t bytes_recvd) + { + if (!error) + { + recv_buf_.data_added(bytes_recvd); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + handler_(asio::error(asio::error::no_recovery), 0); + return; + } + } + + // and try the SSL primitive again + start(); + } + else + { + // Error in network level... + // SSL can't continue either... + handler_(error, 0); + } + } + + // Syncronous functions... + int do_sync_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + size_t sent_len = asio::write( + socket_, + asio::buffer(send_buf_.get_unused_start(), len) + ); + + send_buf_.data_added(len); + send_buf_.data_removed(sent_len); + } + else if (!BIO_should_retry(ssl_bio_)) + { + // Seems like fatal error + // reading from SSL BIO has failed... + throw asio::error(asio::error::no_recovery); + } + } + + if (is_operation_done) + // Finish the operation, with success + return rc; + + // Operation is not finished, read data from net... + return do_sync_read(); + } + + int do_sync_read() + { + size_t len = socket_.read_some + ( + asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()) + ); + + // Write data to ssl + recv_buf_.data_added(len); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + throw asio::error(asio::error::no_recovery); + } + } + + // Try the operation again + return start(); + } +}; // class openssl_operation + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp new file mode 100644 index 000000000..d190ce937 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp @@ -0,0 +1,505 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP +#define ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/detail/openssl_operation.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_stream_service + : public asio::io_service::service +{ +private: + //Base handler for asyncrhonous operations + template + class base_handler + { + public: + typedef boost::function func_t; + + base_handler(asio::io_service& io_service) + : op_(NULL) + , io_service_(io_service) + , work_(io_service) + {} + + void do_func(const asio::error& error, size_t size) + { + func_(error, size); + } + + void set_operation(openssl_operation* op) { op_ = op; } + void set_func(func_t func) { func_ = func; } + + ~base_handler() + { + delete op_; + } + + private: + func_t func_; + openssl_operation* op_; + asio::io_service& io_service_; + asio::io_service::work work_; + }; // class base_handler + + // Handler for asynchronous IO (write/read) operations + template + class io_handler + : public base_handler + { + public: + io_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service) + , handler_(handler) + { + set_func(boost::bind( + &io_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error& error, size_t size) + { + handler_(error, size); + delete this; + } + }; // class io_handler + + // Handler for asyncrhonous handshake (connect, accept) functions + template + class handshake_handler + : public base_handler + { + public: + handshake_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service) + , handler_(handler) + { + set_func(boost::bind( + &handshake_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error& error, size_t) + { + handler_(error); + delete this; + } + + }; // class handshake_handler + + // Handler for asyncrhonous shutdown + template + class shutdown_handler + : public base_handler + { + public: + shutdown_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service), + handler_(handler) + { + set_func(boost::bind( + &shutdown_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error& error, size_t) + { + handler_(error); + delete this; + } + }; // class shutdown_handler + +public: + // The implementation type. + typedef struct impl_struct + { + ::SSL* ssl; + ::BIO* ext_bio; + net_buffer recv_buf; + } * impl_type; + + // Construct a new stream socket service for the specified io_service. + explicit openssl_stream_service(asio::io_service& io_service) + : asio::io_service::service(io_service) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Return a null stream implementation. + impl_type null() const + { + return 0; + } + + // Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + impl = new impl_struct; + impl->ssl = ::SSL_new(context.impl()); + ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + ::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + ::BIO* int_bio = 0; + impl->ext_bio = 0; + ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192); + ::SSL_set_bio(impl->ssl, int_bio, int_bio); + } + + // Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + if (impl != 0) + { + ::BIO_free(impl->ext_bio); + ::SSL_free(impl->ssl); + delete impl; + impl = 0; + } + } + + // Perform SSL handshaking. + template + void handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Error_Handler error_handler) + { + try + { + openssl_operation op( + type == stream_base::client ? + &ssl_wrap::SSL_connect: + &ssl_wrap::SSL_accept, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio); + op.start(); + } + catch (asio::error& e) + { + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Handler handler) + { + typedef handshake_handler connect_handler; + + connect_handler* local_handler = + new connect_handler(handler, io_service()); + + openssl_operation* op = new openssl_operation + ( + type == stream_base::client ? + &ssl_wrap::SSL_connect: + &ssl_wrap::SSL_accept, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Shut down SSL on the stream. + template + void shutdown(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + try + { + openssl_operation op( + &ssl_wrap::SSL_shutdown, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio); + op.start(); + } + catch (asio::error& e) + { + error_handler(e); + return; + } + + asio::error e; + error_handler(e); + } + + // Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) + { + typedef shutdown_handler disconnect_handler; + + disconnect_handler* local_handler = + new disconnect_handler(handler, io_service()); + + openssl_operation* op = new openssl_operation + ( + &ssl_wrap::SSL_shutdown, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Error_Handler error_handler) + { + size_t bytes_transferred = 0; + try + { + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + static_cast(asio::buffer_size(*buffers.begin()))); + openssl_operation op( + send_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio + ); + bytes_transferred = static_cast(op.start()); + } + catch (asio::error& e) + { + error_handler(e); + return 0; + } + + asio::error e; + error_handler(e); + return bytes_transferred; + } + + // Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Handler handler) + { + typedef io_handler send_handler; + + send_handler* local_handler = new send_handler(handler, io_service()); + + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + static_cast(asio::buffer_size(*buffers.begin()))); + + openssl_operation* op = new openssl_operation + ( + send_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + size_t bytes_transferred = 0; + try + { + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + asio::buffer_size(*buffers.begin())); + openssl_operation op(recv_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio + ); + + bytes_transferred = static_cast(op.start()); + } + catch (asio::error& e) + { + error_handler(e); + return 0; + } + + asio::error e; + error_handler(e); + return bytes_transferred; + } + + // Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Handler handler) + { + typedef io_handler recv_handler; + + recv_handler* local_handler = new recv_handler(handler, io_service()); + + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + asio::buffer_size(*buffers.begin())); + + openssl_operation* op = new openssl_operation + ( + recv_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + asio::error e; + error_handler(e); + return 0; + } + + // Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + asio::error e; + error_handler(e); + return 0; + } + +private: + typedef asio::detail::mutex mutex_type; + + template + struct ssl_wrap + { + static Mutex ssl_mutex_; + + static int SSL_accept(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_accept(ssl); + } + + static int SSL_connect(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_connect(ssl); + } + + static int SSL_shutdown(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_shutdown(ssl); + } + }; +}; + +template +Mutex openssl_stream_service::ssl_wrap::ssl_mutex_; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP diff --git a/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp b/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp new file mode 100755 index 000000000..d193b3a0c --- /dev/null +++ b/library/include/libtorrent/asio/ssl/detail/openssl_types.hpp @@ -0,0 +1,29 @@ +// +// openssl_types.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP +#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP diff --git a/library/include/libtorrent/asio/ssl/stream.hpp b/library/include/libtorrent/asio/ssl/stream.hpp new file mode 100644 index 000000000..451b02e32 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/stream.hpp @@ -0,0 +1,509 @@ +// +// stream.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_STREAM_HPP +#define ASIO_SSL_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/stream_service.hpp" + +namespace asio { +namespace ssl { + +/// Provides stream-oriented functionality using SSL. +/** + * The stream class template provides asynchronous and blocking stream-oriented + * functionality using SSL. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example: + * To use the SSL stream template with a stream_socket, you would write: + * @code + * asio::io_service io_service; + * asio::ssl::context context(io_service, asio::ssl::context::sslv23); + * asio::ssl::stream sock(io_service, context); + * @endcode + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class stream + : public stream_base, + private boost::noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type used for reporting errors. + typedef typename next_layer_type::error_type error_type; + + /// The type of the service that will be used to provide stream operations. + typedef Service service_type; + + /// The native implementation type of the stream. + typedef typename service_type::impl_type impl_type; + + /// Construct a stream. + /** + * This constructor creates a stream and initialises the underlying stream + * object. + * + * @param arg The argument to be passed to initialise the underlying stream. + * + * @param context The SSL context to be used for the stream. + */ + template + explicit stream(Arg& arg, basic_context& context) + : next_layer_(arg), + service_(asio::use_service(next_layer_.io_service())), + impl_(service_.null()) + { + service_.create(impl_, next_layer_, context); + } + + /// Destructor. + ~stream() + { + service_.destroy(impl_, next_layer_); + } + + /// Get the io_service associated with the object. + /** + * This function may be used to obtain the io_service object that the stream + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that stream will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Get a reference to the next layer. + /** + * This function returns a reference to the next layer in a stack of stream + * layers. + * + * @return A reference to the next layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to stream functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @throws asio::error Thrown on failure. + */ + void handshake(handshake_type type) + { + service_.handshake(impl_, next_layer_, type, throw_error()); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void handshake(handshake_type type, Error_Handler error_handler) + { + service_.handshake(impl_, next_layer_, type, error_handler); + } + + /// Start an asynchronous SSL handshake. + /** + * This function is used to asynchronously perform an SSL handshake on the + * stream. This function call always returns immediately. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation + * ); @endcode + */ + template + void async_handshake(handshake_type type, Handler handler) + { + service_.async_handshake(impl_, next_layer_, type, handler); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @throws asio::error Thrown on failure. + */ + void shutdown() + { + service_.shutdown(impl_, next_layer_, throw_error()); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + */ + template + void shutdown(Error_Handler error_handler) + { + service_.shutdown(impl_, next_layer_, error_handler); + } + + /// Asynchronously shut down SSL on the stream. + /** + * This function is used to asynchronously shut down SSL on the stream. This + * function call always returns immediately. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation + * ); @endcode + */ + template + void async_shutdown(Handler handler) + { + service_.async_shutdown(impl_, next_layer_, handler); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written. + * + * @returns The number of bytes written. + * + * @throws asio::error Thrown on failure. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers) + { + return service_.write_some(impl_, next_layer_, buffers, throw_error()); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written to the stream. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. Returns 0 if an error occurred and + * the error handler did not throw an exception. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const Const_Buffers& buffers, + Error_Handler error_handler) + { + return service_.write_some(impl_, next_layer_, buffers, error_handler); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write one or more bytes of data to + * the stream. The function call always returns immediately. + * + * @param buffers The data to be written to the stream. Although the buffers + * object may be copied as necessary, ownership of the underlying buffers is + * retained by the caller, which must guarantee that they remain valid until + * the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * + * @note The async_write_some operation may not transmit all of the data to + * the peer. Consider using the @ref async_write function if you need to + * ensure that all data is written before the blocking operation completes. + */ + template + void async_write_some(const Const_Buffers& buffers, Handler handler) + { + service_.async_write_some(impl_, next_layer_, buffers, handler); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::error Thrown on failure. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers) + { + return service_.read_some(impl_, next_layer_, buffers, throw_error()); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const Mutable_Buffers& buffers, + Error_Handler error_handler) + { + return service_.read_some(impl_, next_layer_, buffers, error_handler); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read one or more bytes of data from + * the stream. The function call always returns immediately. + * + * @param buffers The buffers into which the data will be read. Although the + * buffers object may be copied as necessary, ownership of the underlying + * buffers is retained by the caller, which must guarantee that they remain + * valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * + * @note The async_read_some operation may not read all of the requested + * number of bytes. Consider using the @ref async_read function if you need to + * ensure that the requested amount of data is read before the asynchronous + * operation completes. + */ + template + void async_read_some(const Mutable_Buffers& buffers, Handler handler) + { + service_.async_read_some(impl_, next_layer_, buffers, handler); + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, without + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::error Thrown on failure. + */ + template + std::size_t peek(const Mutable_Buffers& buffers) + { + return service_.peek(impl_, next_layer_, buffers, throw_error()); + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, withoutxi + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes read. Returns 0 if an error occurred and the + * error handler did not throw an exception. + */ + template + std::size_t peek(const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_.peek(impl_, next_layer_, buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @returns The number of bytes of data that can be read without blocking. + * + * @throws asio::error Thrown on failure. + */ + std::size_t in_avail() + { + return service_.in_avail(impl_, next_layer_, throw_error()); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation + * ); @endcode + * + * @returns The number of bytes of data that can be read without blocking. + */ + template + std::size_t in_avail(Error_Handler error_handler) + { + return service_.in_avail(impl_, next_layer_, error_handler); + } + +private: + /// The next layer. + Stream next_layer_; + + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_HPP diff --git a/library/include/libtorrent/asio/ssl/stream_base.hpp b/library/include/libtorrent/asio/ssl/stream_base.hpp new file mode 100755 index 000000000..89c4b65a9 --- /dev/null +++ b/library/include/libtorrent/asio/ssl/stream_base.hpp @@ -0,0 +1,60 @@ +// +// stream_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_STREAM_BASE_HPP +#define ASIO_SSL_STREAM_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace ssl { + +/// The stream_base class is used as a base for the asio::ssl::stream +/// class template so that we have a common place to define various enums. +class stream_base +{ +public: + /// Different handshake types. + enum handshake_type + { + /// Perform handshaking as a client. + client, + + /// Perform handshaking as a server. + server + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~stream_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_BASE_HPP diff --git a/library/include/libtorrent/asio/ssl/stream_service.hpp b/library/include/libtorrent/asio/ssl/stream_service.hpp new file mode 100644 index 000000000..5ad5165cc --- /dev/null +++ b/library/include/libtorrent/asio/ssl/stream_service.hpp @@ -0,0 +1,173 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SSL_STREAM_SERVICE_HPP +#define ASIO_SSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/detail/openssl_stream_service.hpp" + +namespace asio { +namespace ssl { + +/// Default service implementation for an SSL stream. +class stream_service + : public asio::io_service::service +{ +private: + // The type of the platform-specific implementation. + typedef detail::openssl_stream_service service_impl_type; + +public: + /// The type of a stream implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef service_impl_type::impl_type impl_type; +#endif + + /// Construct a new stream service for the specified io_service. + explicit stream_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Return a null stream implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + service_impl_.create(impl, next_layer, context); + } + + /// Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + service_impl_.destroy(impl, next_layer); + } + + /// Perform SSL handshaking. + template + void handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Error_Handler error_handler) + { + service_impl_.handshake(impl, next_layer, type, error_handler); + } + + /// Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Handler handler) + { + service_impl_.async_handshake(impl, next_layer, type, handler); + } + + /// Shut down SSL on the stream. + template + void shutdown(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, next_layer, error_handler); + } + + /// Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) + { + service_impl_.async_shutdown(impl, next_layer, handler); + } + + /// Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.write_some(impl, next_layer, buffers, error_handler); + } + + /// Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Handler handler) + { + service_impl_.async_write_some(impl, next_layer, buffers, handler); + } + + /// Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.read_some(impl, next_layer, buffers, error_handler); + } + + /// Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Handler handler) + { + service_impl_.async_read_some(impl, next_layer, buffers, handler); + } + + /// Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Error_Handler error_handler) + { + return service_impl_.peek(impl, next_layer, buffers, error_handler); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + Error_Handler error_handler) + { + return service_impl_.in_avail(impl, next_layer, error_handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_SERVICE_HPP diff --git a/library/include/libtorrent/asio/strand.hpp b/library/include/libtorrent/asio/strand.hpp new file mode 100644 index 000000000..e378bfd32 --- /dev/null +++ b/library/include/libtorrent/asio/strand.hpp @@ -0,0 +1,166 @@ +// +// strand.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_STRAND_HPP +#define ASIO_STRAND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/strand_service.hpp" +#include "asio/detail/wrapped_handler.hpp" + +namespace asio { + +/// Provides serialised handler execution. +/** + * The io_service::strand class provides the ability to post and dispatch + * handlers with the guarantee that none of those handlers will execute + * concurrently. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Dispatcher. + */ +class io_service::strand +{ +public: + /// Constructor. + /** + * Constructs the strand. + * + * @param io_service The io_service object that the strand will use to + * dispatch handlers that are ready to be run. + */ + explicit strand(asio::io_service& io_service) + : service_(asio::use_service< + asio::detail::strand_service>(io_service)) + { + service_.construct(impl_); + } + + /// Destructor. + ~strand() + { + service_.destroy(impl_); + } + + /// Get the io_service associated with the strand. + /** + * This function may be used to obtain the io_service object that the strand + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that the strand will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return service_.io_service(); + } + + /// Request the strand to invoke the given handler. + /** + * This function is used to ask the strand to execute the given handler. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The handler may be executed + * inside this function if the guarantee can be met. If this function is + * called from within a handler that was posted or dispatched through the same + * strand, then the new handler will be executed immediately. + * + * The strand's guarantee is in addition to the guarantee provided by the + * underlying io_service. The io_service guarantees that the handler will only + * be called in a thread in which the io_service's run member function is + * currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void dispatch(Handler handler) + { + service_.dispatch(impl_, handler); + } + + /// Request the strand to invoke the given handler and return + /// immediately. + /** + * This function is used to ask the strand to execute the given handler, but + * without allowing the strand to call the handler from inside this function. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The strand's guarantee is in + * addition to the guarantee provided by the underlying io_service. The + * io_service guarantees that the handler will only be called in a thread in + * which the io_service's run member function is currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void post(Handler handler) + { + service_.post(impl_, handler); + } + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the strand. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the strand's + * dispatch function. + * + * @param handler The handler to be wrapped. The strand will make a copy of + * the handler object as required. The function signature of the handler must + * be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the strand's dispatch function. Given a function object with the signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code strand.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler) + { + return detail::wrapped_handler(*this, handler); + } + +private: + asio::detail::strand_service& service_; + asio::detail::strand_service::implementation_type impl_; +}; + +/// Typedef for backwards compatibility. +typedef asio::io_service::strand strand; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STRAND_HPP diff --git a/library/include/libtorrent/asio/stream_socket_service.hpp b/library/include/libtorrent/asio/stream_socket_service.hpp new file mode 100644 index 000000000..bb604b764 --- /dev/null +++ b/library/include/libtorrent/asio/stream_socket_service.hpp @@ -0,0 +1,256 @@ +// +// stream_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_STREAM_SOCKET_SERVICE_HPP +#define ASIO_STREAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" +#include "asio/detail/reactive_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a stream socket. +template +class stream_socket_service + : public asio::io_service::service +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a stream socket implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new stream socket service for the specified io_service. + explicit stream_socket_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new stream socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a stream socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a stream socket. + template + void open(implementation_type& impl, const protocol_type& protocol, + Error_Handler error_handler) + { + if (protocol.type() == SOCK_STREAM) + service_impl_.open(impl, protocol, error_handler); + else + error_handler(asio::error(asio::error::invalid_argument)); + } + + /// Assign an existing native socket to a stream socket. + template + void assign(implementation_type& impl, const protocol_type& protocol, + const native_type& native_socket, Error_Handler error_handler) + { + service_impl_.assign(impl, protocol, native_socket, error_handler); + } + + /// Close a stream socket implementation. + template + void close(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.close(impl, error_handler); + } + + /// Get the native socket implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + template + void cancel(implementation_type& impl, Error_Handler error_handler) + { + service_impl_.cancel(impl, error_handler); + } + + /// Bind the stream socket to the specified local endpoint. + template + void bind(implementation_type& impl, const endpoint_type& endpoint, + Error_Handler error_handler) + { + service_impl_.bind(impl, endpoint, error_handler); + } + + /// Connect the stream socket to the specified endpoint. + template + void connect(implementation_type& impl, const endpoint_type& peer_endpoint, + Error_Handler error_handler) + { + service_impl_.connect(impl, peer_endpoint, error_handler); + } + + /// Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + void set_option(implementation_type& impl, const Option& option, + Error_Handler error_handler) + { + service_impl_.set_option(impl, option, error_handler); + } + + /// Get a socket option. + template + void get_option(const implementation_type& impl, Option& option, + Error_Handler error_handler) const + { + service_impl_.get_option(impl, option, error_handler); + } + + /// Perform an IO control command on the socket. + template + void io_control(implementation_type& impl, IO_Control_Command& command, + Error_Handler error_handler) + { + service_impl_.io_control(impl, command, error_handler); + } + + /// Get the local endpoint. + template + endpoint_type local_endpoint(const implementation_type& impl, + Error_Handler error_handler) const + { + endpoint_type endpoint; + service_impl_.get_local_endpoint(impl, endpoint, error_handler); + return endpoint; + } + + /// Get the remote endpoint. + template + endpoint_type remote_endpoint(const implementation_type& impl, + Error_Handler error_handler) const + { + endpoint_type endpoint; + service_impl_.get_remote_endpoint(impl, endpoint, error_handler); + return endpoint; + } + + /// Disable sends or receives on the socket. + template + void shutdown(implementation_type& impl, socket_base::shutdown_type what, + Error_Handler error_handler) + { + service_impl_.shutdown(impl, what, error_handler); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.send(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous send. + template + void async_send(implementation_type& impl, const Const_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Error_Handler error_handler) + { + return service_impl_.receive(impl, buffers, flags, error_handler); + } + + /// Start an asynchronous receive. + template + void async_receive(implementation_type& impl, const Mutable_Buffers& buffers, + socket_base::message_flags flags, Handler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STREAM_SOCKET_SERVICE_HPP diff --git a/library/include/libtorrent/asio/streambuf.hpp b/library/include/libtorrent/asio/streambuf.hpp new file mode 100644 index 000000000..d20c957da --- /dev/null +++ b/library/include/libtorrent/asio/streambuf.hpp @@ -0,0 +1,31 @@ +// +// streambuf.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_STREAMBUF_HPP +#define ASIO_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/// Typedef for the typical usage of basic_streambuf. +typedef basic_streambuf<> streambuf; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STREAMBUF_HPP diff --git a/library/include/libtorrent/asio/system_exception.hpp b/library/include/libtorrent/asio/system_exception.hpp new file mode 100644 index 000000000..599e22712 --- /dev/null +++ b/library/include/libtorrent/asio/system_exception.hpp @@ -0,0 +1,198 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_SYSTEM_EXCEPTION_HPP +#define ASIO_SYSTEM_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/win_local_free_on_block_exit.hpp" + +namespace asio { + +/// The system_exception class is used to represent system conditions that +/// prevent the library from operating correctly. +class system_exception + : public std::exception +{ +public: + /// Construct with a specific context and error code. + system_exception(const std::string& context, int code) + : context_(context), + code_(code) + { + } + + /// Copy constructor. + system_exception(const system_exception& e) + : std::exception(e), + context_(e.context_), + code_(e.code_) + { + } + + /// Destructor. + virtual ~system_exception() throw () + { + } + + /// Assignment operator. + system_exception& operator=(const system_exception& e) + { + context_ = e.context_; + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + try + { + if (!what_) + { + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::win_local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + { + std::string tmp(context_); + tmp += ": "; + tmp += msg; + what_.reset(new std::string(tmp)); + } + else + { + return "asio system_exception"; + } + } + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#elif defined(__sun) || defined(__QNX__) + return strerror(code_); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + try + { + char buf[256] = ""; + strerror_r(code_, buf, sizeof(buf)); + std::string tmp(context_); + tmp += ": "; + tmp += buf; + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#else + try + { + char buf[256] = ""; + std::string tmp(context_); + tmp += ": "; + tmp += strerror_r(code_, buf, sizeof(buf)); + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#endif + } + + /// Get the implementation-defined context associated with the exception. + const std::string& context() const + { + return context_; + } + + /// Get the implementation-defined code associated with the exception. + int code() const + { + return code_; + } + +private: + // The context associated with the error. + std::string context_; + + // The code associated with the error. + int code_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +/// Output the string associated with a system exception. +/** + * Used to output a human-readable string that is associated with a system + * exception. + * + * @param os The output stream to which the string will be written. + * + * @param e The exception to be written. + * + * @return The output stream. + * + * @relates asio::system_exception + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SYSTEM_EXCEPTION_HPP diff --git a/library/include/libtorrent/asio/thread.hpp b/library/include/libtorrent/asio/thread.hpp new file mode 100644 index 000000000..72b2510ca --- /dev/null +++ b/library/include/libtorrent/asio/thread.hpp @@ -0,0 +1,91 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_THREAD_HPP +#define ASIO_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { + +/// A simple abstraction for starting threads. +/** + * The asio::thread class implements the smallest possible subset of the + * functionality of boost::thread. It is intended to be used only for starting + * a thread and waiting for it to exit. If more extensive threading + * capabilities are required, you are strongly advised to use something else. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example: + * A typical use of asio::thread would be to launch a thread to run an + * io_service's event processing loop: + * + * @par + * @code asio::io_service io_service; + * // ... + * asio::thread t(boost::bind(&asio::io_service::run, &io_service)); + * // ... + * t.join(); @endcode + */ +class thread + : private noncopyable +{ +public: + /// Start a new thread that executes the supplied function. + /** + * This constructor creates a new thread that will execute the given function + * or function object. + * + * @param f The function or function object to be run in the thread. The + * function signature must be: @code void f(); @endcode + */ + template + explicit thread(Function f) + : impl_(f) + { + } + + /// Destructor. + ~thread() + { + } + + /// Wait for the thread to exit. + /** + * This function will block until the thread has exited. + * + * If this function is not called before the thread object is destroyed, the + * thread itself will continue to run until completion. You will, however, + * no longer have the ability to wait for it to exit. + */ + void join() + { + impl_.join(); + } + +private: + detail::thread impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_THREAD_HPP diff --git a/library/include/libtorrent/asio/time_traits.hpp b/library/include/libtorrent/asio/time_traits.hpp new file mode 100644 index 000000000..7b459944c --- /dev/null +++ b/library/include/libtorrent/asio/time_traits.hpp @@ -0,0 +1,78 @@ +// +// time_traits.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_TIME_TRAITS_HPP +#define ASIO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +/// Time traits suitable for use with the deadline timer. +template +struct time_traits; + +/// Time traits specialised for posix_time. +template <> +struct time_traits +{ + /// The time type. + typedef boost::posix_time::ptime time_type; + + /// The duration type. + typedef boost::posix_time::time_duration duration_type; + + /// Get the current time. + static time_type now() + { + return boost::posix_time::microsec_clock::universal_time(); + } + + /// Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + return t + d; + } + + /// Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + return t1 - t2; + } + + /// Test whether one time is less than another. + static bool less_than(const time_type& t1, const time_type& t2) + { + return t1 < t2; + } + + /// Convert to POSIX duration type. + static boost::posix_time::time_duration to_posix_duration( + const duration_type& d) + { + return d; + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_TIME_TRAITS_HPP diff --git a/library/include/libtorrent/asio/write.hpp b/library/include/libtorrent/asio/write.hpp new file mode 100644 index 000000000..7bebecf49 --- /dev/null +++ b/library/include/libtorrent/asio/write.hpp @@ -0,0 +1,543 @@ +// +// write.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under 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 ASIO_WRITE_HPP +#define ASIO_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/** + * @defgroup write asio::write + */ +/*@{*/ + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all(), + * asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * completion_condition, + * asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Write_Stream::error_type& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. If an error occurs, and the error + * handler does not throw an exception, returns the total number of bytes + * successfully transferred prior to the error. + */ +template +std::size_t write(Sync_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Error_Handler error_handler); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, b, + * asio::transfer_all(), + * asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, basic_streambuf& b); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws Sync_Write_Stream::error_type Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, b, + * completion_condition, + * asio::throw_error()); @endcode + */ +template +std::size_t write(Sync_Write_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Sync_Write_Stream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Sync_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const Sync_Write_Stream::error_type& error // Result of operation. + * ); @endcode + * + * @returns The number of bytes written. If an error occurs, and the error + * handler does not throw an exception, returns the total number of bytes + * successfully transferred prior to the error. + */ +template +std::size_t write(Sync_Write_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition, Error_Handler error_handler); + +/*@}*/ +/** + * @defgroup async_write asio::async_write + */ +/*@{*/ + +/// Start an asynchronous operation to write of all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_write(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Handler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's async_write_some function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example: + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::async_write(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(Async_Write_Stream& s, const Const_Buffers& buffers, + Completion_Condition completion_condition, Handler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write(Async_Write_Stream& s, basic_streambuf& b, + Handler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the Async_Write_Stream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const Async_Write_Stream::error_type& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's async_write_some function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const Async_Write_Stream::error_type& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written + * // from the buffers. If an + * // error occurred, this will + * // be less than the sum of the + * // buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write(Async_Write_Stream& s, basic_streambuf& b, + Completion_Condition completion_condition, Handler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/write.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_HPP diff --git a/library/include/libtorrent/aux_/allocate_resources_impl.hpp b/library/include/libtorrent/aux_/allocate_resources_impl.hpp new file mode 100644 index 000000000..4be3d8a1e --- /dev/null +++ b/library/include/libtorrent/aux_/allocate_resources_impl.hpp @@ -0,0 +1,239 @@ +/* + +Copyright (c) 2003, 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. + +*/ + +#ifndef TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED +#define TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED + +#include +#include + +#include + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/size_type.hpp" + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +namespace libtorrent +{ + + int saturated_add(int a, int b); + + namespace aux + { + // give num_resources to r, + // return how how many were actually accepted. + inline int give(resource_request& r, int num_resources) + { + assert(num_resources >= 0); + assert(r.given <= r.max); + + int accepted = (std::min)(num_resources, r.max - r.given); + assert(accepted >= 0); + + r.given += accepted; + assert(r.given <= r.max); + + return accepted; + } + +#ifndef NDEBUG + + template + class allocate_resources_contract_check + { + int m_resources; + It m_start; + It m_end; + resource_request T::* m_res; + + public: + allocate_resources_contract_check( + int resources + , It start + , It end + , resource_request T::* res) + : m_resources(resources) + , m_start(start) + , m_end(end) + , m_res(res) + { + assert(m_resources >= 0); + for (It i = m_start, end(m_end); i != end; ++i) + { + assert(((*i).*m_res).max >= 0); + assert(((*i).*m_res).given >= 0); + } + } + + ~allocate_resources_contract_check() + { + int sum_given = 0; + int sum_max = 0; + int sum_min = 0; + for (It i = m_start, end(m_end); i != end; ++i) + { + assert(((*i).*m_res).max >= 0); + assert(((*i).*m_res).min >= 0); + assert(((*i).*m_res).max >= ((*i).*m_res).min); + assert(((*i).*m_res).given >= 0); + assert(((*i).*m_res).given <= ((*i).*m_res).max); + + sum_given = saturated_add(sum_given, ((*i).*m_res).given); + sum_max = saturated_add(sum_max, ((*i).*m_res).max); + sum_min = saturated_add(sum_min, ((*i).*m_res).min); + } + assert(sum_given == (std::min)(std::max(m_resources, sum_min), sum_max)); + } + }; + +#endif + + template + void allocate_resources_impl( + int resources + , It start + , It end + , resource_request T::* res) + { + assert(resources >= 0); + #ifndef NDEBUG + allocate_resources_contract_check contract_check( + resources + , start + , end + , res); + #endif + + if (resources == resource_request::inf) + { + // No competition for resources. + // Just give everyone what they want. + for (It i = start; i != end; ++i) + { + ((*i).*res).given = ((*i).*res).max; + } + return; + } + + // Resources are scarce + + int sum_max = 0; + int sum_min = 0; + for (It i = start; i != end; ++i) + { + sum_max = saturated_add(sum_max, ((*i).*res).max); + assert(((*i).*res).min < resource_request::inf); + assert(((*i).*res).min >= 0); + assert(((*i).*res).min <= ((*i).*res).max); + sum_min += ((*i).*res).min; + ((*i).*res).given = ((*i).*res).min; + } + + if (resources == 0 || sum_max == 0) + return; + + resources = (std::max)(resources, sum_min); + int resources_to_distribute = (std::min)(resources, sum_max) - sum_min; + assert(resources_to_distribute >= 0); +#ifndef NDEBUG + int prev_resources_to_distribute = resources_to_distribute; +#endif + while (resources_to_distribute > 0) + { + size_type total_used = 0; + size_type max_used = 0; + for (It i = start; i != end; ++i) + { + resource_request& r = (*i).*res; + if(r.given == r.max) continue; + + assert(r.given < r.max); + + max_used = (std::max)(max_used, (size_type)r.used + 1); + total_used += (size_type)r.used + 1; + } + + size_type kNumer = resources_to_distribute; + size_type kDenom = total_used; + assert(kNumer >= 0); + assert(kDenom >= 0); + assert(kNumer <= (std::numeric_limits::max)()); + assert(total_used < (std::numeric_limits::max)()); + + if (kNumer * max_used <= kDenom) + { + kNumer = 1; + kDenom = max_used; + assert(kDenom >= 0); + assert(kDenom <= (std::numeric_limits::max)()); + } + + for (It i = start; i != end && resources_to_distribute > 0; ++i) + { + resource_request& r = (*i).*res; + if (r.given == r.max) continue; + + assert(r.given < r.max); + + size_type used = (size_type)r.used + 1; + if (used < 1) used = 1; + size_type to_give = used * kNumer / kDenom; + if (to_give > resources_to_distribute) + to_give = resources_to_distribute; + assert(to_give >= 0); + assert(to_give <= resources_to_distribute); + resources_to_distribute -= give(r, (int)to_give); + assert(resources_to_distribute >= 0); + } + + assert(resources_to_distribute >= 0); + assert(resources_to_distribute < prev_resources_to_distribute); +#ifndef NDEBUG + prev_resources_to_distribute = resources_to_distribute; +#endif + } + } + + } // namespace libtorrent::aux +} + + +#endif diff --git a/library/include/libtorrent/aux_/session_impl.hpp b/library/include/libtorrent/aux_/session_impl.hpp new file mode 100644 index 000000000..2712e7078 --- /dev/null +++ b/library/include/libtorrent/aux_/session_impl.hpp @@ -0,0 +1,382 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_IMPL_HPP_INCLUDED +#define TORRENT_SESSION_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include +#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/peer_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/debug.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/stat.hpp" + +namespace libtorrent +{ + + namespace aux + { + struct session_impl; + + // this data is shared between the main thread and the + // thread that initialize pieces + struct piece_checker_data + { + piece_checker_data() + : processing(false), progress(0.f), abort(false) {} + + boost::shared_ptr torrent_ptr; + boost::filesystem::path save_path; + + sha1_hash info_hash; + + void parse_resume_data( + const entry& rd + , const torrent_info& info + , std::string& error); + + std::vector piece_map; + std::vector unfinished_pieces; + std::vector peers; + entry resume_data; + + // this is true if this torrent is being processed (checked) + // if it is not being processed, then it can be removed from + // the queue without problems, otherwise the abort flag has + // to be set. + bool processing; + + // is filled in by storage::initialize_pieces() + // and represents the progress. It should be a + // value in the range [0, 1] + float progress; + + // abort defaults to false and is typically + // filled in by torrent_handle when the user + // aborts the torrent + bool abort; + }; + + struct checker_impl: boost::noncopyable + { + checker_impl(session_impl& s): m_ses(s), m_abort(false) {} + void operator()(); + piece_checker_data* find_torrent(const sha1_hash& info_hash); + void remove_torrent(sha1_hash const& info_hash); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + // when the files has been checked + // the torrent is added to the session + session_impl& m_ses; + + mutable boost::mutex m_mutex; + boost::condition m_cond; + + // a list of all torrents that are currently in queue + // or checking their files + std::deque > m_torrents; + std::deque > m_processing; + + bool m_abort; + }; + + // this is the link between the main thread and the + // thread started to run the main downloader loop + struct session_impl: boost::noncopyable + { +#ifndef NDEBUG + friend class ::libtorrent::peer_connection; +#endif + friend class checker_impl; + friend class invariant_access; + typedef std::map + , boost::intrusive_ptr > + connection_map; + typedef std::map > torrent_map; + typedef std::deque > + connection_queue; + + session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface = "0.0.0.0"); + ~session_impl(); + + void operator()(); + + void open_listen_port(); + + void async_accept(); + void on_incoming_connection(boost::shared_ptr const& s + , boost::weak_ptr const& as, asio::error const& e); + + // must be locked to access the data + // in this struct + typedef boost::recursive_mutex mutex_t; + mutable mutex_t m_mutex; + + boost::weak_ptr find_torrent(const sha1_hash& info_hash); + peer_id const& get_peer_id() const { return m_peer_id; } + + // this will see if there are any pending connection attempts + // and in that case initiate new connections until the limit + // is reached. + void process_connection_queue(); + + void close_connection(boost::intrusive_ptr const& p); + void connection_completed(boost::intrusive_ptr const& p); + void connection_failed(boost::shared_ptr const& s + , tcp::endpoint const& a, char const* message); + + void set_settings(session_settings const& s); + session_settings const& settings() const { return m_settings; } + +#ifndef TORRENT_DISABLE_DHT + void add_dht_node(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& kad_settings() const { return m_dht_settings; } + void start_dht(entry const& startup_state); + void stop_dht(); + entry dht_state() const; +#endif + bool is_aborted() const { return m_abort; } + + void set_ip_filter(ip_filter const& f); + + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0); + bool is_listening() const; + + torrent_handle add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size); + + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size); + + void remove_torrent(torrent_handle const& h); + + void disable_extensions(); + void enable_extension(extension_index i); + bool extensions_enabled() const; + bool extension_enabled(int i) const + { return m_extension_enabled[i]; } + + std::vector get_torrents(); + + void set_severity_level(alert::severity_t s); + std::auto_ptr pop_alert(); + 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); + + + session_status status() const; + void set_peer_id(peer_id const& id); + void set_key(int key); + unsigned short listen_port() const; + + void abort(); + + // handles delayed alerts + alert_manager m_alerts; + +// private: + + // this is where all active sockets are stored. + // the selector can sleep while there's no activity on + // them + demuxer m_selector; + + tracker_manager m_tracker_manager; + torrent_map m_torrents; + + // this maps sockets to their peer_connection + // object. It is the complete list of all connected + // peers. + connection_map m_connections; + + // this is a list of half-open tcp connections + // (only outgoing connections) + connection_map m_half_open; + + // this is a queue of pending outgoing connections. If the + // list of half-open connections is full (given the global + // limit), new outgoing connections are put on this queue, + // waiting for one slot in the half-open queue to open up. + connection_queue m_connection_queue; + + // filters incoming connections + ip_filter m_ip_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 range of ports we try to listen on + std::pair m_listen_port_range; + + // 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; + + boost::shared_ptr m_listen_socket; + + // the entries in this array maps the + // extension index (as specified in peer_connection) + bool m_extension_enabled[num_supported_extensions]; + + // the settings for the client + session_settings m_settings; + + // set to true when the session object + // is being destructed and the thread + // should exit + volatile bool m_abort; + + // maximum upload rate given in + // bytes per second. -1 means + // unlimited + int m_upload_rate; + int m_download_rate; + int m_max_uploads; + int m_max_connections; + // the number of simultaneous half-open tcp + // connections libtorrent will have. + int m_half_open_limit; + + // statistics gathered from all torrents. + stat m_stat; + + // 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; + + // does the actual disconnections + // that are queued up in m_disconnect_peer + void second_tick(asio::error const& e); + boost::posix_time::ptime m_last_tick; + +#ifndef TORRENT_DISABLE_DHT + boost::scoped_ptr m_dht; + dht_settings m_dht_settings; +#endif + // the timer used to fire the second_tick + deadline_timer m_timer; +#ifndef NDEBUG + void check_invariant(const char *place = 0); +#endif +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr create_log(std::string const& name, bool append = true); + public: + boost::shared_ptr m_logger; + private: +#endif + + // data shared between the main thread + // and the checker thread + checker_impl m_checker_impl; + + // the main working thread + boost::scoped_ptr m_thread; + + // the thread that calls initialize_pieces() + // on all torrents before they start downloading + boost::scoped_ptr m_checker_thread; + }; + } +} + + +#endif + diff --git a/library/include/libtorrent/bencode.hpp b/library/include/libtorrent/bencode.hpp new file mode 100755 index 000000000..a142b5864 --- /dev/null +++ b/library/include/libtorrent/bencode.hpp @@ -0,0 +1,299 @@ +/* + +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 +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" + +#if defined(_MSC_VER) +namespace std +{ + using ::isdigit; + using ::atoi; +}; + +#define for if (false) {} else for +#endif + + +namespace libtorrent +{ + + struct TORRENT_EXPORT invalid_encoding: std::exception + { + virtual const char* what() const throw() { return "invalid bencoding"; } + }; + + namespace detail + { + template + void write_string(OutIt& out, const std::string& val) + { + std::string::const_iterator end = val.begin() + val.length(); + std::copy(val.begin(), end, out); + } + + TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val); + + template + void 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]; + for (char const* str = integer_to_str(buf, 21, val); + *str != 0; ++str) + { + *out = *str; + ++out; + } + } + + template + void write_char(OutIt& out, char c) + { + *out = c; + ++out; + } + + template + std::string read_until(InIt& in, InIt end, char end_token) + { + if (in == end) throw invalid_encoding(); + std::string ret; + while (*in != end_token) + { + ret += *in; + ++in; + if (in == end) throw invalid_encoding(); + } + return ret; + } + + template + void read_string(InIt& in, InIt end, int len, std::string& str) + { + assert(len >= 0); + for (int i = 0; i < len; ++i) + { + if (in == end) throw invalid_encoding(); + str += *in; + ++in; + } + } + + template + void bencode_recursive(OutIt& out, const entry& e) + { + switch(e.type()) + { + case entry::int_t: + write_char(out, 'i'); + write_integer(out, e.integer()); + write_char(out, 'e'); + break; + case entry::string_t: + write_integer(out, e.string().length()); + write_char(out, ':'); + write_string(out, e.string()); + break; + case entry::list_t: + write_char(out, 'l'); + for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i) + bencode_recursive(out, *i); + write_char(out, 'e'); + 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 + write_integer(out, i->first.length()); + write_char(out, ':'); + write_string(out, i->first); + // write value + bencode_recursive(out, i->second); + } + write_char(out, 'e'); + break; + default: + // do nothing + break; + } + } + + template + void bdecode_recursive(InIt& in, InIt end, entry& ret) + { + if (in == end) throw invalid_encoding(); + switch (*in) + { + + // ---------------------------------------------- + // integer + case 'i': + { + ++in; // 'i' + std::string val = read_until(in, end, 'e'); + assert(*in == 'e'); + ++in; // 'e' + ret = entry(entry::int_t); + ret.integer() = boost::lexical_cast(val); + } 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); + if (in == end) throw invalid_encoding(); + } + 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); + entry& e = ret[key.string()]; + bdecode_recursive(in, end, e); + if (in == end) throw invalid_encoding(); + } + assert(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // string + default: + if (isdigit((unsigned char)*in)) + { + std::string len_s = read_until(in, end, ':'); + assert(*in == ':'); + ++in; // ':' + int len = std::atoi(len_s.c_str()); + ret = entry(entry::string_t); + read_string(in, end, len, ret.string()); + } + else + { + throw invalid_encoding(); + } + } + } + } + + template + void bencode(OutIt out, const entry& e) + { + detail::bencode_recursive(out, e); + } + + template + entry bdecode(InIt start, InIt end) + { + try + { + entry e; + detail::bdecode_recursive(start, end, e); + return e; + } + catch(type_error&) + { + throw invalid_encoding(); + } + } + +} + +#endif // TORRENT_BENCODE_HPP_INCLUDED diff --git a/library/include/libtorrent/bt_peer_connection.hpp b/library/include/libtorrent/bt_peer_connection.hpp new file mode 100755 index 000000000..d02614c52 --- /dev/null +++ b/library/include/libtorrent/bt_peer_connection.hpp @@ -0,0 +1,295 @@ +/* + +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_BT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_BT_PEER_CONNECTION_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/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.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); + + // 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); + + ~bt_peer_connection(); + + // called from the main loop when this connection has any + // work to do. + + void on_sent(asio::error const& error + , std::size_t bytes_transferred); + void on_receive(asio::error const& error + , std::size_t bytes_transferred); + + virtual void get_peer_info(peer_info& p) const; + + bool support_extensions() const { return m_supports_extensions; } + + bool supports_extension(extension_index ex) const + { return m_extension_messages[ex] > 0; } + + bool has_metadata() const; + + // 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); + void on_dht_port(int received); + + void on_extended(int received); + + void on_extended_handshake(); + void on_chat(); + void on_metadata(); + void on_peer_exchange(); + + 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(std::vector const& bitfield); + void write_have(int index); + void write_piece(peer_request const& r); + void write_handshake(); + void write_extensions(); + void write_chat_message(const std::string& msg); + void write_metadata(std::pair req); + void write_metadata_request(std::pair req); + void write_keepalive(); + void write_dht_port(int listen_port); + void on_connected() {} + void on_tick(); + +#ifndef NDEBUG + void check_invariant() const; + boost::posix_time::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; + + // if we don't have all metadata + // this function will request a part of it + // from this peer + void request_metadata(); + + enum state + { + read_protocol_length = 0, + read_protocol_string, + read_info_hash, + read_peer_id, + + read_packet_size, + read_packet + }; + + std::string m_client_version; + + state m_state; + + // the timeout in seconds + int m_timeout; + + 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, + msg_dht_port, + // extension protocol message + msg_extended = 20, + + num_supported_messages + }; + + 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) + { + assert(s >= 0); + assert(l > 0); + } + int start; + int length; + }; + static bool range_below_zero(const range& r) + { return r.start < 0; } + std::deque m_payloads; + + // this is set to true if the handshake from + // the peer indicated that it supports the + // extension protocol + bool m_supports_extensions; + bool m_supports_dht_port; + + static const char* extension_names[num_supported_extensions]; + // contains the indices of the extension messages for each extension + // supported by the other end. A value of <= 0 means that the extension + // is not supported. + int m_extension_messages[num_supported_extensions]; + + // this is set to the current time each time we get a + // "I don't have metadata" message. + boost::posix_time::ptime m_no_metadata; + + // this is set to the time when we last sent + // a request for metadata to this peer + boost::posix_time::ptime m_metadata_request; + + // 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; + + // if we're waiting for a metadata request + // this was the request we sent + std::pair m_last_metadata_request; + + // 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; + +#ifndef NDEBUG + bool m_in_constructor; +#endif + }; +} + +#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/library/include/libtorrent/buffer.hpp b/library/include/libtorrent/buffer.hpp new file mode 100644 index 000000000..5dc2e558a --- /dev/null +++ b/library/include/libtorrent/buffer.hpp @@ -0,0 +1,447 @@ +/* +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. + +*/ + +#ifndef LIBTORRENT_BUFFER_HPP +#define LIBTORRENT_BUFFER_HPP + +//#define TORRENT_BUFFER_DEBUG + +#include "libtorrent/invariant_check.hpp" +#include + +namespace libtorrent { + +class buffer +{ +public: + struct interval + { + interval(char* begin, char* end) + : begin(begin) + , end(end) + {} + + char operator[](int index) const + { + assert(begin + index < end); + return begin[index]; + } + + int left() const { assert(end > begin); return end - begin; } + + char* begin; + char* end; + }; + + struct const_interval + { + const_interval(char const* begin, char const* end) + : begin(begin) + , end(end) + {} + + char operator[](int index) const + { + assert(begin + index < end); + return begin[index]; + } + + int left() const { assert(end > begin); return end - begin; } + + char const* begin; + char const* end; + }; + + typedef std::pair interval_type; + + buffer(std::size_t n = 0); + ~buffer(); + + interval allocate(std::size_t n); + void insert(char const* first, char const* last); + void erase(std::size_t n); + std::size_t size() const; + std::size_t capacity() const; + void reserve(std::size_t n); + interval_type data() const; + bool empty() const; + + std::size_t space_left() const; + + char const* raw_data() const + { + return m_first; + } + +#ifndef NDEBUG + void check_invariant() const; +#endif + +private: + char* m_first; + char* m_last; + char* m_write_cursor; + char* m_read_cursor; + char* m_read_end; + bool m_empty; +#ifdef TORRENT_BUFFER_DEBUG + mutable std::vector m_debug; + mutable int m_pending_copy; +#endif +}; + +inline buffer::buffer(std::size_t n) + : m_first((char*)::operator new(n)) + , m_last(m_first + n) + , m_write_cursor(m_first) + , m_read_cursor(m_first) + , m_read_end(m_last) + , m_empty(true) +{ +#ifdef TORRENT_BUFFER_DEBUG + m_pending_copy = 0; +#endif +} + +inline buffer::~buffer() +{ + ::operator delete (m_first); +} + +inline buffer::interval buffer::allocate(std::size_t n) +{ + assert(m_read_cursor <= m_read_end || m_empty); + + INVARIANT_CHECK; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } + m_debug.resize(m_debug.size() + n); + m_pending_copy = n; +#endif + if (m_read_cursor < m_write_cursor || m_empty) + { + // ..R***W.. + if (m_last - m_write_cursor >= (std::ptrdiff_t)n) + { + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + m_read_end = m_write_cursor; + assert(m_read_cursor <= m_read_end); + if (n) m_empty = false; + return ret; + } + + if (m_read_cursor - m_first >= (std::ptrdiff_t)n) + { + m_read_end = m_write_cursor; + interval ret(m_first, m_first + n); + m_write_cursor = m_first + n; + assert(m_read_cursor <= m_read_end); + if (n) m_empty = false; + return ret; + } + + reserve(capacity() + n - (m_last - m_write_cursor)); + assert(m_last - m_write_cursor >= (std::ptrdiff_t)n); + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + m_read_end = m_write_cursor; + if (n) m_empty = false; + assert(m_read_cursor <= m_read_end); + return ret; + + } + //**W...R** + if (m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n) + { + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + if (n) m_empty = false; + return ret; + } + reserve(capacity() + n - (m_read_cursor - m_write_cursor)); + assert(m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n); + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + if (n) m_empty = false; + return ret; +} + +inline void buffer::insert(char const* first, char const* last) +{ + INVARIANT_CHECK; + + std::size_t n = last - first; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } + m_debug.insert(m_debug.end(), first, last); +#endif + + if (space_left() < n) + { + reserve(capacity() + n); + } + + m_empty = false; + + char const* end = (m_last - m_write_cursor) < (std::ptrdiff_t)n ? + m_last : m_write_cursor + n; + + std::size_t copied = end - m_write_cursor; + std::memcpy(m_write_cursor, first, copied); + + m_write_cursor += copied; + if (m_write_cursor > m_read_end) m_read_end = m_write_cursor; + first += copied; + n -= copied; + + if (n == 0) return; + + assert(m_write_cursor == m_last); + m_write_cursor = m_first; + + memcpy(m_write_cursor, first, n); + m_write_cursor += n; +} + +inline void buffer::erase(std::size_t n) +{ + INVARIANT_CHECK; + + if (n == 0) return; + assert(!m_empty); + +#ifndef NDEBUG + int prev_size = size(); +#endif + assert(m_read_cursor <= m_read_end); + m_read_cursor += n; + if (m_read_cursor > m_read_end) + { + m_read_cursor = m_first + (m_read_cursor - m_read_end); + assert(m_read_cursor <= m_write_cursor); + } + + m_empty = m_read_cursor == m_write_cursor; + + assert(prev_size - n == size()); + +#ifdef TORRENT_BUFFER_DEBUG + m_debug.erase(m_debug.begin(), m_debug.begin() + n); +#endif +} + +inline std::size_t buffer::size() const +{ + // ...R***W. + if (m_read_cursor < m_write_cursor) + { + return m_write_cursor - m_read_cursor; + } + // ***W..R* + else + { + if (m_empty) return 0; + return (m_write_cursor - m_first) + (m_read_end - m_read_cursor); + } +} + +inline std::size_t buffer::capacity() const +{ + return m_last - m_first; +} + +inline void buffer::reserve(std::size_t size) +{ + std::size_t n = (std::size_t)(capacity() * 1.f); + if (n < size) n = size; + + char* buf = (char*)::operator new(n); + char* old = m_first; + + if (m_read_cursor < m_write_cursor) + { + // ...R***W.<>. + std::memcpy( + buf + (m_read_cursor - m_first) + , m_read_cursor + , m_write_cursor - m_read_cursor + ); + + m_write_cursor = buf + (m_write_cursor - m_first); + m_read_cursor = buf + (m_read_cursor - m_first); + m_read_end = m_write_cursor; + m_first = buf; + m_last = buf + n; + } + else + { + // **W..<>.R** + std::size_t skip = n - (m_last - m_first); + + std::memcpy(buf, m_first, m_write_cursor - m_first); + std::memcpy( + buf + (m_read_cursor - m_first) + skip + , m_read_cursor + , m_last - m_read_cursor + ); + + m_write_cursor = buf + (m_write_cursor - m_first); + + if (!m_empty) + { + m_read_cursor = buf + (m_read_cursor - m_first) + skip; + m_read_end = buf + (m_read_end - m_first) + skip; + } + else + { + m_read_cursor = m_write_cursor; + m_read_end = m_write_cursor; + } + + m_first = buf; + m_last = buf + n; + } + + ::operator delete (old); +} + +#ifndef NDEBUG +inline void buffer::check_invariant() const +{ + assert(m_read_end >= m_read_cursor); + assert(m_last >= m_read_cursor); + assert(m_last >= m_write_cursor); + assert(m_last >= m_first); + assert(m_first <= m_read_cursor); + assert(m_first <= m_write_cursor); +#ifdef TORRENT_BUFFER_DEBUG + int a = m_debug.size(); + int b = size(); + (void)a; + (void)b; + assert(m_debug.size() == size()); +#endif +} +#endif + +inline buffer::interval_type buffer::data() const +{ + INVARIANT_CHECK; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } +#endif + + // ...R***W. + if (m_read_cursor < m_write_cursor) + { +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.end(), m_read_cursor)); +#endif + return interval_type( + const_interval(m_read_cursor, m_write_cursor) + , const_interval(m_last, m_last) + ); + } + // **W...R** + else + { + if (m_read_cursor == m_read_end) + { +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.end(), m_first)); +#endif + + return interval_type( + const_interval(m_first, m_write_cursor) + , const_interval(m_last, m_last)); + } +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.begin() + (m_read_end + - m_read_cursor), m_read_cursor)); + assert(std::equal(m_debug.begin() + (m_read_end - m_read_cursor), m_debug.end() + , m_first)); +#endif + + assert(m_read_cursor <= m_read_end || m_empty); + return interval_type( + const_interval(m_read_cursor, m_read_end) + , const_interval(m_first, m_write_cursor) + ); + } +} + +inline bool buffer::empty() const +{ + return m_empty; +} + +inline std::size_t buffer::space_left() const +{ + if (m_empty) return m_last - m_first; + + // ...R***W. + if (m_read_cursor < m_write_cursor) + { + return (m_last - m_write_cursor) + (m_read_cursor - m_first); + } + // ***W..R* + else + { + return m_read_cursor - m_write_cursor; + } +} + +} + +#endif // LIBTORRENT_BUFFER_HPP + diff --git a/library/include/libtorrent/config.hpp b/library/include/libtorrent/config.hpp new file mode 100755 index 000000000..c8d86955e --- /dev/null +++ b/library/include/libtorrent/config.hpp @@ -0,0 +1,66 @@ +/* + +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 + +#if defined(__GNUC__) && __GNUC__ >= 4 + +# if defined(TORRENT_BUILDING_SHARED) || defined(TORRENT_LINKING_SHARED) +# define TORRENT_EXPORT __attribute__ ((visibility("default"))) +# else +# define TORRENT_EXPORT +# endif + +#elif defined(__GNUC__) + +# define TORRENT_EXPORT + +#elif defined(BOOST_MSVC) + +# if defined(TORRENT_BUILDING_SHARED) +# define TORRENT_EXPORT __declspec(dllexport) +# elif defined(TORRENT_LINKING_SHARED) +# define TORRENT_EXPORT __declspec(dllimport) +# else +# define TORRENT_EXPORT +# endif + +#else +# define TORRENT_EXPORT +#endif + + +#endif // TORRENT_CONFIG_HPP_INCLUDED + diff --git a/library/include/libtorrent/debug.hpp b/library/include/libtorrent/debug.hpp new file mode 100755 index 000000000..8aeeeb544 --- /dev/null +++ b/library/include/libtorrent/debug.hpp @@ -0,0 +1,92 @@ +/* + +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 + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +namespace libtorrent +{ + + // PROFILING CODE +#ifdef TORRENT_PROFILE + + void add_checkpoint(std::string const& str); + void print_checkpoints(); +#define TORRENT_CHECKPOINT(str) libtorrent::add_checkpoint(str) +#else +#define TORRENT_CHECKPOINT(str) void(0) +#endif + + // DEBUG API + + struct logger + { + logger(boost::filesystem::path const& filename, bool append = true) + { + using namespace boost::filesystem; + path dir(complete("libtorrent_logs")); + if (!exists(dir)) create_directories(dir); + m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out)); + *this << "\n\n\n*** starting log ***\n"; + } + + template + logger& operator<<(T const& v) + { + m_file << v; + m_file.flush(); + return *this; + } + + boost::filesystem::ofstream m_file; + }; + +} + +#endif // TORRENT_DEBUG_HPP_INCLUDED diff --git a/library/include/libtorrent/entry.hpp b/library/include/libtorrent/entry.hpp new file mode 100755 index 000000000..85c5462a2 --- /dev/null +++ b/library/include/libtorrent/entry.hpp @@ -0,0 +1,272 @@ +/* + +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 +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT type_error: std::runtime_error + { + type_error(const char* error): std::runtime_error(error) {} + }; + + namespace detail + { + template + struct max2 { enum { value = v1>v2?v1:v2 }; }; + + template + struct max3 + { + enum + { + temp = max2::value, + value = temp>v3?temp:v3 + }; + }; + + template + struct max4 + { + enum + { + temp = max3::value, + value = temp>v4?temp:v4 + }; + }; + } + + 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(const dictionary_type&); + entry(const string_type&); + entry(const list_type&); + entry(const integer_type&); + + entry(); + entry(data_type t); + entry(const entry& e); + ~entry(); + + bool operator==(entry const& e) 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; + + // these functions requires that the entry + // is a dictionary, otherwise they will throw + entry& operator[](char const* key); + entry& operator[](std::string const& key); + const entry& operator[](char const* key) const; + const entry& 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; + + private: + + void construct(data_type t); + void copy(const entry& e); + void destruct(); + + data_type m_type; + +#if defined(_MSC_VER) && _MSC_VER < 1310 + // workaround for msvc-bug. + // assumes sizeof(map) == sizeof(map) + // and sizeof(list) == sizeof(list) + union + { + char data[ + detail::max4) + , sizeof(std::map) + , sizeof(string_type) + , sizeof(integer_type)>::value]; + integer_type dummy_aligner; + }; +#else + union + { + char data[detail::max4::value]; + integer_type dummy_aligner; + }; +#endif + + }; + + inline std::ostream& operator<<(std::ostream& os, const entry& e) + { + e.print(os, 0); + return os; + } + + inline entry::data_type entry::type() const { return m_type; } + + inline entry::entry(): m_type(undefined_t) {} + inline entry::entry(data_type t): m_type(t) { construct(t); } + inline entry::entry(const entry& e) { copy(e); } + inline entry::~entry() { destruct(); } + + inline void entry::operator=(const entry& e) + { + destruct(); + copy(e); + } + + inline entry::integer_type& entry::integer() + { + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::integer_type const& entry::integer() const + { + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type& entry::string() + { + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type const& entry::string() const + { + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type& entry::list() + { + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type const& entry::list() const + { + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type& entry::dict() + { + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type const& entry::dict() const + { + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + +} + +#endif // TORRENT_ENTRY_HPP_INCLUDED diff --git a/library/include/libtorrent/escape_string.hpp b/library/include/libtorrent/escape_string.hpp new file mode 100755 index 000000000..e0e743e1e --- /dev/null +++ b/library/include/libtorrent/escape_string.hpp @@ -0,0 +1,46 @@ +/* + +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 "libtorrent/config.hpp" + +namespace libtorrent +{ + std::string TORRENT_EXPORT unescape_string(std::string const& s); + std::string TORRENT_EXPORT escape_string(const char* str, int len); + std::string TORRENT_EXPORT escape_path(const char* str, int len); +} + +#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED diff --git a/library/include/libtorrent/file.hpp b/library/include/libtorrent/file.hpp new file mode 100755 index 000000000..1b71c66f5 --- /dev/null +++ b/library/include/libtorrent/file.hpp @@ -0,0 +1,130 @@ +/* + +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 +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT file_error: std::runtime_error + { + file_error(std::string const& msg): std::runtime_error(msg) {} + }; + + class TORRENT_EXPORT file: public boost::noncopyable + { + public: + + class seek_mode + { + friend class file; + private: + seek_mode(int v): m_val(v) {} + int m_val; + }; + + static const seek_mode begin; + static const seek_mode end; + + class open_mode + { + friend class file; + public: + + open_mode(): m_mask(0) {} + + open_mode operator|(open_mode m) const + { return open_mode(m.m_mask | m_mask); } + + open_mode operator&(open_mode m) const + { return open_mode(m.m_mask & m_mask); } + + open_mode operator|=(open_mode m) + { + m_mask |= m.m_mask; + return *this; + } + + bool operator==(open_mode m) const { return m_mask == m.m_mask; } + bool operator!=(open_mode m) const { return m_mask != m.m_mask; } + + private: + + open_mode(int val): m_mask(val) {} + int m_mask; + }; + + static const open_mode in; + static const open_mode out; + + file(); + file(boost::filesystem::path const& p, open_mode m); + ~file(); + + void open(boost::filesystem::path const& p, open_mode m); + void close(); + + size_type write(const char*, size_type num_bytes); + size_type read(char*, size_type num_bytes); + + size_type seek(size_type pos, seek_mode m = begin); + size_type tell(); + + private: + + struct impl; + const std::auto_ptr m_impl; + + }; + +} + +#endif // TORRENT_FILE_HPP_INCLUDED + diff --git a/library/include/libtorrent/fingerprint.hpp b/library/include/libtorrent/fingerprint.hpp new file mode 100755 index 000000000..d7e5a5fc6 --- /dev/null +++ b/library/include/libtorrent/fingerprint.hpp @@ -0,0 +1,93 @@ +/* + +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/peer_id.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) + { + assert(id_string); + assert(major >= 0); + assert(minor >= 0); + assert(revision >= 0); + assert(tag >= 0); + assert(std::strlen(id_string) == 2); + name[0] = id_string[0]; + name[1] = id_string[1]; + } + + std::string to_string() const + { + std::stringstream s; + s << "-" << 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.str(); + } + + 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); + assert(false); + return '0'; + } + + }; + +} + +#endif // TORRENT_FINGERPRINT_HPP_INCLUDED diff --git a/library/include/libtorrent/hasher.hpp b/library/include/libtorrent/hasher.hpp new file mode 100755 index 000000000..803b758eb --- /dev/null +++ b/library/include/libtorrent/hasher.hpp @@ -0,0 +1,118 @@ +/* + +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 + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" + +// from sha1.cpp +struct TORRENT_EXPORT SHA1_CTX +{ + boost::uint32_t state[5]; + boost::uint32_t count[2]; + boost::uint8_t buffer[64]; +}; + +TORRENT_EXPORT void SHA1Init(SHA1_CTX* context); +TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, boost::uint8_t const* data, boost::uint32_t len); +TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, boost::uint8_t* digest); + +extern "C" +{ + // from zlib/adler32.c + unsigned long adler32(unsigned long adler, const char* data, unsigned int len); +} + +namespace libtorrent +{ + + class adler32_crc + { + public: + adler32_crc(): m_adler(adler32(0, 0, 0)) {} + + void update(const char* data, int len) + { + assert(data != 0); + assert(len > 0); + m_adler = adler32(m_adler, data, len); + } + unsigned long final() const { return m_adler; } + void reset() { m_adler = adler32(0, 0, 0); } + + private: + + unsigned long m_adler; + + }; + + class hasher + { + public: + + hasher() { SHA1Init(&m_context); } + hasher(const char* data, int len) + { + SHA1Init(&m_context); + assert(data != 0); + assert(len > 0); + SHA1Update(&m_context, reinterpret_cast(data), len); + } + void update(const char* data, int len) + { + assert(data != 0); + assert(len > 0); + SHA1Update(&m_context, reinterpret_cast(data), len); + } + + sha1_hash final() + { + sha1_hash digest; + SHA1Final(&m_context, digest.begin()); + return digest; + } + + void reset() { SHA1Init(&m_context); } + + private: + + SHA1_CTX m_context; + + }; +} + +#endif // TORRENT_HASHER_HPP_INCLUDED diff --git a/library/include/libtorrent/http_tracker_connection.hpp b/library/include/libtorrent/http_tracker_connection.hpp new file mode 100755 index 000000000..b66d4349d --- /dev/null +++ b/library/include/libtorrent/http_tracker_connection.hpp @@ -0,0 +1,176 @@ +/* + +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 +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/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" +#include "libtorrent/buffer.hpp" + +namespace libtorrent +{ + + class http_parser + { + public: + http_parser(); + template + T header(char const* key) const; + std::string const& protocol() const { return m_protocol; } + int status_code() const { return m_status_code; } + std::string message() const { return m_server_message; } + buffer::const_interval get_body(); + bool header_finished() const { return m_state == read_body; } + bool finished() const { return m_finished; } + boost::tuple incoming(buffer::const_interval recv_buffer); + int body_start() const { return m_body_start_pos; } + private: + int m_recv_pos; + int m_status_code; + std::string m_protocol; + std::string m_server_message; + + int m_content_length; + enum { plain, gzip } m_content_encoding; + + enum { read_status, read_header, read_body } m_state; + + std::map m_header; + buffer::const_interval m_recv_buffer; + int m_body_start_pos; + + bool m_finished; + }; + + template + T http_parser::header(char const* key) const + { + std::map::const_iterator i + = m_header.find(key); + if (i == m_header.end()) return T(); + return boost::lexical_cast(i->second); + } + + class TORRENT_EXPORT http_tracker_connection + : public tracker_connection + { + friend class tracker_manager; + public: + + http_tracker_connection( + demuxer& d + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string request + , boost::weak_ptr c + , session_settings const& stn + , std::string const& password = ""); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_response(); + + void init_send_buffer( + std::string const& hostname + , std::string const& request); + + void name_lookup(asio::error const& error, tcp::resolver::iterator i); + void connected(asio::error const& error); + void sent(asio::error const& error); + void receive(asio::error const& error + , std::size_t bytes_transferred); + + virtual void on_timeout(); + + void parse(const entry& e); + peer_entry extract_peer_info(const entry& e); + + tracker_manager& m_man; + enum { read_status, read_header, read_body } m_state; + + enum { plain, gzip } m_content_encoding; + int m_content_length; + std::string m_location; + + tcp::resolver m_name_lookup; + int m_port; + boost::shared_ptr m_socket; + int m_recv_pos; + std::vector m_buffer; + std::string m_send_buffer; + + std::string m_server_message; + std::string m_server_protocol; + + session_settings const& m_settings; + std::string m_password; + int m_code; + + // server string in http-reply + std::string m_server; + + bool m_timed_out; + }; + +} + +#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/library/include/libtorrent/identify_client.hpp b/library/include/libtorrent/identify_client.hpp new file mode 100755 index 000000000..e8cb3b930 --- /dev/null +++ b/library/include/libtorrent/identify_client.hpp @@ -0,0 +1,58 @@ +/* + +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/library/include/libtorrent/invariant_check.hpp b/library/include/libtorrent/invariant_check.hpp new file mode 100755 index 000000000..c6eacf338 --- /dev/null +++ b/library/include/libtorrent/invariant_check.hpp @@ -0,0 +1,78 @@ +// 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 + +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 (...) + { + assert(false); + } + } + + ~invariant_checker_impl() + { + try + { + check_invariant(self); + } + catch (...) + { + assert(false); + } + } + + T const& self; + }; + + template + invariant_checker_impl make_invariant_checker(T const& x) + { + return invariant_checker_impl(x); + } +} + +#ifndef NDEBUG +#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/library/include/libtorrent/io.hpp b/library/include/libtorrent/io.hpp new file mode 100755 index 000000000..57a22cf97 --- /dev/null +++ b/library/include/libtorrent/io.hpp @@ -0,0 +1,140 @@ +/* + +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 + +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); } + + } +} + +#endif // TORRENT_IO_HPP_INCLUDED diff --git a/library/include/libtorrent/ip_filter.hpp b/library/include/libtorrent/ip_filter.hpp new file mode 100644 index 000000000..c75dafd66 --- /dev/null +++ b/library/include/libtorrent/ip_filter.hpp @@ -0,0 +1,276 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include +#include + +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 +{ + + // 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() + { + typename Addr::bytes_type zero; + std::fill(zero.begin(), zero.end(), 0); + // make the entire ip-range non-blocked + m_access_list.insert(range(Addr(zero), 0)); + } + + void add_rule(Addr first, Addr last, int flags) + { + using boost::next; + using boost::prior; + + assert(!m_access_list.empty()); + 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; + + assert(j != m_access_list.begin()); + assert(j != i); + + int first_access = i->access; + int last_access = 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() && prior(i)->access == flags) + { + --i; + first_access = i->access; + } + assert(!m_access_list.empty()); + assert(i != m_access_list.end()); + + if (i != j) m_access_list.erase(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())) + { + 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); + assert(!m_access_list.empty()); + } + + int access(Addr const& addr) const + { + assert(!m_access_list.empty()); + typename range_t::const_iterator i = m_access_list.upper_bound(addr); + if (i != m_access_list.begin()) --i; + assert(i != m_access_list.end()); + assert(i->start <= addr && (boost::next(i) == m_access_list.end() + || addr < boost::next(i)->start)); + return i->access; + } + + 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 = i->start; + r.flags = i->access; + + ++i; + if (i == end) + r.last = max_addr(); + else + r.last = minus_one(i->start); + + ret.push_back(r); + } + return ret; + } + + private: + + Addr plus_one(Addr const& a) const + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i < (std::numeric_limits::max)()) + { + *i += 1; + break; + } + *i = 0; + } + return Addr(tmp); + } + + Addr minus_one(Addr const& a) const + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i > 0) + { + *i -= 1; + break; + } + *i = (std::numeric_limits::max)(); + } + return Addr(tmp); + } + + Addr max_addr() const + { + typename Addr::bytes_type tmp; + std::fill(tmp.begin(), tmp.end() + , (std::numeric_limits::max)()); + return Addr(tmp); + } + + struct range + { + range(Addr addr, int access = 0): start(addr), access(access) {} + 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; + + }; + +} + +class TORRENT_EXPORT ip_filter +{ +public: + + 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; + + typedef boost::tuple > + , std::vector > > filter_tuple_t; + + filter_tuple_t export_filter() const; + +// void print() const; + +private: + + detail::filter_impl m_filter4; + detail::filter_impl m_filter6; +}; + +} + +#endif + diff --git a/library/include/libtorrent/kademlia/closest_nodes.hpp b/library/include/libtorrent/kademlia/closest_nodes.hpp new file mode 100644 index 000000000..d5580b9c9 --- /dev/null +++ b/library/include/libtorrent/kademlia/closest_nodes.hpp @@ -0,0 +1,86 @@ +/* + +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 CLOSEST_NODES_050323_HPP +#define CLOSEST_NODES_050323_HPP + +#include + +#include +#include +#include + +#include + +namespace libtorrent { namespace dht +{ + +class rpc_manager; + +// -------- closest nodes ----------- + +class closest_nodes : public traversal_algorithm +{ +public: + typedef boost::function< + void(std::vector const&) + > done_callback; + + static void initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + +private: + void done(); + void invoke(node_id const& id, asio::ip::udp::endpoint addr); + + closest_nodes( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + done_callback m_done_callback; +}; + +} } // namespace libtorrent::dht + +#endif // CLOSEST_NODES_050323_HPP + diff --git a/library/include/libtorrent/kademlia/dht_tracker.hpp b/library/include/libtorrent/kademlia/dht_tracker.hpp new file mode 100644 index 000000000..d447d4b23 --- /dev/null +++ b/library/include/libtorrent/kademlia/dht_tracker.hpp @@ -0,0 +1,144 @@ +/* + +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 +#include +#include + +#include "libtorrent/kademlia/node.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/traversal_algorithm.hpp" +#include "libtorrent/kademlia/packet_iterator.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(dht_tracker); +#endif + + struct dht_tracker + { + dht_tracker(asio::io_service& d, dht_settings const& settings + , asio::ip::address listen_interface, entry const& bootstrap); + + void add_node(udp::endpoint node); + void add_node(std::pair const& node); + void add_router_node(std::pair const& node); + + void rebind(asio::ip::address listen_interface, int listen_port); + + entry state() const; + + void announce(sha1_hash const& ih, int listen_port + , boost::function const& + , sha1_hash const&)> f); + + void dht_status(session_status& s); + + private: + + void on_name_lookup(asio::error const& e + , udp::resolver::iterator host); + void on_router_name_lookup(asio::error const& e + , udp::resolver::iterator host); + void connection_timeout(asio::error const& e); + void refresh_timeout(asio::error const& e); + void tick(asio::error const& e); + + // translate bittorrent kademlia message into the generice kademlia message + // used by the library + void on_receive(asio::error const& error, size_t bytes_transferred); + void on_bootstrap(); + void send_packet(msg const& m); + + asio::io_service& m_demuxer; + asio::ip::udp::socket m_socket; + + node_impl m_dht; + + // this is the index of the receive buffer we are currently receiving to + // the other buffer is the one containing the last message + int m_buffer; + std::vector m_in_buf[2]; + udp::endpoint m_remote_endpoint[2]; + std::vector m_send_buf; + + boost::posix_time::ptime m_last_refresh; + deadline_timer m_timer; + deadline_timer m_connection_timer; + deadline_timer m_refresh_timer; + dht_settings const& m_settings; + int m_refresh_bucket; + + // used to resolve hostnames for nodes + udp::resolver m_host_resolver; + +#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_announces; + int m_failed_announces; + + int m_total_message_input; + int m_ut_message_input; + int m_lt_message_input; + int m_mp_message_input; + int m_gr_message_input; + + int m_total_in_bytes; + int m_total_out_bytes; + + int m_queries_out_bytes; +#endif + }; +}} + +#endif +#endif diff --git a/library/include/libtorrent/kademlia/find_data.hpp b/library/include/libtorrent/kademlia/find_data.hpp new file mode 100644 index 000000000..bbafcdd77 --- /dev/null +++ b/library/include/libtorrent/kademlia/find_data.hpp @@ -0,0 +1,95 @@ +/* + +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 + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +typedef std::vector packet_t; + +class rpc_manager; + +// -------- find data ----------- + +class find_data : public traversal_algorithm +{ +public: + typedef boost::function done_callback; + + static void initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + void got_data(msg const* m); + +private: + void done(); + void invoke(node_id const& id, udp::endpoint addr); + + find_data( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + done_callback m_done_callback; + boost::shared_ptr m_packet; + bool m_done; +}; + +} } // namespace libtorrent::dht + +#endif // FIND_DATA_050323_HPP + diff --git a/library/include/libtorrent/kademlia/logging.hpp b/library/include/libtorrent/kademlia/logging.hpp new file mode 100644 index 000000000..8bd488f1a --- /dev/null +++ b/library/include/libtorrent/kademlia/logging.hpp @@ -0,0 +1,146 @@ +/* + +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 +#include + +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_ << '[' << 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("libtorrent_logs/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 + diff --git a/library/include/libtorrent/kademlia/node.hpp b/library/include/libtorrent/kademlia/node.hpp new file mode 100644 index 000000000..c44a21f33 --- /dev/null +++ b/library/include/libtorrent/kademlia/node.hpp @@ -0,0 +1,185 @@ +/* + +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 + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(node); +#endif + +// 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; + boost::posix_time::ptime added; +}; + +// this is a group. It contains a set of group members +struct torrent_entry +{ + std::set peers; +}; + +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 node_impl : boost::noncopyable +{ +typedef std::map table_t; +public: + node_impl(boost::function const& f + , dht_settings const& settings, boost::optional node_id); + + virtual ~node_impl() {} + + void refresh(node_id const& id, boost::function0 f); + void bootstrap(std::vector const& nodes + , boost::function0 f); + void find_node(node_id const& id, boost::function< + void(std::vector const&)> f); + void add_router_node(udp::endpoint router); + + void incoming(msg const& m); + + void refresh(); + void refresh_bucket(int bucket); + int bucket_size(int bucket); + + typedef routing_table::iterator iterator; + + iterator begin() const { return m_table.begin(); } + iterator end() const { return m_table.end(); } + + typedef table_t::iterator data_iterator; + + node_id const& nid() const { return m_id; } + boost::tuple size() const{ return m_table.size(); } + + data_iterator begin_data() { return m_map.begin(); } + data_iterator end_data() { return m_map.end(); } + int data_size() const { return int(m_map.size()); } + + void print_state(std::ostream& os) const + { m_table.print_state(os); } + + void announce(sha1_hash const& info_hash, int listen_port + , boost::function const& + , sha1_hash const&)> f); + + bool verify_token(msg const& m); + entry generate_token(msg const& m); + + // the returned time is the delay until connection_timeout() + // should be called again the next time + boost::posix_time::time_duration connection_timeout(); + boost::posix_time::time_duration refresh_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); } + +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 on_find(msg const& m, std::vector& peers) const; + + // this is called when a store request is received. The data + // is store-parameters and the data to be stored. + void on_announce(msg const& m, msg& reply); + + 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: + void incoming_request(msg const& h); + + node_id m_id; + routing_table m_table; + rpc_manager m_rpc; + table_t m_map; + + boost::posix_time::ptime m_last_tracker_tick; + + // secret random numbers used to create write tokens + int m_secret[2]; +}; + + +} } // namespace libtorrent::dht + +#endif // NODE_HPP + diff --git a/library/include/libtorrent/kademlia/node_entry.hpp b/library/include/libtorrent/kademlia/node_entry.hpp new file mode 100644 index 000000000..edc5dff80 --- /dev/null +++ b/library/include/libtorrent/kademlia/node_entry.hpp @@ -0,0 +1,63 @@ +/* + +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" + +namespace libtorrent { namespace dht +{ + +struct node_entry +{ + node_entry(node_id const& id_, asio::ip::udp::endpoint addr_) + : id(id_) + , addr(addr_) + , fail_count(0) {} + node_entry(asio::ip::udp::endpoint addr_) + : id(0) + , addr(addr_) + , fail_count(0) {} + + node_id id; + udp::endpoint addr; + // the number of times this node has failed to + // respond in a row + int fail_count; +}; + +} } // namespace libtorrent::dht + +#endif + diff --git a/library/include/libtorrent/kademlia/node_id.hpp b/library/include/libtorrent/kademlia/node_id.hpp new file mode 100644 index 000000000..eb4d6c539 --- /dev/null +++ b/library/include/libtorrent/kademlia/node_id.hpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef NODE_ID_HPP +#define NODE_ID_HPP + +#include +#include + +#include +#include "libtorrent/peer_id.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 distance(node_id const& n1, node_id const& n2); + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool 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 distance_exp(node_id const& n1, node_id const& n2); + +} } // namespace libtorrent::dht + +#endif // NODE_ID_HPP + diff --git a/library/include/libtorrent/kademlia/packet_iterator.hpp b/library/include/libtorrent/kademlia/packet_iterator.hpp new file mode 100644 index 000000000..e906a90bf --- /dev/null +++ b/library/include/libtorrent/kademlia/packet_iterator.hpp @@ -0,0 +1,95 @@ +/* + +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 PACKET_ITERATOR_HPP +#define PACKET_ITERATOR_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class packet_iterator: public boost::iterator_facade< + packet_iterator, const char, boost::forward_traversal_tag> +{ +public: + typedef std::vector::const_iterator base_iterator; + + packet_iterator() {} + + packet_iterator(std::vector::const_iterator start + , std::vector::const_iterator end + , std::string const& error_msg = "") + : m_base(start) + , m_end(end) + , m_msg(error_msg) + {} + + base_iterator base() const + { return m_base; } + + base_iterator end() const + { return m_end; } + + int left() const { return int(m_end - m_base); } + +private: + friend class boost::iterator_core_access; + + bool equal(packet_iterator const& other) const + { return m_base == other.m_base; } + + void advance(int n) + { + m_base += n; + } + + void increment() + { ++m_base; } + + char const& dereference() const + { + if (m_base == m_end) throw std::runtime_error(m_msg); + return *m_base; + } + + base_iterator m_base; + base_iterator m_end; + std::string m_msg; +}; + +} } // namespace libtorrent::dht + +#endif // PACKET_ITERATOR_HPP + diff --git a/library/include/libtorrent/kademlia/refresh.hpp b/library/include/libtorrent/kademlia/refresh.hpp new file mode 100644 index 000000000..f47b80462 --- /dev/null +++ b/library/include/libtorrent/kademlia/refresh.hpp @@ -0,0 +1,159 @@ +/* + +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 + +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(refresh); +#endif + +class routing_table; +class rpc_manager; + +class refresh : public traversal_algorithm +{ +public: + typedef boost::function done_callback; + + template + static void initiate( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback + ); + + void ping_reply(node_id id); + void ping_timeout(node_id id); + +private: + template + refresh( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback + ); + + void done(); + void invoke(node_id const& id, udp::endpoint addr); + + void invoke_pings_or_finish(); + + int m_max_active_pings; + int m_active_pings; + + done_callback m_done_callback; + + std::vector::iterator m_leftover_nodes_iterator; +}; + +template +inline refresh::refresh( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , first + , last + ) + , m_max_active_pings(max_active_pings) + , m_active_pings(0) + , m_done_callback(callback) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +template +inline void refresh::initiate( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback +) +{ + new refresh( + target + , branch_factor + , max_active_pings + , max_results + , table + , first + , last + , rpc + , callback + ); +} + +} } // namespace libtorrent::dht + +#endif // REFRESH_050324_HPP + diff --git a/library/include/libtorrent/kademlia/routing_table.hpp b/library/include/libtorrent/kademlia/routing_table.hpp new file mode 100644 index 000000000..3abc472b4 --- /dev/null +++ b/library/include/libtorrent/kademlia/routing_table.hpp @@ -0,0 +1,246 @@ +/* + +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 pt = boost::posix_time; + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +//TORRENT_DECLARE_LOG(table); + +typedef std::deque bucket_t; + +// differences in the implementation from the description in +// the paper: +// +// * The routing table tree is not allocated dynamically, there +// are always 160 buckets. +// * 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 routing_table; + +namespace aux +{ + + // Iterates over a flattened routing_table structure. + class routing_table_iterator + : public boost::iterator_facade< + routing_table_iterator + , node_entry const + , boost::forward_traversal_tag + > + { + public: + routing_table_iterator() + { + } + + private: + friend class libtorrent::dht::routing_table; + friend class boost::iterator_core_access; + + typedef boost::array, 160>::const_iterator + bucket_iterator_t; + + routing_table_iterator( + bucket_iterator_t begin + , bucket_iterator_t end) + : m_bucket_iterator(begin) + , m_bucket_end(end) + , m_iterator(begin != end ? begin->first.begin() : bucket_t::iterator()) + { + if (m_bucket_iterator == m_bucket_end) return; + while (m_iterator == m_bucket_iterator->first.end()) + { + if (++m_bucket_iterator == m_bucket_end) + break; + m_iterator = m_bucket_iterator->first.begin(); + } + } + + bool equal(routing_table_iterator const& other) const + { + return m_bucket_iterator == other.m_bucket_iterator + && (m_bucket_iterator == m_bucket_end + || m_iterator == other.m_iterator); + } + + void increment() + { + assert(m_bucket_iterator != m_bucket_end); + ++m_iterator; + while (m_iterator == m_bucket_iterator->first.end()) + { + if (++m_bucket_iterator == m_bucket_end) + break; + m_iterator = m_bucket_iterator->first.begin(); + } + } + + node_entry const& dereference() const + { + assert(m_bucket_iterator != m_bucket_end); + return *m_iterator; + } + + bucket_iterator_t m_bucket_iterator; + bucket_iterator_t m_bucket_end; + bucket_t::const_iterator m_iterator; + }; + +} // namespace aux + +class routing_table +{ +public: + typedef aux::routing_table_iterator iterator; + typedef iterator const_iterator; + + routing_table(node_id const& id, int bucket_size + , dht_settings const& settings); + + 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(); } + + // 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 addr); + + // returns time when the given bucket needs another refresh. + // if the given bucket is empty but there are nodes + // in a bucket closer to us, or if the bucket is non-empty and + // the time from the last activity is more than 15 minutes + boost::posix_time::ptime next_refresh(int bucket); + + // 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 + , bool include_self, int count = 0); + + // returns true if the given node would be placed in a bucket + // that is not full. If the node already exists in the table + // this function returns false + bool need_node(node_id const& id); + + // this will set the given bucket's latest activity + // to the current time + void touch_bucket(int bucket); + + int bucket_size(int bucket) + { + assert(bucket >= 0 && bucket < 160); + return (int)m_buckets[bucket].first.size(); + } + int bucket_size() const { return m_bucket_size; } + + iterator begin() const; + iterator end() const; + + boost::tuple size() const; + + // returns true if there are no working nodes + // in the routing table + bool need_bootstrap() const; + + void replacement_cache(bucket_t& nodes) const; + + // 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; + +private: + + // constant called k in paper + int m_bucket_size; + + dht_settings const& m_settings; + + // 160 (k-bucket, replacement cache) pairs + typedef boost::array, 160> table_t; + table_t m_buckets; + // timestamps of the last activity in each bucket + typedef boost::array table_activity_t; + table_activity_t m_bucket_activity; + node_id m_id; // our own node id + + // 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; + + // this is the lowest bucket index with nodes in it + int m_lowest_active_bucket; +}; + +} } // namespace libtorrent::dht + +#endif // ROUTING_TABLE_HPP + diff --git a/library/include/libtorrent/kademlia/rpc_manager.hpp b/library/include/libtorrent/kademlia/rpc_manager.hpp new file mode 100644 index 000000000..5aa850e95 --- /dev/null +++ b/library/include/libtorrent/kademlia/rpc_manager.hpp @@ -0,0 +1,194 @@ +/* + +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 +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(rpc); +#endif + +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), piggy_backed_ping(false) + , port(0) {} + + // true if this message is a reply + bool reply; + // true if this is a reply with a piggy backed ping + bool piggy_backed_ping; + // 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; + // if this packet has a piggy backed ping, this + // is the transaction id of that ping + std::string ping_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 + typedef std::vector nodes_t; + nodes_t nodes; + + typedef std::vector peers_t; + peers_t peers; + + // similar to transaction_id but for write operations. + entry 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; +}; + +struct observer : boost::noncopyable +{ + observer() + : sent(boost::posix_time::microsec_clock::universal_time()) + {} + + virtual ~observer() {} + + // this two callbacks lets the observer add + // information to the message before it's sent + virtual void send(msg& m) = 0; + + // this is called when a reply is received + virtual void reply(msg const& m) = 0; + + // this is called when no reply has been received within + // some timeout + virtual void timeout() = 0; + + udp::endpoint target_addr; + boost::posix_time::ptime sent; +}; + +class routing_table; + +class rpc_manager +{ +public: + typedef boost::function1 fun; + typedef boost::function1 send_fun; + + rpc_manager(fun const& incoming_fun, node_id const& our_id + , routing_table& table, send_fun const& sf); + ~rpc_manager(); + + // returns true if the node needs a refresh + bool incoming(msg const&); + boost::posix_time::time_duration tick(); + + void invoke(int message_id, udp::endpoint target + , boost::shared_ptr o); + + void reply(msg& m, msg const& reply_to); + void reply_with_ping(msg& m, msg const& reply_to); + +#ifndef NDEBUG + void check_invariant() const; +#endif + +private: + + enum { max_transactions = 2048 }; + unsigned int new_transaction_id(); + void update_oldest_transaction_id(); + + boost::uint32_t calc_connection_id(udp::endpoint addr); + + typedef boost::array, max_transactions> + transactions_t; + transactions_t m_transactions; + + // this is the next transaction id to be used + int m_next_transaction_id; + // this is the oldest transaction id still + // (possibly) in use. This is the transaction + // that will time out first, the one we are + // waiting for to time out + int m_oldest_transaction_id; + + fun m_incoming; + send_fun m_send; + node_id m_our_id; + routing_table& m_table; + boost::posix_time::ptime m_timer; + node_id m_random_number; +}; + +} } // namespace libtorrent::dht + +#endif + + diff --git a/library/include/libtorrent/kademlia/traversal_algorithm.hpp b/library/include/libtorrent/kademlia/traversal_algorithm.hpp new file mode 100644 index 000000000..a99a9c6ea --- /dev/null +++ b/library/include/libtorrent/kademlia/traversal_algorithm.hpp @@ -0,0 +1,161 @@ +/* + +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 + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(traversal); +#endif + +class rpc_manager; + +// this class may not be instantiated as a stack object +class traversal_algorithm : boost::noncopyable +{ +public: + void traverse(node_id const& id, udp::endpoint addr); + void finished(node_id const& id); + void failed(node_id const& id); + virtual ~traversal_algorithm() {} + +protected: + template + traversal_algorithm( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , InIt start + , InIt end + ); + + void add_request(node_id const& id, udp::endpoint addr); + void add_requests(); + void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); + + virtual void done() = 0; + virtual void invoke(node_id const& id, udp::endpoint addr) = 0; + + struct result + { + result(node_id const& id, udp::endpoint addr, unsigned char f = 0) + : id(id), addr(addr), flags(f) + {} + + node_id id; + udp::endpoint addr; + enum { queried = 1, initial = 2 }; + unsigned char flags; + }; + + std::vector::iterator last_iterator(); + + 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_id m_target; + int m_branch_factor; + int m_max_results; + std::vector m_results; + std::set m_failed; + routing_table& m_table; + rpc_manager& m_rpc; + int m_invoke_count; +}; + +template +traversal_algorithm::traversal_algorithm( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , InIt start // <- nodes to initiate traversal with + , InIt end +) + : m_ref_count(0) + , m_target(target) + , m_branch_factor(branch_factor) + , m_max_results(max_results) + , m_table(table) + , m_rpc(rpc) + , m_invoke_count(0) +{ + using boost::bind; + + for (InIt i = start; i != end; ++i) + { + add_entry(i->id, i->addr, result::initial); + } + + // in case the routing table is empty, use the + // router nodes in the table + if (start == end) + { + for (routing_table::router_iterator i = table.router_begin() + , end(table.router_end()); i != end; ++i) + { + add_entry(node_id(0), *i, result::initial); + } + } + +} + +} } // namespace libtorrent::dht + +#endif // TRAVERSAL_ALGORITHM_050324_HPP + diff --git a/library/include/libtorrent/peer.hpp b/library/include/libtorrent/peer.hpp new file mode 100755 index 000000000..c404a611d --- /dev/null +++ b/library/include/libtorrent/peer.hpp @@ -0,0 +1,63 @@ +/* + +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/library/include/libtorrent/peer_connection.hpp b/library/include/libtorrent/peer_connection.hpp new file mode 100755 index 000000000..a7f9b7e53 --- /dev/null +++ b/library/include/libtorrent/peer_connection.hpp @@ -0,0 +1,557 @@ +/* + +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 +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.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/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session.hpp" + +// TODO: each time a block is 'taken over' +// from another peer. That peer must be given +// a chance to become not-interested. + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); + TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); + + struct TORRENT_EXPORT protocol_error: std::runtime_error + { + protocol_error(const std::string& msg): std::runtime_error(msg) {}; + }; + + class TORRENT_EXPORT peer_connection + : public boost::noncopyable + { + friend class invariant_access; + friend void intrusive_ptr_add_ref(peer_connection const*); + friend void intrusive_ptr_release(peer_connection const*); + 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 + peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote); + + // 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); + + // 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(); + + void set_upload_limit(int limit); + void set_download_limit(int limit); + + virtual ~peer_connection(); + + // 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); + + // 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() const; + + bool is_seed() const; + + bool has_timed_out() const; + + // 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& pid) { m_peer_id = pid; } + bool has_piece(int i) const; + + const std::deque& download_queue() const; + const std::deque& request_queue() const; + const std::deque& upload_queue() 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; } + + // 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(float tick_interval); + + boost::shared_ptr get_socket() const { return m_socket; } + tcp::endpoint const& remote() const { return m_remote; } + + std::vector const& get_bitfield() const; + + // this will cause this peer_connection to be disconnected. + // what it does is that it puts a reference to it in + // m_ses.m_disconnect_peer list, which will be scanned in the + // mainloop to disconnect peers. + void disconnect(); + 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(asio::error 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 connect(); + + // 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(); + void received_invalid_data(); + int trust_points() const; + + 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; } + + void set_failed() { m_failed = true; } + bool failed() const { return m_failed; } + + int desired_queue_size() const { return m_desired_queue_size; } + +#ifdef TORRENT_VERBOSE_LOGGING + 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(std::vector const& bitfield); + void incoming_request(peer_request const& r); + void incoming_piece(peer_request const& p, char const* data); + void incoming_piece_fragment(); + void incoming_cancel(peer_request const& r); + void incoming_dht_port(int listen_port); + + // the following functions appends messages + // to the send buffer + void send_choke(); + void send_unchoke(); + void send_interested(); + void send_not_interested(); + + // adds a block to the request queue + void add_request(piece_block const& b); + void cancel_request(piece_block const& b); + void send_block_requests(); + + // how much bandwidth we're using, how much we want, + // and how much we are allowed to use. + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + +#ifndef NDEBUG + void check_invariant() const; + boost::posix_time::ptime m_last_choke; +#endif + + virtual void get_peer_info(peer_info& p) 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(); + } + + protected: + + 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) = 0; + + virtual void on_connected() = 0; + virtual void on_tick() {} + + virtual void on_receive(asio::error const& error + , std::size_t bytes_transferred) = 0; + virtual void on_sent(asio::error const& error + , std::size_t bytes_transferred) = 0; + + void send_buffer(char const* begin, char const* end); + buffer::interval allocate_send_buffer(int size); + int send_buffer_size() const + { + return (int)m_send_buffer[0].size() + + (int)m_send_buffer[1].size() + - m_write_pos; + } + + buffer::const_interval receive_buffer() const + { + return buffer::const_interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + m_recv_pos); + } + + void cut_receive_buffer(int size, int packet_size); + + void reset_recv_buffer(int packet_size); + int packet_size() const { return m_packet_size; } + + bool packet_finished() const + { + assert(m_recv_pos <= m_packet_size); + return m_packet_size == m_recv_pos; + } + + void setup_send(); + void setup_receive(); + + void attach_to_torrent(sha1_hash const& ih); + + bool verify_piece(peer_request const& p) const; + + // 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; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + // called from the main loop when this connection has any + // work to do. + void on_send_data(asio::error const& error + , std::size_t bytes_transferred); + void on_receive_data(asio::error 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; + + void set_timeout(int s) { m_timeout = s; } + + private: + + void fill_send_buffer(); + + // the timeout in seconds + int m_timeout; + + // the time when we last got a part of a + // piece packet from this peer + boost::posix_time::ptime m_last_piece; + + int m_packet_size; + int m_recv_pos; + std::vector m_recv_buffer; + + // this is the buffer where data that is + // to be sent is stored until it gets + // consumed by send(). Since asio requires + // the memory buffer that is given to async. + // operations to remain valid until the operation + // finishes, there has to be two buffers. While + // waiting for a async_write operation on one + // buffer, the other is used to write data to + // be queued up. + std::vector m_send_buffer[2]; + // the current send buffer is the one to write to. + // (m_current_send_buffer + 1) % 2 is the + // buffer we're currently waiting for. + int m_current_send_buffer; + + // if the sending buffer doesn't finish in one send + // operation, this is the position within that buffer + // where the next operation should continue + int m_write_pos; + + // timeouts + boost::posix_time::ptime m_last_receive; + boost::posix_time::ptime m_last_sent; + + boost::shared_ptr m_socket; + tcp::endpoint m_remote; + + // this is the torrent this connection is + // associated with. If the connection is an + // incoming conncetion, 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; + // is true if it was we that connected to the peer + // and false if we got an incomming connection + // could be considered: true = local, false = remote + bool m_active; + + // remote peer's id + peer_id m_peer_id; + + // other side says that it's interested in downloading + // from us. + bool m_peer_interested; + + // the other side has told us that it won't send anymore + // data to us for a while + bool m_peer_choked; + + // the peer has pieces we are interested in + bool m_interesting; + + // we have choked the upload to the peer + bool m_choked; + + // 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; + + // the pieces the other end have + std::vector m_have_piece; + + // 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 queue of requests we have got + // from this peer + std::deque m_requests; + + // the blocks we have reserved in the piece + // picker and will send to this peer. + std::deque m_request_queue; + + // the queue of blocks we have requested + // from this peer + std::deque m_download_queue; + + // the number of request we should queue up + // at the remote end. + int m_desired_queue_size; + + // 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; + + // 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. + int m_trust_points; + + // if this is true, this peer is assumed to handle all piece + // requests in fifo order. All skipped blocks are re-requested + // immediately instead of having a looser requirement + // where blocks can be sent out of order. The default is to + // allow non-fifo order. + bool m_assume_fifo; + + // 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 true if this connection has been added + // to the list of connections that will be closed. + bool m_disconnecting; + + // the time when this peer sent us a not_interested message + // the last time. + boost::posix_time::ptime m_became_uninterested; + + // the time when we sent a not_interested message to + // this peer the last time. + boost::posix_time::ptime m_became_uninteresting; + + // 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; + + // This is true until connect is called on the + // peer_connection's socket. It is false on incoming + // connections. + bool m_queued; + + // these are true when there's a asynchronous write + // or read operation running. + bool m_writing; + // this is the number of bytes sent to the socket last + // time it was invoked. This is compared against the + // bytes_transferred in the callback function that tells + // how much actually was sent. Then the quota can be + // corrected according to the actual number of bytes sent + int m_last_write_size; + bool m_reading; + int m_last_read_size; + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + +#ifndef NDEBUG + public: + bool m_in_constructor; +#endif + }; +} + +#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/library/include/libtorrent/peer_id.hpp b/library/include/libtorrent/peer_id.hpp new file mode 100755 index 000000000..d3bf624b7 --- /dev/null +++ b/library/include/libtorrent/peer_id.hpp @@ -0,0 +1,177 @@ +/* + +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 + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class TORRENT_EXPORT big_number + { + // private type + struct private_pointer {}; + // the number of bytes of the number + enum { number_size = 20 }; + public: + enum { size = number_size }; + + big_number() {} + + // when initialized with 0 + big_number(private_pointer*) { clear(); } + + void clear() + { + std::fill(m_number,m_number+number_size,0); + } + + bool is_all_zeros() const + { + return std::count(m_number,m_number+number_size,0) == number_size; + } + + 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; + } + + unsigned char& operator[](int i) + { assert(i >= 0 && i < number_size); return m_number[i]; } + + unsigned char const& operator[](int i) const + { 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; } + + private: + + unsigned char m_number[number_size]; + + }; + + typedef big_number peer_id; + typedef big_number sha1_hash; + + inline std::ostream& operator<<(std::ostream& os, big_number const& peer) + { + for (big_number::const_iterator i = peer.begin(); + i != peer.end(); ++i) + { + os << std::hex << std::setw(2) << std::setfill('0') + << static_cast(*i); + } + os << std::dec << std::setfill(' '); + return os; + } + + inline std::istream& operator>>(std::istream& is, big_number& peer) + { + using namespace std; + + for (big_number::iterator i = peer.begin(); + i != peer.end(); ++i) + { + char c[2]; + is >> c[0] >> c[1]; + c[0] = tolower(c[0]); + c[1] = tolower(c[1]); + if ( + ((c[0] < '0' || c[0] > '9') && (c[0] < 'a' || c[0] > 'f')) + || ((c[1] < '0' || c[1] > '9') && (c[1] < 'a' || c[1] > 'f')) + || is.fail()) + { + is.setstate(ios_base::failbit); + return is; + } + *i = ((isdigit(c[0])?c[0]-'0':c[0]-'a'+10) << 4) + + (isdigit(c[1])?c[1]-'0':c[1]-'a'+10); + } + return is; + } + +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/library/include/libtorrent/peer_info.hpp b/library/include/libtorrent/peer_info.hpp new file mode 100755 index 000000000..7a6e50834 --- /dev/null +++ b/library/include/libtorrent/peer_info.hpp @@ -0,0 +1,106 @@ +/* + +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 + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.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 + }; + unsigned int flags; + tcp::endpoint ip; + float up_speed; + float down_speed; + float payload_up_speed; + float payload_down_speed; + size_type total_download; + size_type total_upload; + peer_id pid; + std::vector pieces; + bool seed; // true if this is a seed + int upload_limit; + int download_limit; + + 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; + + // this is the number of requests + // the peer has sent to us + // that we haven't sent yet + int upload_queue_length; + + // 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 + }; + int connection_type; + }; + +} + +#endif // TORRENT_PEER_INFO_HPP_INCLUDED diff --git a/library/include/libtorrent/peer_request.hpp b/library/include/libtorrent/peer_request.hpp new file mode 100644 index 000000000..445ff4d7e --- /dev/null +++ b/library/include/libtorrent/peer_request.hpp @@ -0,0 +1,49 @@ +/* + +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/library/include/libtorrent/piece_block_progress.hpp b/library/include/libtorrent/piece_block_progress.hpp new file mode 100644 index 000000000..481ffc971 --- /dev/null +++ b/library/include/libtorrent/piece_block_progress.hpp @@ -0,0 +1,57 @@ +/* + +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/library/include/libtorrent/piece_picker.hpp b/library/include/libtorrent/piece_picker.hpp new file mode 100755 index 000000000..23f20a79a --- /dev/null +++ b/library/include/libtorrent/piece_picker.hpp @@ -0,0 +1,355 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + + struct TORRENT_EXPORT piece_block + { + piece_block(int p_index, int b_index) + : piece_index(p_index) + , block_index(b_index) + {} + int piece_index; + int block_index; + + 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: + + enum { max_blocks_per_piece = 256 }; + + struct block_info + { + block_info(): num_downloads(0) {} + // the peer this block was requested or + // downloaded from + tcp::endpoint peer; + // the number of times this block has been downloaded + int num_downloads; + }; + + struct downloading_piece + { + int index; + // each bit represents a block in the piece + // set to one if the block has been requested + std::bitset requested_blocks; + // the bit is set to one if the block has been acquired + std::bitset finished_blocks; + // info about each block + block_info info[max_blocks_per_piece]; + }; + + piece_picker(int blocks_per_piece + , int total_num_blocks); + + void set_sequenced_download_threshold(int sequenced_download_threshold); + + // this is called before any other method is called + // after the local files has been checked. + // the vector tells which pieces we already have + // and which we don't have. + void files_checked( + const std::vector& pieces + , const std::vector& unfinished); + + // increases the peer count for the given piece + // (is used when a HAVE or BITFIELD message is received) + void inc_refcount(int index); + + // decreases the peer count for the given piece + // (used when a peer disconnects) + void dec_refcount(int index); + + // 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); + + // This will mark a piece as unfiltered, and if it was + // previously marked as filtered, it will be considered + // interesting again and be placed in the piece list available + // for downloading. + void mark_as_unfiltered(int index); + + // This will mark a piece as filtered. The piece will be + // removed from the list of pieces avalable for downloading + // and hence, will not be downloaded. + void mark_as_filtered(int index); + + // returns true if the pieces at 'index' is marked as filtered + bool is_filtered(int index) const; + + // fills the bitmask with 1's for pieces that are filtered + void filtered_pieces(std::vector& mask) const; + + // 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 tcp::endpoint of the peer that we'll download + // from. + void pick_pieces(const std::vector& pieces + , std::vector& interesting_blocks + , int num_pieces, bool prefer_whole_pieces + , tcp::endpoint peer) const; + + // 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_downloading(piece_block block) const; + bool is_finished(piece_block block) const; + + // marks this piece-block as queued for downloading + void mark_as_downloading(piece_block block, tcp::endpoint const& peer); + void mark_as_finished(piece_block block, tcp::endpoint const& peer); + + // 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); + + 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; + + const std::vector& get_download_queue() const + { return m_downloads; } + + boost::optional 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; } +#ifndef NDEBUG + // used in debug mode + void check_invariant(const torrent* t = 0) const; +#endif + + // functor that compares indices on downloading_pieces + struct has_index + { + has_index(int i): index(i) { 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; } + + float distributed_copies() const; + + private: + + struct piece_pos + { + piece_pos() {} + piece_pos(int peer_count_, int index_) + : peer_count(peer_count_) + , downloading(0) + , filtered(0) + , index(index_) + { + assert(peer_count_ >= 0); + assert(index_ >= 0); + } + + // selects which vector to look in + unsigned peer_count : 11; + // is 1 if the piece is marked as being downloaded + unsigned downloading : 1; + // is 1 if the piece is filtered (not to be downloaded) + unsigned filtered : 1; + // index in to the piece_info vector + unsigned index : 19; + + enum { we_have_index = 0x3ffff }; + + int priority(int limit) const + { + return peer_count >= (unsigned)limit ? limit : peer_count; + } + + bool ordered(int limit) const + { + return peer_count >= (unsigned)limit; + } + + bool operator!=(piece_pos p) const + { return index != p.index || peer_count != p.peer_count; } + + bool operator==(piece_pos p) const + { return index == p.index && peer_count == p.peer_count; } + + }; + + + void add(int index); + void move(bool downloading, bool filtered, int vec_index, int elem_index); + void remove(bool downloading, bool filtered, int vec_index, int elem_index); + std::vector >& pick_piece_info_vector(bool downloading + , bool filtered); + + std::vector > const& pick_piece_info_vector( + bool downloading, bool filtered) const; + + int add_interesting_blocks_free(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces) const; + + int add_interesting_blocks_partial(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer) const; + + + // this vector contains all pieces we don't have. + // in the first entry (index 0) is a vector of all pieces + // that no peer have, the vector at index 1 contains + // all pieces that exactly one peer have, index 2 contains + // all pieces exactly two peers have and so on. + std::vector > m_piece_info; + + // this vector has the same structure as m_piece_info + // but only contains pieces we are currently downloading + // they have higher priority than pieces we aren't downloading + // during piece picking + std::vector > m_downloading_piece_info; + + // 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 + 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; + + 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 required popularity of a piece in order to download + // it in sequence instead of random order. + int m_sequenced_download_threshold; + }; + + inline int piece_picker::blocks_in_piece(int index) const + { + assert(index >= 0); + 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/library/include/libtorrent/policy.hpp b/library/include/libtorrent/policy.hpp new file mode 100755 index 000000000..1358e2bdf --- /dev/null +++ b/library/include/libtorrent/policy.hpp @@ -0,0 +1,245 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + + 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 + }; + + + class TORRENT_EXPORT policy + { + public: + + policy(torrent* t); + + // this is called every 10 seconds to allow + // for peer choking management + void pulse(); + + // this is called once for every peer we get from + // the tracker + void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid); + + // called when an incoming connection is accepted + // return false if the connection closed + void new_connection(peer_connection& c); + + // this is called if a peer timed-out or + // forcefully closed the connection. This + // will mark the connection as non-reconnectale + void peer_failed(peer_connection const& c); + + // the given connection was just closed + void connection_closed(const peer_connection& c); + + // is called when a peer is believed to have + // sent invalid data + void ban_peer(const peer_connection& c); + + // the peer has got at least one interesting piece + void peer_is_interesting(peer_connection& c); + + void piece_finished(int index, bool successfully_verified); + + void block_finished(peer_connection& c, piece_block b); + + // the peer choked us + void choked(peer_connection& c); + + // the peer unchoked us + void unchoked(peer_connection& c); + + // the peer is interested in our pieces + void interested(peer_connection& c); + + // the peer is not interested in our pieces + void not_interested(peer_connection& c); + +#ifndef NDEBUG + bool has_connection(const peer_connection* p); + + void check_invariant() const; +#endif + + struct peer + { + enum connection_type { not_connectable,connectable }; + + peer(const tcp::endpoint& ip, connection_type t); + + size_type total_download() const; + size_type total_upload() const; + + // the ip/port pair this peer is or was connected on + // if it was a remote (incoming) connection, type is + // set thereafter. If it was a peer we got from the + // tracker, type is set to local_connection. + tcp::endpoint ip; + connection_type type; + + // the time when this peer was optimistically unchoked + // the last time. + boost::posix_time::ptime last_optimistically_unchoked; + + // the time when the peer connected to us + // or disconnected if it isn't connected right now + boost::posix_time::ptime connected; + + // 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. + size_type prev_amount_upload; + size_type prev_amount_download; + + // is set to true if this peer has been banned + bool banned; + + // if the peer is connected now, this + // will refer to a valid peer_connection + peer_connection* connection; + }; + + int num_peers() const + { + return m_peers.size(); + } + + int num_uploads() const + { + return m_num_unchoked; + } + + typedef std::vector::iterator iterator; + iterator begin_peer() { return m_peers.begin(); } + iterator end_peer() { return m_peers.end(); } + + private: + + bool unchoke_one_peer(); + void choke_one_peer(); + peer* find_choke_candidate(); + peer* find_unchoke_candidate(); + + // the seed prefix means that the + // function is used while seeding. + bool seed_unchoke_one_peer(); + void seed_choke_one_peer(); + peer* find_seed_choke_candidate(); + peer* find_seed_unchoke_candidate(); + + bool connect_peer(peer *); + bool connect_one_peer(); + bool disconnect_one_peer(); + peer* find_disconnect_candidate(); + peer* find_connect_candidate(); + + // a functor that identifies peers that have disconnected and that + // are too old for still being saved. + struct old_disconnected_peer + { + bool operator()(const peer& p) + { + using namespace boost::posix_time; + + ptime not_tried_yet(boost::gregorian::date(1970,boost::gregorian::Jan,1)); + + // this timeout has to be customizable! + return p.connection == 0 + && p.connected != not_tried_yet + && second_clock::universal_time() - p.connected > minutes(30); + } + }; + + + std::vector m_peers; + + torrent* m_torrent; + + // the number of unchoked peers + // at any given time + int m_num_unchoked; + + // free download we have got that hasn't + // been distributed yet. + size_type m_available_free_upload; + + // if there is a connection limit, + // we disconnect one peer every minute in hope of + // establishing a connection with a better peer + boost::posix_time::ptime m_last_optimistic_disconnect; + }; + +} + +#endif // TORRENT_POLICY_HPP_INCLUDED + diff --git a/library/include/libtorrent/random_sample.hpp b/library/include/libtorrent/random_sample.hpp new file mode 100644 index 000000000..741576fd9 --- /dev/null +++ b/library/include/libtorrent/random_sample.hpp @@ -0,0 +1,72 @@ +/* + +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_RANDOM_SAMPLE_HPP +#define TORRENT_RANDOM_SAMPLE_HPP + +#include +#include + +namespace libtorrent +{ + + template + inline void random_sample_n(InIter start, InIter end + , OutIter out, Distance n) + { + Distance t = 0; + Distance m = 0; + Distance N = std::distance(start, end); + + assert(N >= n); + + while (m < n) + { + if ((rand() / (RAND_MAX + 1.f)) * (N - t) >= n - m) + { + ++start; + ++t; + } + else + { + *out = *start; + ++out; + ++start; + ++t; + ++m; + } + } + } + +} + +#endif diff --git a/library/include/libtorrent/resource_request.hpp b/library/include/libtorrent/resource_request.hpp new file mode 100755 index 000000000..046b49df9 --- /dev/null +++ b/library/include/libtorrent/resource_request.hpp @@ -0,0 +1,91 @@ +/* + +Copyright (c) 2003, Magnus Jonsson, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 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_RESOURCE_REQUEST_HPP_INCLUDED +#define TORRENT_RESOURCE_REQUEST_HPP_INCLUDED + +#include + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT resource_request + { + resource_request() + : used(0) + , min(0) + , max(0) + , given(0) + {} + + resource_request(int used_, int min_, int max_, int given_) + : used(used_) + , min(min_) + , max(max_) + , given(given_) + {} + + int left() const + { + assert(given <= max); + assert(given >= min); + if (used < 0 && (given - used < 0)) + return boost::integer_traits::const_max; + return given - used; + } + + static const int inf = boost::integer_traits::const_max; + + // right now I'm actively using this amount + int used; + + // given cannot be smaller than min + // and not greater than max. + int min; + int max; + + // Reply: Okay, you're allowed to use this amount (a compromise): + int given; + }; +} + + +#endif diff --git a/library/include/libtorrent/session.hpp b/library/include/libtorrent/session.hpp new file mode 100755 index 000000000..d62d69493 --- /dev/null +++ b/library/include/libtorrent/session.hpp @@ -0,0 +1,226 @@ +/* + +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 +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/fingerprint.hpp" + + +#if !defined(NDEBUG) && defined(_MSC_VER) +# include +# include +#endif + +namespace libtorrent +{ + class torrent; + class ip_filter; + + enum extension_index + { + extended_handshake, + extended_chat_message, + extended_metadata_message, + extended_peer_exchange_message, + num_supported_extensions + }; + + namespace aux + { + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff +#if defined(_MSC_VER) + struct eh_initializer + { + eh_initializer() + { +#ifndef NDEBUG + _clearfp(); + _controlfp(_EM_INEXACT | _EM_UNDERFLOW, _MCW_EM ); + ::_set_se_translator(straight_to_debugger); +#endif + } + + 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; + }; + + 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)); + session( + fingerprint const& print + , std::pair listen_port_range + , char const* listen_interface = "0.0.0.0"); + + ~session(); + + std::vector get_torrents() const; + + // all torrent_handles must be destructed before the session is destructed! + torrent_handle add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024); + + // TODO: deprecated, this is for backwards compatibility only + torrent_handle add_torrent( + entry const& e + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024) + { + return add_torrent(torrent_info(e), save_path, resume_data + , compact_mode, block_size); + } + + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024); + + session_proxy abort() { return session_proxy(m_impl); } + + session_status status() const; + +#ifndef TORRENT_DISABLE_DHT + void start_dht(entry const& startup_state = entry()); + 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); +#endif + + void enable_extension(extension_index i); + void disable_extensions(); + + void set_ip_filter(ip_filter const& f); + void set_peer_id(peer_id const& pid); + void set_key(int key); + + 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. + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0); + + // returns the port we ended up listening on + unsigned short listen_port() const; + + void remove_torrent(const torrent_handle& h); + + void set_settings(session_settings const& s); + session_settings const& settings(); + void set_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); + void set_max_uploads(int limit); + void set_max_connections(int limit); + void set_max_half_open_connections(int limit); + + std::auto_ptr pop_alert(); + void set_severity_level(alert::severity_t s); + + private: + + // data shared between the main thread + // and the working thread + boost::shared_ptr m_impl; + }; + +} + +#endif // TORRENT_SESSION_HPP_INCLUDED + diff --git a/library/include/libtorrent/session_settings.hpp b/library/include/libtorrent/session_settings.hpp new file mode 100644 index 000000000..80d328611 --- /dev/null +++ b/library/include/libtorrent/session_settings.hpp @@ -0,0 +1,164 @@ +/* + +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" + +namespace libtorrent +{ + + struct TORRENT_EXPORT session_settings + { + session_settings(std::string const& user_agent_ = "libtorrent/" + LIBTORRENT_VERSION) + : proxy_port(0) + , user_agent(user_agent_) + , tracker_completion_timeout(60) + , tracker_receive_timeout(20) + , stop_tracker_timeout(10) + , tracker_maximum_response_length(1024*1024) + , piece_timeout(120) + , request_queue_time(3.f) + , 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) + {} + + std::string proxy_ip; + int proxy_port; + std::string proxy_login; + std::string proxy_password; + + // 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 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. + float 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; + }; + +#ifndef TORRENT_DISABLE_DHT + struct dht_settings + { + dht_settings() + : max_peers_reply(50) + , search_branching(5) + , service_port(6881) + , max_fail_count(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; + + // the listen port for the dht. This is a UDP port. + int service_port; + + // the maximum number of times a node can fail + // in a row before it is removed from the table. + int max_fail_count; + }; +#endif + +} + +#endif diff --git a/library/include/libtorrent/session_status.hpp b/library/include/libtorrent/session_status.hpp new file mode 100644 index 000000000..7d12674fd --- /dev/null +++ b/library/include/libtorrent/session_status.hpp @@ -0,0 +1,68 @@ +/* + +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 + + +namespace libtorrent +{ + + struct TORRENT_EXPORT session_status + { + bool has_incoming_connections; + + float upload_rate; + float download_rate; + + float payload_upload_rate; + float payload_download_rate; + + size_type total_download; + size_type total_upload; + + size_type total_payload_download; + size_type total_payload_upload; + + int num_peers; + +#ifndef TORRENT_DISABLE_DHT + int m_dht_nodes; + int m_dht_node_cache; + int m_dht_torrents; +#endif + }; + +} + +#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED + diff --git a/library/include/libtorrent/size_type.hpp b/library/include/libtorrent/size_type.hpp new file mode 100755 index 000000000..6020a5ac3 --- /dev/null +++ b/library/include/libtorrent/size_type.hpp @@ -0,0 +1,52 @@ +/* + +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; +} + + +#endif diff --git a/library/include/libtorrent/socket.hpp b/library/include/libtorrent/socket.hpp new file mode 100755 index 000000000..394c2a133 --- /dev/null +++ b/library/include/libtorrent/socket.hpp @@ -0,0 +1,156 @@ +/* + +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 + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#undef Protocol +#endif + +#include "libtorrent/io.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ +/* + namespace asio = boost::asio; + + using boost::asio::ipv4::tcp; + using boost::asio::ipv4::address; + using boost::asio::stream_socket; + using boost::asio::datagram_socket; + using boost::asio::socket_acceptor; + using boost::asio::demuxer; + using boost::asio::ipv4::host_resolver; + using boost::asio::async_write; + using boost::asio::ipv4::host; + using boost::asio::deadline_timer; +*/ +// namespace asio = ::asio; + + using asio::ip::tcp; + using asio::ip::udp; + typedef asio::ip::tcp::socket stream_socket; + typedef asio::ip::address address; + typedef asio::ip::address_v4 address_v4; + typedef asio::ip::address_v6 address_v6; + typedef asio::ip::udp::socket datagram_socket; + typedef asio::ip::tcp::acceptor socket_acceptor; + typedef asio::io_service demuxer; + + using asio::async_write; + using asio::deadline_timer; + + namespace detail + { + template + void write_address(address const& a, OutIt& out) + { + if (a.is_v4()) + { + write_uint32(a.to_v4().to_ulong(), out); + } + else if (a.is_v6()) + { + asio::ip::address_v6::bytes_type bytes + = a.to_v6().to_bytes(); + std::copy(bytes.begin(), bytes.end(), out); + } + } + + template + address read_v4_address(InIt& in) + { + unsigned long ip = read_uint32(in); + return asio::ip::address_v4(ip); + } + + template + address read_v6_address(InIt& in) + { + typedef asio::ip::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 asio::ip::address_v6(bytes); + } + + 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); + } + + template + Endpoint read_v6_endpoint(InIt& in) + { + address addr = read_v6_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } + } +} + +#endif // TORRENT_SOCKET_HPP_INCLUDED + diff --git a/library/include/libtorrent/stat.hpp b/library/include/libtorrent/stat.hpp new file mode 100755 index 000000000..8e92c12a1 --- /dev/null +++ b/library/include/libtorrent/stat.hpp @@ -0,0 +1,193 @@ +/* + +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 "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class TORRENT_EXPORT stat + { + friend class invariant_access; + enum { history = 10 }; + public: + + stat() + : m_downloaded_payload(0) + , m_uploaded_payload(0) + , m_downloaded_protocol(0) + , m_uploaded_protocol(0) + , m_total_download_payload(0) + , m_total_upload_payload(0) + , m_total_download_protocol(0) + , m_total_upload_protocol(0) + , m_mean_download_rate(0) + , m_mean_upload_rate(0) + , m_mean_download_payload_rate(0) + , m_mean_upload_payload_rate(0) + { + std::fill(m_download_rate_history, m_download_rate_history+history, 0.f); + std::fill(m_upload_rate_history, m_upload_rate_history+history, 0.f); + std::fill(m_download_payload_rate_history, m_download_payload_rate_history+history, 0.f); + std::fill(m_upload_payload_rate_history, m_upload_payload_rate_history+history, 0.f); + } + + void operator+=(const stat& s) + { + INVARIANT_CHECK; + + m_downloaded_payload += s.m_downloaded_payload; + m_total_download_payload += s.m_downloaded_payload; + m_downloaded_protocol += s.m_downloaded_protocol; + m_total_download_protocol += s.m_downloaded_protocol; + + m_uploaded_payload += s.m_uploaded_payload; + m_total_upload_payload += s.m_uploaded_payload; + m_uploaded_protocol += s.m_uploaded_protocol; + m_total_upload_protocol += s.m_uploaded_protocol; + } + + void received_bytes(int bytes_payload, int bytes_protocol) + { + INVARIANT_CHECK; + + assert(bytes_payload >= 0); + assert(bytes_protocol >= 0); + + m_downloaded_payload += bytes_payload; + m_total_download_payload += bytes_payload; + m_downloaded_protocol += bytes_protocol; + m_total_download_protocol += bytes_protocol; + } + + void sent_bytes(int bytes_payload, int bytes_protocol) + { + INVARIANT_CHECK; + + assert(bytes_payload >= 0); + assert(bytes_protocol >= 0); + + m_uploaded_payload += bytes_payload; + m_total_upload_payload += bytes_payload; + m_uploaded_protocol += bytes_protocol; + m_total_upload_protocol += bytes_protocol; + } + + // should be called once every second + void second_tick(float tick_interval); + + float upload_rate() const { return m_mean_upload_rate; } + float download_rate() const { return m_mean_download_rate; } + + float upload_payload_rate() const { return m_mean_upload_payload_rate; } + float download_payload_rate() const { return m_mean_download_payload_rate; } + + size_type total_payload_upload() const { return m_total_upload_payload; } + size_type total_payload_download() const { return m_total_download_payload; } + + size_type total_protocol_upload() const { return m_total_upload_protocol; } + size_type total_protocol_download() const { return m_total_download_protocol; } + + // 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_total_download_payload += downloaded; + m_total_upload_payload += uploaded; + } + + private: + +#ifndef NDEBUG + void check_invariant() const + { + assert(m_mean_upload_rate >= 0); + assert(m_mean_download_rate >= 0); + assert(m_mean_upload_payload_rate >= 0); + assert(m_mean_download_payload_rate >= 0); + assert(m_total_upload_payload >= 0); + assert(m_total_download_payload >= 0); + assert(m_total_upload_protocol >= 0); + assert(m_total_download_protocol >= 0); + } +#endif + + // history of download/upload speeds a few seconds back + float m_download_rate_history[history]; + float m_upload_rate_history[history]; + + float m_download_payload_rate_history[history]; + float m_upload_payload_rate_history[history]; + + // the accumulators we are adding the downloads/uploads + // to this second. This only counts the actual payload + // and ignores the bytes sent as protocol chatter. + int m_downloaded_payload; + int m_uploaded_payload; + + // the accumulators we are adding the downloads/uploads + // to this second. This only counts the protocol + // chatter and ignores the actual payload + int m_downloaded_protocol; + int m_uploaded_protocol; + + // total download/upload counters + // only counting payload data + size_type m_total_download_payload; + size_type m_total_upload_payload; + + // total download/upload counters + // only counting protocol chatter + size_type m_total_download_protocol; + size_type m_total_upload_protocol; + + // current mean download/upload rates + float m_mean_download_rate; + float m_mean_upload_rate; + + float m_mean_download_payload_rate; + float m_mean_upload_payload_rate; + }; + +} + +#endif // TORRENT_STAT_HPP_INCLUDED diff --git a/library/include/libtorrent/storage.hpp b/library/include/libtorrent/storage.hpp new file mode 100755 index 000000000..44f956786 --- /dev/null +++ b/library/include/libtorrent/storage.hpp @@ -0,0 +1,178 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux + { + struct piece_checker_data; + } + + class session; + +#if defined(_WIN32) && defined(UNICODE) + + TORRENT_EXPORT std::wstring safe_convert(std::string const& s); + +#endif + + TORRENT_EXPORT std::vector > get_filesizes( + torrent_info const& t + , boost::filesystem::path p); + + TORRENT_EXPORT bool match_filesizes( + torrent_info const& t + , boost::filesystem::path p + , std::vector > const& sizes + , 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; + }; + + class TORRENT_EXPORT storage + { + public: + storage( + const torrent_info& info + , const boost::filesystem::path& path); + + void swap(storage&); + + // may throw file_error if storage for slot does not exist + size_type read(char* buf, int slot, int offset, int size); + + // may throw file_error if storage for slot hasn't been allocated + void write(const char* buf, int slot, int offset, int size); + + bool move_storage(boost::filesystem::path save_path); + + // this will close all open files that are opened for + // writing. This is called when a torrent has finished + // downloading. + void release_files(); + +#ifndef NDEBUG + // overwrites some slots with the + // contents of others + void shuffle(); +#endif + + private: + class impl; + boost::shared_ptr m_pimpl; + }; + + class TORRENT_EXPORT piece_manager : boost::noncopyable + { + public: + + piece_manager( + const torrent_info& info + , const boost::filesystem::path& path); + + ~piece_manager(); + + bool check_fastresume(aux::piece_checker_data& d + , std::vector& pieces, int& num_pieces, bool compact_mode); + std::pair check_files(std::vector& pieces + , int& num_pieces); + + void release_files(); + + bool is_allocating() const; + void allocate_slots(int num_slots); + void mark_failed(int index); + + unsigned long piece_crc( + int slot_index + , int block_size + , const std::bitset<256>& bitmask); + int slot_for_piece(int piece_index) const; + + size_type read( + char* buf + , int piece_index + , int offset + , int size); + + void write( + const char* buf + , int piece_index + , int offset + , int size); + + boost::filesystem::path const& save_path() const; + bool move_storage(boost::filesystem::path const&); + + // fills the vector that maps all allocated + // slots to the piece that is stored (or + // partially stored) there. -2 is the index + // of unassigned pieces and -1 is unallocated + void export_piece_map(std::vector& pieces) const; + + private: + class impl; + std::auto_ptr m_pimpl; + }; + +} + +#endif // TORRENT_STORAGE_HPP_INCLUDED + diff --git a/library/include/libtorrent/torrent.hpp b/library/include/libtorrent/torrent.hpp new file mode 100755 index 000000000..17bdc50d7 --- /dev/null +++ b/library/include/libtorrent/torrent.hpp @@ -0,0 +1,654 @@ +/* + +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 +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/resource_request.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" + +namespace libtorrent +{ +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + struct logger; +#endif + + class piece_manager; + + namespace aux + { + struct session_impl; + struct piece_checker_data; + } + + int div_round_up(int numerator, int denominator); + std::pair req_to_offset(std::pair req, int total_size); + std::pair offset_to_req(std::pair offset, int total_size); + + // 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 + , aux::checker_impl& checker + , torrent_info const& tf + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s); + + // used with metadata-less torrents + // (the metadata is downloaded from the peers) + torrent( + aux::session_impl& ses + , aux::checker_impl& checker + , char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s); + + ~torrent(); + + // this is called when the torrent has metadata. + // it will initialize the storage and the piece-picker + void init(); + + // 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; } + + // returns true if this torrent is being allocated + // by the checker thread. + bool is_allocating() const; + + session_settings const& settings() const; + + void set_sequenced_download_threshold(int threshold); + + // is called every second by session. This will + // caclulate the upload/download and number + // of connections this torrent needs. And prepare + // it for being used by allocate_resources. + void second_tick(stat& accumulator, float tick_interval); + + // debug purpose only + void print(std::ostream& os) const; + + // this is called from the peer_connection for + // each piece of metadata it receives + void metadata_progress(int total_size, int received); + + bool check_fastresume(aux::piece_checker_data&); + std::pair check_files(); + void files_checked(std::vector const& + unfinished_pieces); + + stat statistics() const { return m_stat; } + size_type bytes_left() const; + boost::tuples::tuple bytes_done() const; + size_type quantized_bytes_done() const; + + void pause(); + void resume(); + bool is_paused() const { return m_paused; } + + 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); + + torrent_status status() const; + void file_progress(std::vector& fp) const; + + void use_interface(const char* net_interface); + tcp::endpoint const& get_interface() const { return m_net_interface; } + + void connect_to_url_seed(std::string const& url); + peer_connection& connect_to_peer(tcp::endpoint const& a); + + void set_ratio(float ratio) + { assert(ratio >= 0.0f); m_ratio = ratio; } + + float ratio() const + { return m_ratio; } + +// -------------------------------------------- + // PEER MANAGEMENT + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void add_url_seed(std::string const& url) + { m_web_seeds.insert(url); } + + void remove_url_seed(std::string const& url) + { m_web_seeds.erase(url); } + + // 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. + void 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); + + peer_connection* connection_for(tcp::endpoint const& a) + { + peer_iterator i = m_connections.find(a); + if (i == m_connections.end()) return 0; + return i->second; + } + + // the number of peers that belong to this torrent + int num_peers() const { return (int)m_connections.size(); } + int num_seeds() const; + + typedef std::map::iterator peer_iterator; + typedef std::map::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(); } + + +// -------------------------------------------- + // 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 + , std::vector& e, int interval + , int complete, int incomplete); + virtual void tracker_request_timed_out( + tracker_request const& r); + virtual void tracker_request_error(tracker_request const& r + , int response_code, const std::string& str); + virtual void tracker_warning(std::string const& msg); + + // generates a request string for sending + // to the tracker + tracker_request generate_tracker_request(); + + // 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. + boost::posix_time::ptime next_announce() const; + + // returns true if it is time for this torrent to make another + // tracker request + bool should_request(); + + // forcefully sets next_announce to the current time + void force_tracker_request(); + void force_tracker_request(boost::posix_time::ptime); + + // 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 + const tcp::endpoint& current_tracker() const; + +// -------------------------------------------- + // PIECE MANAGEMENT + + // returns true if we have downloaded the given piece + bool have_piece(int index) const + { + assert(index >= 0 && index < (signed)m_have_pieces.size()); + return m_have_pieces[index]; + } + + const std::vector& pieces() const + { return m_have_pieces; } + + int num_pieces() const { return m_num_pieces; } + + // when we get a have- or bitfield- messages, this is called for every + // piece a peer has gained. + void peer_has(int index) + { + assert(m_picker.get()); + assert(index >= 0 && index < (signed)m_have_pieces.size()); + m_picker->inc_refcount(index); + } + + // when peer disconnects, this is called for every piece it had + void peer_lost(int index) + { + assert(m_picker.get()); + assert(index >= 0 && index < (signed)m_have_pieces.size()); + m_picker->dec_refcount(index); + } + + int block_size() const { assert(m_block_size > 0); return m_block_size; } + + // 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 announce_piece(int index); + + void disconnect_all(); + + // 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(); + + // this is the asio callback that is called when a name + // lookup for a web seed is completed. + void on_name_lookup(asio::error const& e, tcp::resolver::iterator i + , std::string 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(); + + bool verify_piece(int piece_index); + + // this is called from the peer_connection + // each time a piece has failed the hash + // test + void piece_failed(int index); + void received_redundant_data(int num_bytes) + { assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; } + + float priority() const + { return m_priority; } + + void set_priority(float p) + { + assert(p >= 0.f && p <= 1.f); + m_priority = p; + } + + bool is_seed() const + { + return valid_metadata() + && m_num_pieces == m_torrent_file.num_pieces(); + } + + boost::filesystem::path save_path() const; + alert_manager& alerts() const; + piece_picker& picker() + { + assert(m_picker.get()); + return *m_picker; + } + policy& get_policy() + { + assert(m_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); + + torrent_handle get_handle() const; + + // LOGGING +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + logger* spawn_logger(const char* title); + + virtual void debug_log(const std::string& line); +#endif + + // DEBUG +#ifndef NDEBUG + void check_invariant() const; +#endif + +// -------------------------------------------- + // RESOURCE MANAGEMENT + + // this will distribute the given upload/download + // quotas and number of connections, among the peers + void distribute_resources(); + + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + resource_request m_uploads_quota; + resource_request m_connections_quota; + + 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); + void set_download_limit(int limit); + void set_max_uploads(int limit); + void set_max_connections(int limit); + bool move_storage(boost::filesystem::path const& save_path); + + // 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_storage.get() != 0; } + std::vector const& metadata() const; + + bool received_metadata( + char const* buf + , int size + , int offset + , int total_size); + + // returns a range of the metadata that + // we should request. + std::pair metadata_request(); + void cancel_metadata_request(std::pair req); + + private: + + void try_next_tracker(); + int prioritize_tracker(int tracker_index); + + torrent_info m_torrent_file; + + // is set to true when the torrent has + // been aborted. + bool m_abort; + + // is true if this torrent has been paused + bool m_paused; + // this is true from the time when the torrent was + // paused to the time should_request() is called + bool m_just_paused; + + tracker_request::event_t m_event; + + void parse_response(const entry& e, std::vector& peer_list); + + // the size of a request block + // each piece is divided into these + // blocks when requested + int m_block_size; + + // if this pointer is 0, the torrent is in + // a state where the metadata hasn't been + // received yet. + boost::scoped_ptr m_storage; + + // the time of next tracker request + boost::posix_time::ptime m_next_request; + + // ----------------------------- + // DATA FROM TRACKER RESPONSE + + // the number number of seconds between requests + // from the tracker + int m_duration; + + // the scrape data from the tracker response, this + // is optional and may be -1. + int m_complete; + int m_incomplete; + +#ifndef NDEBUG + public: +#endif + std::map m_connections; +#ifndef NDEBUG + private: +#endif + + // The list of web seeds in this torrent. Seeds + // with fatal errors are removed from the set + std::set m_web_seeds; + + // urls of the web seeds that we are currently + // resolving the address for + std::set m_resolving_web_seeds; + + // used to resolve the names of web seeds + tcp::resolver m_host_resolver; + +#ifndef TORRENT_DISABLE_DHT + static void on_dht_announce_response_disp(boost::weak_ptr t + , std::vector const& peers); + deadline_timer m_dht_announce_timer; + void on_dht_announce(asio::error const& e); + void on_dht_announce_response(std::vector const& peers); +#endif + + // 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; + + // ----------------------------- + + boost::shared_ptr m_policy; + + // a back reference to the session + // this torrent belongs to. + aux::session_impl& m_ses; + aux::checker_impl& m_checker; + + boost::scoped_ptr m_picker; + + std::vector m_trackers; + // this is an index into m_torrent_file.trackers() + int m_last_working_tracker; + int m_currently_trying_tracker; + // the number of connection attempts that has + // failed in a row, this is currently used to + // determine the timeout until next try. + int m_failed_trackers; + + // this is a counter that is increased every + // second, and when it reaches 10, the policy::pulse() + // is called and the time scaler is reset to 0. + int m_time_scaler; + + // this is the priority of this torrent. It is used + // to weight the assigned upload bandwidth between peers + // it should be within the range [0, 1] + float m_priority; + + // the bitmask that says which pieces we have + std::vector m_have_pieces; + + // the number of pieces we have. The same as + // std::accumulate(m_have_pieces.begin(), + // m_have_pieces.end(), 0) + int m_num_pieces; + + // is false by default and set to + // true when the first tracker reponse + // is received + bool m_got_tracker_response; + + // the upload/download ratio that each peer + // tries to maintain. + // 0 is infinite + float m_ratio; + + // the number of bytes that has been + // downloaded that failed the hash-test + size_type m_total_failed_bytes; + size_type m_total_redundant_bytes; + + std::string m_username; + std::string m_password; + + // the network interface all outgoing connections + // are opened through + tcp::endpoint m_net_interface; + + // the max number of bytes this torrent + // can upload per second + int m_upload_bandwidth_limit; + int m_download_bandwidth_limit; + + // 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 std::vector m_metadata; + + // 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; + + boost::filesystem::path m_save_path; + + // determines the storage state for this torrent. + const bool m_compact_mode; + + int m_metadata_progress; + int m_metadata_size; + + // defaults to 16 kiB, but can be set by the user + // when creating the torrent + const int m_default_block_size; + + // 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; + + session_settings const& m_settings; + +#ifndef NDEBUG + // this is the amount downloaded when this torrent + // is started. i.e. + // total_done - m_initial_done <= total_payload_download + size_type m_initial_done; +#endif + }; + + inline boost::posix_time::ptime torrent::next_announce() const + { + return m_next_request; + } + + inline void torrent::force_tracker_request() + { + using boost::posix_time::second_clock; + m_next_request = second_clock::universal_time(); + } + + inline void torrent::force_tracker_request(boost::posix_time::ptime t) + { + namespace time = boost::posix_time; + m_next_request = t; + } + + inline void torrent::set_tracker_login( + std::string const& name + , std::string const& pw) + { + m_username = name; + m_password = pw; + } + +} + +#endif // TORRENT_TORRENT_HPP_INCLUDED + diff --git a/library/include/libtorrent/torrent_handle.hpp b/library/include/libtorrent/torrent_handle.hpp new file mode 100755 index 000000000..80d57fec7 --- /dev/null +++ b/library/include/libtorrent/torrent_handle.hpp @@ -0,0 +1,350 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux + { + struct session_impl; + struct checker_impl; + } + + struct TORRENT_EXPORT duplicate_torrent: std::exception + { + virtual const char* what() const throw() + { return "torrent already exists in session"; } + }; + + struct TORRENT_EXPORT invalid_handle: std::exception + { + virtual const char* what() const throw() + { return "invalid torrent handle used"; } + }; + + struct TORRENT_EXPORT torrent_status + { + torrent_status() + : state(queued_for_checking) + , paused(false) + , progress(0.f) + , 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_peers(0) + , num_complete(-1) + , num_incomplete(-1) + , pieces(0) + , num_pieces(0) + , total_done(0) + , total_wanted_done(0) + , total_wanted(0) + , num_seeds(0) + , distributed_copies(0.f) + , block_size(0) + {} + + enum state_t + { + queued_for_checking, + checking_files, + connecting_to_tracker, + downloading_metadata, + downloading, + finished, + seeding, + allocating + }; + + state_t state; + bool paused; + float progress; + 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 + float download_rate; + float upload_rate; + + // the rate of payload that is + // sent and received + float download_payload_rate; + float upload_payload_rate; + + // the number of peers this torrent + // is connected to. + 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; + + const std::vector* 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 peers this torrent is connected to + // that are seeding. + int num_seeds; + + // the number of distributed copies of the file. + // note that one copy may be spread out among many peers. + // + // the whole number part tells how many copies + // there are of the rarest piece(s) + // + // the fractional part tells the fraction of pieces that + // have more copies than the rarest piece(s). + float distributed_copies; + + // the 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; + }; + + struct TORRENT_EXPORT partial_piece_info + { + enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece }; + int piece_index; + int blocks_in_piece; + std::bitset requested_blocks; + std::bitset finished_blocks; + tcp::endpoint peer[max_blocks_per_piece]; + int num_downloads[max_blocks_per_piece]; + }; + + struct TORRENT_EXPORT torrent_handle + { + friend class invariant_access; + friend struct aux::session_impl; + friend class torrent; + + torrent_handle(): m_ses(0), m_chk(0) {} + + void get_peer_info(std::vector& v) const; + bool send_chat_message(tcp::endpoint ip, std::string message) const; + torrent_status status() const; + void get_download_queue(std::vector& queue) const; + + // 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. + void file_progress(std::vector& progress); + + std::vector const& trackers() const; + void replace_trackers(std::vector const&) const; + + void add_url_seed(std::string const& url); + + bool has_metadata() const; + const torrent_info& get_torrent_info() const; + bool is_valid() const; + + bool is_seed() const; + bool is_paused() const; + void pause() const; + void resume() const; + + // marks the piece with the given index as filtered + // it will not be downloaded + void filter_piece(int index, bool filter) const; + void filter_pieces(std::vector const& pieces) const; + bool is_piece_filtered(int index) const; + std::vector filtered_pieces() const; + + // marks the file with the given index as filtered + // it will not be downloaded + void filter_files(std::vector const& files) const; + + // set the interface to bind outgoing connections + // to. + void use_interface(const char* net_interface) const; + + entry write_resume_data() const; + + // kind of similar to get_torrent_info() but this + // is lower level, returning the exact info-part of + // the .torrent file. When hashed, this buffer + // will produce the info hash. The reference is valid + // only as long as the torrent is running. + std::vector const& metadata() const; + + // forces this torrent to reannounce + // (make a rerequest from the tracker) + void force_reannounce() const; + + // 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; + + // 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; + void set_download_limit(int limit) const; + void set_sequenced_download_threshold(int threshold) 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) 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; + + boost::filesystem::path save_path() const; + + // -1 means unlimited unchokes + void set_max_uploads(int max_uploads) const; + + // -1 means unlimited connections + void set_max_connections(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 + bool move_storage(boost::filesystem::path const& save_path) const; + + const sha1_hash& info_hash() const + { return m_info_hash; } + + bool operator==(const torrent_handle& h) const + { return m_info_hash == h.m_info_hash; } + + bool operator!=(const torrent_handle& h) const + { return m_info_hash != h.m_info_hash; } + + bool operator<(const torrent_handle& h) const + { return m_info_hash < h.m_info_hash; } + + private: + + torrent_handle(aux::session_impl* s, + aux::checker_impl* c, + const sha1_hash& h) + : m_ses(s) + , m_chk(c) + , m_info_hash(h) + { + assert(m_ses != 0); + } + +#ifndef NDEBUG + void check_invariant() const; +#endif + + aux::session_impl* m_ses; + aux::checker_impl* m_chk; + sha1_hash m_info_hash; + + }; + + +} + +#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED diff --git a/library/include/libtorrent/torrent_info.hpp b/library/include/libtorrent/torrent_info.hpp new file mode 100755 index 000000000..4e6e20d9e --- /dev/null +++ b/library/include/libtorrent/torrent_info.hpp @@ -0,0 +1,242 @@ +/* + +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_INFO_HPP_INCLUDE +#define TORRENT_TORRENT_INFO_HPP_INCLUDE + + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT file_entry + { + boost::filesystem::path path; + size_type offset; // the offset of this file inside the torrent + size_type size; // the size of this file + // if the path was incorrectly encoded, this is + // the origianal corrupt encoded string. It is + // preserved in order to be able to reproduce + // the correct info-hash + boost::shared_ptr orig_path; + }; + + struct TORRENT_EXPORT file_slice + { + int file_index; + size_type offset; + size_type size; + }; + + struct TORRENT_EXPORT announce_entry + { + announce_entry(std::string const& u): url(u), tier(0) {} + std::string url; + int tier; + }; + + struct TORRENT_EXPORT invalid_torrent_file: std::exception + { + virtual const char* what() const throw() { return "invalid torrent file"; } + }; + + class TORRENT_EXPORT torrent_info + { + public: + + torrent_info(); + torrent_info(sha1_hash const& info_hash); + torrent_info(entry const& torrent_file); + ~torrent_info(); + + entry create_torrent() const; + entry create_info_metadata() const; + void set_comment(char const* str); + void set_creator(char const* str); + void set_piece_size(int size); + void set_hash(int index, sha1_hash const& h); + void add_tracker(std::string const& url, int tier = 0); + void add_file(boost::filesystem::path file, size_type size); + void add_url_seed(std::string const& url); + + std::vector map_block(int piece, size_type offset, int size) const; + peer_request map_file(int file, size_type offset, int size) const; + + std::vector const& url_seeds() const { return m_url_seeds; } + + typedef std::vector::const_iterator file_iterator; + typedef std::vector::const_reverse_iterator reverse_file_iterator; + + // list the files in the torrent file + 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 + { assert(m_piece_length > 0); return (int)m_files.size(); } + const file_entry& file_at(int index) const + { assert(index >= 0 && index < (int)m_files.size()); return m_files[index]; } + + const std::vector& trackers() const { return m_urls; } + + size_type total_size() const { assert(m_piece_length > 0); return m_total_size; } + size_type piece_length() const { assert(m_piece_length > 0); return m_piece_length; } + int num_pieces() const { assert(m_piece_length > 0); return (int)m_piece_hash.size(); } + const sha1_hash& info_hash() const { return m_info_hash; } + const std::string& name() const { assert(m_piece_length > 0); return m_name; } + void print(std::ostream& os) const; + bool is_valid() const { return m_piece_length > 0; } + + bool priv() const { return m_private; } + void set_priv(bool v) { m_private = v; } + + void convert_file_names(); + + size_type piece_size(int index) const; + + const sha1_hash& hash_for_piece(int index) const + { + assert(index >= 0); + assert(index < (int)m_piece_hash.size()); + return m_piece_hash[index]; + } + + 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); + + void parse_info_section(entry const& e); + + private: + + void read_torrent_info(const entry& libtorrent); + + // the urls to the trackers + std::vector m_urls; + + std::vector m_url_seeds; + + // the length of one piece + // if this is 0, the torrent_info is + // in an uninitialized state + size_type m_piece_length; + + // the sha-1 hashes of each piece + std::vector m_piece_hash; + + // the list of files that this torrent consists of + std::vector m_files; + + nodes_t m_nodes; + + // the sum of all filesizes + size_type m_total_size; + + // the hash that identifies this torrent + // is mutable because it's calculated + // lazily + mutable sha1_hash m_info_hash; + + std::string m_name; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + boost::posix_time::ptime 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; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private; + + // contains any non-parsed entries from the info-section + // these are kept in order to be able to accurately + // reproduce the info-section when sending the metadata + // to peers. + entry m_extra_info; + }; + +} + +#endif // TORRENT_TORRENT_INFO_HPP_INCLUDED + diff --git a/library/include/libtorrent/tracker_manager.hpp b/library/include/libtorrent/tracker_manager.hpp new file mode 100755 index 000000000..a45f88b59 --- /dev/null +++ b/library/include/libtorrent/tracker_manager.hpp @@ -0,0 +1,248 @@ +/* + +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 +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct request_callback; + class tracker_manager; + struct timeout_handler; + struct tracker_connection; + + // encodes a string using the base64 scheme + TORRENT_EXPORT std::string base64encode(const std::string& s); + + // returns -1 if gzip header is invalid or the header size in bytes + TORRENT_EXPORT int gzip_header(const char* buf, int size); + + TORRENT_EXPORT boost::tuple + parse_url_components(std::string url); + + struct TORRENT_EXPORT tracker_request + { + tracker_request() + : kind(announce_request) + , event(none) + , key(0) + , num_want(0) + {} + + enum + { + announce_request, + scrape_request + } kind; + + enum event_t + { + none, + completed, + started, + stopped + }; + + sha1_hash info_hash; + peer_id pid; + size_type downloaded; + size_type uploaded; + size_type left; + unsigned short listen_port; + event_t event; + std::string url; + int key; + int num_want; + }; + + struct TORRENT_EXPORT request_callback + { + friend class tracker_manager; + request_callback(): m_manager(0) {} + virtual ~request_callback() {} + virtual void tracker_warning(std::string const& msg) = 0; + virtual void tracker_response( + tracker_request const& + , std::vector& peers + , int interval + , int complete + , int incomplete) = 0; + virtual void tracker_request_timed_out( + tracker_request const&) = 0; + virtual void tracker_request_error( + tracker_request const& + , int response_code + , const std::string& description) = 0; + + tcp::endpoint m_tracker_address; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + virtual void debug_log(const std::string& line) = 0; +#endif + private: + tracker_manager* m_manager; + }; + + TORRENT_EXPORT bool inflate_gzip( + std::vector& buffer + , tracker_request const& req + , request_callback* requester + , int maximum_tracker_response_length); + + TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); + TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); + + struct TORRENT_EXPORT timeout_handler + : boost::noncopyable + { + friend void intrusive_ptr_add_ref(timeout_handler const*); + friend void intrusive_ptr_release(timeout_handler const*); + + timeout_handler(demuxer& d); + + void set_timeout(int completion_timeout, int read_timeout); + void restart_read_timeout(); + void cancel(); + + virtual void on_timeout() = 0; + virtual ~timeout_handler() {} + + private: + + void timeout_callback(asio::error const&); + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + demuxer& m_demuxer; + // used for timeouts + // this is set when the request has been sent + boost::posix_time::ptime m_start_time; + // this is set every time something is received + boost::posix_time::ptime m_read_time; + // the asio async operation + asio::deadline_timer m_timeout; + + int m_completion_timeout; + int m_read_timeout; + + typedef boost::mutex mutex_t; + mutable mutex_t m_mutex; + mutable int m_refs; + }; + + struct TORRENT_EXPORT tracker_connection + : timeout_handler + { + tracker_connection(tracker_manager& man + , tracker_request req + , demuxer& d + , boost::weak_ptr r); + + request_callback& requester(); + virtual ~tracker_connection() {} + + tracker_request const& tracker_req() const { return m_req; } + bool has_requester() const { return !m_requester.expired(); } + + void fail(int code, char const* msg); + void fail_timeout(); + void close(); + + 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(const session_settings& s) + : m_settings(s) {} + + void queue_request( + demuxer& d + , tracker_request r + , std::string const& auth + , boost::weak_ptr c + = boost::weak_ptr()); + void abort_all_requests(); + + void remove_request(tracker_connection const*); + bool empty() const; + + private: + + typedef boost::recursive_mutex mutex_t; + mutable mutex_t m_mutex; + + typedef std::list > + tracker_connections_t; + tracker_connections_t m_connections; + session_settings const& m_settings; + }; +} + +#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED + diff --git a/library/include/libtorrent/udp_tracker_connection.hpp b/library/include/libtorrent/udp_tracker_connection.hpp new file mode 100755 index 000000000..43671ed86 --- /dev/null +++ b/library/include/libtorrent/udp_tracker_connection.hpp @@ -0,0 +1,122 @@ +/* + +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 +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/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 +{ + class TORRENT_EXPORT udp_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + udp_tracker_connection( + demuxer& d + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , boost::weak_ptr c + , session_settings const& stn); + + private: + + enum action_t + { + action_connect, + action_announce, + action_scrape, + action_error + }; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void name_lookup(asio::error const& error, tcp::resolver::iterator i); + void timeout(asio::error const& error); + + void send_udp_connect(); + void connect_response(asio::error const& error, std::size_t bytes_transferred); + + void send_udp_announce(); + void announce_response(asio::error const& error, std::size_t bytes_transferred); + + void send_udp_scrape(); + void scrape_response(asio::error const& error, std::size_t bytes_transferred); + + virtual void on_timeout(); + + tracker_manager& m_man; + + tcp::resolver m_name_lookup; + int m_port; + boost::shared_ptr m_socket; + udp::endpoint m_target; + udp::endpoint m_sender; + + int m_transaction_id; + boost::int64_t m_connection_id; + session_settings const& m_settings; + int m_attempts; + std::vector m_buffer; + }; + +} + +#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/library/include/libtorrent/utf8.hpp b/library/include/libtorrent/utf8.hpp new file mode 100644 index 000000000..7a31af804 --- /dev/null +++ b/library/include/libtorrent/utf8.hpp @@ -0,0 +1,160 @@ +/* + Copyright (C) 2004-2005 Cory Nelson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors 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. +*/ + +// namespaces added by Arvid Norberg + +#ifndef __UTF8_H__ +#define __UTF8_H__ + +#include +#include +#include + +namespace libtorrent { +namespace detail { + +template +wchar_t decode_utf8_mb(InputIterator &iter, InputIterator last) +{ + if (iter == last) throw std::runtime_error("incomplete UTF-8 sequence"); + if (((*iter) & 0xc0) != 0x80) throw std::runtime_error("invalid UTF-8 sequence"); + + return (wchar_t)((*iter++) & 0x3f); +} + +template +wchar_t decode_utf8(InputIterator &iter, InputIterator last) +{ + wchar_t ret; + + if (((*iter) & 0x80) == 0) // one byte + { + ret = *iter++; + } + else if (((*iter) & 0xe0) == 0xc0) // two bytes + { + wchar_t byte1 = (*iter++) & 0x1f; + wchar_t byte2 = decode_utf8_mb(iter, last); + ret = (byte1 << 6) | byte2; + } + else if (((*iter) & 0xf0) == 0xe0) // three bytes + { + wchar_t byte1 = (*iter++) & 0x0f; + wchar_t byte2 = decode_utf8_mb(iter, last); + wchar_t byte3 = decode_utf8_mb(iter, last); + ret = (byte1 << 12) | (byte2 << 6) | byte3; + } + // TODO: support surrogate pairs + else throw std::runtime_error("UTF-8 not convertable to UTF-16"); + + return ret; +} + +template +OutputIterator utf8_wchar(InputIterator first, InputIterator last, OutputIterator dest) +{ + for(; first!=last; ++dest) + *dest = decode_utf8(first, last); + return dest; +} + +template +void encode_wchar(InputIterator iter, OutputIterator &dest) +{ + if(*iter <= 0x007F) + { + *dest=(char)*iter; + ++dest; + } + else if(*iter <= 0x07FF) + { + *dest = (char)( + 0xC0 | + ((*iter & 0x07C0) >> 6) + ); + ++dest; + + *dest = (char)( + 0x80 | + (*iter & 0x003F) + ); + ++dest; + } + else if(*iter <= 0xFFFF) + { + *dest = (char)( + 0xE0 | + ((*iter & 0xF000) >> 12) + ); + ++dest; + + *dest = (char)( + 0x80 | + ((*iter & 0x0FC0) >> 6) + ); + ++dest; + + *dest = (char)( + 0x80 | + (*iter & 0x003F) + ); + ++dest; + } +} + +template +OutputIterator wchar_utf8(InputIterator first, InputIterator last, OutputIterator dest) +{ + for(; first!=last; ++first) + encode_wchar(first, dest); + return dest; +} + +} + +inline void utf8_wchar(const std::string &utf8, std::wstring &wide) +{ + wide.clear(); + detail::utf8_wchar(utf8.begin(), utf8.end(), std::insert_iterator(wide, wide.end())); +} + +inline std::wstring utf8_wchar(const std::string &str) +{ + std::wstring ret; + utf8_wchar(str, ret); + return ret; +} + +inline void wchar_utf8(const std::wstring &wide, std::string &utf8) +{ + utf8.clear(); + detail::wchar_utf8(wide.begin(), wide.end(), std::insert_iterator(utf8, utf8.end())); +} + +inline std::string wchar_utf8(const std::wstring &str) +{ + std::string ret; + wchar_utf8(str, ret); + return ret; +} + +} + +#endif diff --git a/library/include/libtorrent/version.hpp b/library/include/libtorrent/version.hpp new file mode 100755 index 000000000..25c7a9f05 --- /dev/null +++ b/library/include/libtorrent/version.hpp @@ -0,0 +1,41 @@ +/* + +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 11 + +#define LIBTORRENT_VERSION "0.11.0.0" + +#endif diff --git a/library/include/libtorrent/web_peer_connection.hpp b/library/include/libtorrent/web_peer_connection.hpp new file mode 100755 index 000000000..77f840409 --- /dev/null +++ b/library/include/libtorrent/web_peer_connection.hpp @@ -0,0 +1,169 @@ +/* + +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 + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#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/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +// parse_url +#include "libtorrent/tracker_manager.hpp" +// http_parser +#include "libtorrent/http_tracker_connection.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXPORT web_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 + web_peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , std::string const& url); + + ~web_peer_connection(); + + // called from the main loop when this connection has any + // work to do. + void on_sent(asio::error const& error + , std::size_t bytes_transferred); + void on_receive(asio::error const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_peer_info(peer_info& p) const; + + // 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_have(int index) {} + void write_piece(peer_request const& r) {} + void write_keepalive() {} + void on_connected(); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + 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 bittorrent request + std::deque m_requests; + // this has one entry per http-request + // (might be more than the bt requests) + std::deque m_file_requests; + + std::string m_server_string; + http_parser m_parser; + std::string m_host; + int m_port; + std::string m_path; + std::string m_url; + + // 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; + + // this is used for intermediate storage of pieces + // that is received in more than on HTTP responses + std::vector m_piece; + // the mapping of the data in the m_piece buffer + peer_request m_intermediate_piece; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/library/installit b/library/installit new file mode 100755 index 000000000..b3fb5468d --- /dev/null +++ b/library/installit @@ -0,0 +1 @@ +sudo python setup.py install diff --git a/library/ip_filter.cpp b/library/ip_filter.cpp new file mode 100644 index 000000000..0afb5a346 --- /dev/null +++ b/library/ip_filter.cpp @@ -0,0 +1,80 @@ +/* + +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/ip_filter.hpp" +#include +//#include + + +namespace libtorrent +{ + void ip_filter::add_rule(address first, address last, int flags) + { + if (first.is_v4()) + { + assert(last.is_v4()); + m_filter4.add_rule(first.to_v4(), last.to_v4(), flags); + } + else if (first.is_v6()) + { + assert(last.is_v6()); + m_filter6.add_rule(first.to_v6(), last.to_v6(), flags); + } + else + assert(false); + } + + int ip_filter::access(address const& addr) const + { + if (addr.is_v4()) + return m_filter4.access(addr.to_v4()); + assert(addr.is_v6()); + return m_filter6.access(addr.to_v6()); + } + + ip_filter::filter_tuple_t ip_filter::export_filter() const + { + return boost::make_tuple(m_filter4.export_filter() + , m_filter6.export_filter()); + } + +/* + 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/library/kademlia/closest_nodes.cpp b/library/kademlia/closest_nodes.cpp new file mode 100644 index 000000000..8d0ccea87 --- /dev/null +++ b/library/kademlia/closest_nodes.cpp @@ -0,0 +1,145 @@ +/* + +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 +#include +#include + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +typedef boost::shared_ptr observer_ptr; + +class closest_nodes_observer : public observer +{ +public: + closest_nodes_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target + ) + : m_algorithm(algorithm) + , m_target(target) + , m_self(self) + {} + + void send(msg& p) + { + p.info_hash = m_target; + } + + void timeout(); + void reply(msg const&); + +private: + boost::intrusive_ptr m_algorithm; + node_id const m_target; + node_id const m_self; +}; + +void closest_nodes_observer::reply(msg const& in) +{ + if (!in.nodes.empty()) + { + for (msg::nodes_t::const_iterator i = in.nodes.begin() + , end(in.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); +} + +void closest_nodes_observer::timeout() +{ + m_algorithm->failed(m_self); +} + + +closest_nodes::closest_nodes( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , table.begin() + , table.end() + ) + , m_done_callback(callback) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +void closest_nodes::invoke(node_id const& id, udp::endpoint addr) +{ + observer_ptr p(new closest_nodes_observer(this, id, m_target)); + m_rpc.invoke(messages::find_node, addr, p); +} + +void closest_nodes::done() +{ + std::vector results; + int result_size = m_table.bucket_size(); + if (result_size > (int)m_results.size()) result_size = (int)m_results.size(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.begin() + result_size); i != end; ++i) + { + results.push_back(node_entry(i->id, i->addr)); + } + m_done_callback(results); +} + +void closest_nodes::initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) +{ + new closest_nodes(target, branch_factor, max_results, table, rpc, callback); +} + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/dht_tracker.cpp b/library/kademlia/dht_tracker.cpp new file mode 100644 index 000000000..ca003e2b1 --- /dev/null +++ b/library/kademlia/dht_tracker.cpp @@ -0,0 +1,905 @@ +/* + +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 +#include +#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/kademlia/dht_tracker.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" + +using boost::posix_time::ptime; +using boost::posix_time::time_duration; +using boost::posix_time::second_clock; +using boost::posix_time::microsec_clock; +using boost::posix_time::seconds; +using boost::posix_time::minutes; +using boost::posix_time::hours; +using boost::posix_time::milliseconds; +using boost::ref; +using boost::lexical_cast; +using libtorrent::dht::node_impl; +using libtorrent::dht::node_id; +using libtorrent::dht::packet_t; +using libtorrent::dht::msg; +using libtorrent::dht::packet_iterator; +namespace messages = libtorrent::dht::messages; +using namespace libtorrent::detail; + +using asio::ip::udp; +typedef asio::ip::address_v4 address; + +namespace +{ + const int tick_period = 1; // minutes + + struct count_peers + { + int& count; + count_peers(int& c): count(c) {} + void operator()(std::pair const& t) + { + count += std::distance(t.second.peers.begin() + , t.second.peers.end()); + } + }; + + boost::optional read_id(libtorrent::entry const& d) + { + using namespace libtorrent; + using libtorrent::dht::node_id; + + if (d.type() != entry::dictionary_t) return boost::optional(); + entry const* nid = d.find_key("node-id"); + if (!nid + || nid->type() != entry::string_t + || nid->string().length() != 40) + return boost::optional(); + return boost::optional( + boost::lexical_cast(nid->string())); + } + + template + void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) + { + using namespace libtorrent; + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + 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)); + else if (p.size() == 18) + epl.push_back(read_v6_endpoint(in)); + } + } + +} + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DEFINE_LOG(dht_tracker) +#endif + + // class that puts the networking and the kademlia node in a single + // unit and connecting them together. + dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings + , asio::ip::address listen_interface, entry const& bootstrap) + : m_demuxer(d) + , m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port)) + , m_dht(bind(&dht_tracker::send_packet, this, _1), settings + , read_id(bootstrap)) + , m_buffer(0) + , m_last_refresh(second_clock::universal_time() - hours(1)) + , m_timer(m_demuxer) + , m_connection_timer(m_demuxer) + , m_refresh_timer(m_demuxer) + , m_settings(settings) + , m_refresh_bucket(160) + , m_host_resolver(d) + { + using boost::bind; + + m_in_buf[0].resize(1000); + m_in_buf[1].resize(1000); +#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); + m_announces = 0; + m_failed_announces = 0; + m_total_message_input = 0; + m_ut_message_input = 0; + m_lt_message_input = 0; + m_mp_message_input = 0; + m_gr_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); + +#endif + std::vector initial_nodes; + + if (bootstrap.type() == entry::dictionary_t) + { + try + { + if (entry const* nodes = bootstrap.find_key("nodes")) + read_endpoint_list(nodes, initial_nodes); + } catch (std::exception&) {} + } + + m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this)); + + m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] + , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] + , bind(&dht_tracker::on_receive, this, _1, _2)); + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(bind(&dht_tracker::tick, this, _1)); + + m_connection_timer.expires_from_now(seconds(10)); + m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1)); + + m_refresh_timer.expires_from_now(minutes(15)); + m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1)); + } + + void dht_tracker::dht_status(session_status& s) + { + boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size(); + s.m_dht_torrents = m_dht.data_size(); + } + + void dht_tracker::connection_timeout(asio::error const& e) + try + { + if (e) return; + time_duration d = m_dht.connection_timeout(); + m_connection_timer.expires_from_now(d); + m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1)); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::refresh_timeout(asio::error const& e) + try + { + if (e) return; + time_duration d = m_dht.refresh_timeout(); + m_refresh_timer.expires_from_now(d); + m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1)); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port) + { + m_socket.close(); + m_socket.open(asio::ip::udp::v4()); + m_socket.bind(udp::endpoint(listen_interface, listen_port)); + } + + void dht_tracker::tick(asio::error const& e) + try + { + if (e) return; + m_timer.expires_from_now(minutes(tick_period)); + m_timer.async_wait(bind(&dht_tracker::tick, this, _1)); + + m_dht.new_write_key(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + static bool first = true; + if (first) + { + boost::filesystem::create_directory("libtorrent_logs"); + } + + std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc); + m_dht.print_state(st); + + // count torrents + int torrents = std::distance(m_dht.begin_data(), m_dht.end_data()); + + // count peers + int peers = 0; + std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers)); + + std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app); + if (first) + { + first = false; + using boost::posix_time::to_simple_string; + pc << "\n\n ***** starting log at " << to_simple_string( + second_clock::universal_time()) << " *****\n\n" + << "minute:active nodes:passive nodes" + ":ping replies sent:ping queries recvd:ping" + ":ping replies sent:ping queries recvd:ping" + ":find_node replies bytes sent:find_node queries bytes recv" + ":find_node replies bytes sent:find_node queries bytes recv" + ":get_peers replies sent:get_peers queries recvd:get_peers" + ":get_peers replies bytes sent:get_peers queries bytes recv" + ":announce_peer replies sent:announce_peer queries recvd:announce_peer" + ":announce_peer replies bytes sent:announce_peer queries bytes recv" + ":error replies sent:error queries recvd:error" + ":error replies bytes sent:error queries bytes recv" + ":num torrents:num peers:announces per min" + ":failed announces per min:total msgs per min" + ":ut msgs per min:lt msgs per min:mp msgs per min" + ":gr 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" << m_announces / float(tick_period) + << "\t" << m_failed_announces / float(tick_period) + << "\t" << (m_total_message_input / float(tick_period)) + << "\t" << (m_ut_message_input / float(tick_period)) + << "\t" << (m_lt_message_input / float(tick_period)) + << "\t" << (m_mp_message_input / float(tick_period)) + << "\t" << (m_gr_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); + m_announces = 0; + m_failed_announces = 0; + m_total_message_input = 0; + m_ut_message_input = 0; + m_lt_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; +#endif + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::announce(sha1_hash const& ih, int listen_port + , boost::function const& + , sha1_hash const&)> f) + { + m_dht.announce(ih, listen_port, f); + } + + // translate bittorrent kademlia message into the generice kademlia message + // used by the library + void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred) + try + { + if (error == asio::error::operation_aborted) return; + + int current_buffer = m_buffer; + m_buffer = (m_buffer + 1) & 1; + m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] + , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] + , bind(&dht_tracker::on_receive, this, _1, _2)); + + if (error) return; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_total_message_input; + m_total_in_bytes += bytes_transferred; +#endif + + try + { + using libtorrent::entry; + using libtorrent::bdecode; + + assert(bytes_transferred > 0); + + entry e = bdecode(m_in_buf[current_buffer].begin() + , m_in_buf[current_buffer].end()); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << microsec_clock::universal_time() + << " RECEIVED [" << m_remote_endpoint[current_buffer] + << "]:"; +#endif + + libtorrent::dht::msg m; + m.message_id = 0; + m.addr = m_remote_endpoint[current_buffer]; + m.transaction_id = e["t"].string(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + try + { + entry const* ver = e.find_key("v"); + if (!ver) throw std::exception(); + + std::string const& client = ver->string(); + if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT")) + { + ++m_ut_message_input; + TORRENT_LOG(dht_tracker) << " client: uTorrent"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT")) + { + ++m_lt_message_input; + TORRENT_LOG(dht_tracker) << " client: libtorrent"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP")) + { + ++m_mp_message_input; + TORRENT_LOG(dht_tracker) << " client: MooPolice"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR")) + { + ++m_gr_message_input; + TORRENT_LOG(dht_tracker) << " client: GetRight"; + } + else + { + TORRENT_LOG(dht_tracker) << " client: generic"; + } + } + catch (std::exception&) + { + TORRENT_LOG(dht_tracker) << " client: generic"; + }; +#endif + + std::string const& msg_type = e["y"].string(); + + if (msg_type == "r") + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " reply: transaction: " + << m.transaction_id; +#endif + + m.reply = true; + entry const& r = e["r"]; + std::string const& id = r["id"].string(); + if (id.size() != 20) throw std::runtime_error("invalid size of id"); + std::copy(id.begin(), id.end(), m.id.begin()); + + if (entry const* n = r.find_key("values")) + { + m.peers.clear(); + read_endpoint_list(n, m.peers); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); +#endif + } + + m.nodes.clear(); + if (entry const* n = r.find_key("nodes")) + { + std::string const& nodes = n->string(); + std::string::const_iterator i = nodes.begin(); + std::string::const_iterator end = nodes.end(); + + while (std::distance(i, end) >= 26) + { + node_id id; + std::copy(i, i + 20, id.begin()); + i += 20; + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v4_endpoint(i))); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); +#endif + } + + if (entry const* n = r.find_key("nodes2")) + { + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + std::string const& p = i->string(); + if (p.size() < 6 + 20) continue; + std::string::const_iterator in = p.begin(); + + node_id id; + std::copy(in, in + 20, id.begin()); + in += 20; + if (p.size() == 6 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v4_endpoint(in))); + else if (p.size() == 18 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v6_endpoint(in))); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); +#endif + } + + entry const* token = r.find_key("token"); + if (token) m.write_token = *token; + } + else if (msg_type == "q") + { + m.reply = false; + entry const& a = e["a"]; + std::string const& id = a["id"].string(); + if (id.size() != 20) throw std::runtime_error("invalid size of id"); + std::copy(id.begin(), id.end(), m.id.begin()); + + std::string request_kind(e["q"].string()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " query: " << request_kind; +#endif + + if (request_kind == "ping") + { + m.message_id = libtorrent::dht::messages::ping; + } + else if (request_kind == "find_node") + { + std::string const& target = a["target"].string(); + if (target.size() != 20) throw std::runtime_error("invalid size of target id"); + std::copy(target.begin(), target.end(), m.info_hash.begin()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " target: " + << boost::lexical_cast(m.info_hash); +#endif + + m.message_id = libtorrent::dht::messages::find_node; + } + else if (request_kind == "get_peers") + { + std::string const& info_hash = a["info_hash"].string(); + if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); + std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); + m.message_id = libtorrent::dht::messages::get_peers; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); +#endif + } + else if (request_kind == "announce_peer") + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_announces; +#endif + std::string const& info_hash = a["info_hash"].string(); + if (info_hash.size() != 20) + throw std::runtime_error("invalid size of info-hash"); + std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); + m.port = a["port"].integer(); + m.write_token = a["token"]; + m.message_id = libtorrent::dht::messages::announce_peer; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); + TORRENT_LOG(dht_tracker) << " port: " << m.port; + + if (!m_dht.verify_token(m)) + ++m_failed_announces; +#endif + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : " + << request_kind; +#endif + throw std::runtime_error("unsupported request: " + request_kind); + } + } + else if (msg_type == "e") + { + entry::list_type const& list = e["e"].list(); + m.message_id = messages::error; + m.error_msg = list.back().string(); + m.error_code = list.front().integer(); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " " + << m.error_msg; +#endif + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : " + << msg_type; +#endif + throw std::runtime_error("unsupported message type: " + msg_type); + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (!m.reply) + { + ++m_queries_received[m.message_id]; + m_queries_bytes_received[m.message_id] += int(bytes_transferred); + } + TORRENT_LOG(dht_tracker) << e; +#endif + + m_dht.incoming(m); + } + catch (std::exception& e) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << "invalid incoming packet: " + << e.what(); +#endif + } + } + catch (std::exception& e) + { + assert(false); + }; + + entry dht_tracker::state() const + { + entry ret(entry::dictionary_t); + { + entry nodes(entry::list_t); + for (node_impl::iterator i(m_dht.begin()) + , end(m_dht.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->addr, out); + nodes.list().push_back(entry(node)); + } + bucket_t cache; + m_dht.replacement_cache(cache); + for (bucket_t::iterator i(cache.begin()) + , end(cache.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->addr, out); + nodes.list().push_back(entry(node)); + } + if (!nodes.list().empty()) + ret["nodes"] = nodes; + } + + ret["node-id"] = boost::lexical_cast(m_dht.nid()); + return ret; + } + + void dht_tracker::add_node(udp::endpoint node) + { + m_dht.add_node(node); + } + + void dht_tracker::add_node(std::pair const& node) + { + udp::resolver::query q(node.first, lexical_cast(node.second)); + m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup + , this, _1, _2)); + } + + void dht_tracker::on_name_lookup(asio::error const& e + , udp::resolver::iterator host) try + { + if (e || host == udp::resolver::iterator()) return; + add_node(host->endpoint()); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::add_router_node(std::pair const& node) + { + udp::resolver::query q(node.first, lexical_cast(node.second)); + m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup + , this, _1, _2)); + } + + void dht_tracker::on_router_name_lookup(asio::error const& e + , udp::resolver::iterator host) try + { + if (e || host == udp::resolver::iterator()) return; + m_dht.add_router_node(host->endpoint()); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::on_bootstrap() + {} + + void dht_tracker::send_packet(msg const& m) + { + using libtorrent::bencode; + using libtorrent::entry; + entry e(entry::dictionary_t); + e["t"] = m.transaction_id; + std::string version_str("LT "); + std::string::iterator i = version_str.begin() + 2; + detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i); + detail::write_uint8(LIBTORRENT_VERSION_MINOR, i); + e["v"] = version_str; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << microsec_clock::universal_time() + << " SENDING [" << m.addr << "]:"; + TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id; +// e.print(std::cerr); +#endif + + if (m.message_id == messages::error) + { + assert(m.reply); + e["y"] = "e"; + entry error_list(entry::list_t); + error_list.list().push_back(entry(m.error_code)); + error_list.list().push_back(entry(m.error_msg)); + e["e"] = error_list; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " " + << m.error_msg; +#endif + } + else if (m.reply) + { + e["y"] = "r"; + e["r"] = entry(entry::dictionary_t); + entry& r = e["r"]; + r["id"] = std::string(m.id.begin(), m.id.end()); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " reply: " + << messages::ids[m.message_id]; +#endif + + if (m.write_token.type() != entry::undefined_t) + r["token"] = m.write_token; + + switch (m.message_id) + { + case messages::ping: + break; + case messages::find_node: + { + bool ipv6_nodes = false; + r["nodes"] = entry(entry::string_t); + entry& n = r["nodes"]; + std::back_insert_iterator out(n.string()); + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + if (!i->addr.address().is_v4()) + { + ipv6_nodes = true; + continue; + } + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(i->addr, out); + } + + if (ipv6_nodes) + { + r["nodes2"] = entry(entry::list_t); + entry& p = r["nodes2"]; + std::string endpoint; + endpoint.resize(6); + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + std::string::iterator out = endpoint.begin(); + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(i->addr, out); + p.list().push_back(entry(endpoint)); + } + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); +#endif + break; + } + case messages::get_peers: + { + if (m.peers.empty()) + { + r["nodes"] = entry(entry::string_t); + entry& n = r["nodes"]; + std::back_insert_iterator out(n.string()); + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + if (!i->addr.address().is_v4()) continue; + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(i->addr, out); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); +#endif + } + else + { + r["values"] = entry(entry::list_t); + entry& p = r["values"]; + std::string endpoint; + endpoint.resize(6); + for (msg::peers_t::const_iterator i = m.peers.begin() + , end(m.peers.end()); i != end; ++i) + { + std::string::iterator out = endpoint.begin(); + write_endpoint(*i, out); + p.list().push_back(entry(endpoint)); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); +#endif + } + break; + } + + case messages::announce_peer: + break; + break; + } + } + else + { + e["y"] = "q"; + e["a"] = entry(entry::dictionary_t); + entry& a = e["a"]; + a["id"] = std::string(m.id.begin(), m.id.end()); + + if (m.write_token.type() != entry::undefined_t) + a["token"] = m.write_token; + assert(m.message_id <= messages::error); + e["q"] = messages::ids[m.message_id]; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " query: " + << messages::ids[m.message_id]; +#endif + + switch (m.message_id) + { + case messages::find_node: + { + a["target"] = std::string(m.info_hash.begin(), m.info_hash.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " target: " + << boost::lexical_cast(m.info_hash); +#endif + break; + } + case messages::get_peers: + { + a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); +#endif + break; + } + case messages::announce_peer: + a["port"] = m_settings.service_port; + a["info_hash"] = boost::lexical_cast(m.info_hash); + a["token"] = m.write_token; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " port: " + << m_settings.service_port + << " info_hash: " << boost::lexical_cast(m.info_hash); +#endif + break; + default: break; + } + + } + + m_send_buf.clear(); + bencode(std::back_inserter(m_send_buf), e); + m_socket.send_to(asio::buffer(&m_send_buf[0] + , (int)m_send_buf.size()), m.addr); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_total_out_bytes += m_send_buf.size(); + + if (m.reply) + { + ++m_replies_sent[m.message_id]; + m_replies_bytes_sent[m.message_id] += int(m_send_buf.size()); + } + else + { + m_queries_out_bytes += m_send_buf.size(); + } + TORRENT_LOG(dht_tracker) << e; +#endif + + if (!m.piggy_backed_ping) return; + + msg pm; + pm.reply = false; + pm.piggy_backed_ping = false; + pm.message_id = messages::ping; + pm.transaction_id = m.ping_transaction_id; + pm.id = m.id; + pm.addr = m.addr; + + send_packet(pm); + } + +}} + diff --git a/library/kademlia/find_data.cpp b/library/kademlia/find_data.cpp new file mode 100644 index 000000000..e1e09925b --- /dev/null +++ b/library/kademlia/find_data.cpp @@ -0,0 +1,156 @@ +/* + +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 +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +typedef boost::shared_ptr observer_ptr; + +class find_data_observer : public observer +{ +public: + find_data_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target) + : m_algorithm(algorithm) + , m_target(target) + , m_self(self) + {} + + void send(msg& m) + { + m.reply = false; + m.message_id = messages::get_peers; + m.info_hash = m_target; + } + + void timeout(); + void reply(msg const&); + +private: + boost::intrusive_ptr m_algorithm; + node_id const m_target; + node_id const m_self; +}; + +void find_data_observer::reply(msg const& m) +{ + if (!m.peers.empty()) + { + m_algorithm->got_data(&m); + } + else + { + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); +} + +void find_data_observer::timeout() +{ + m_algorithm->failed(m_self); +} + + +find_data::find_data( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , table.begin() + , table.end() + ) + , m_done_callback(callback) + , m_done(false) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr) +{ + if (m_done) + { + m_invoke_count = -1; + return; + } + + observer_ptr p(new find_data_observer(this, id, m_target)); + m_rpc.invoke(messages::get_peers, addr, p); +} + +void find_data::got_data(msg const* m) +{ + m_done = true; + m_done_callback(m); +} + +void find_data::done() +{ + if (m_invoke_count != 0) return; + if (!m_done) m_done_callback(0); +} + +void find_data::initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) +{ + std::cerr << "find_data::initiate, key: " << target << "\n"; + new find_data(target, branch_factor, max_results, table, rpc, callback); +} + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/node.cpp b/library/kademlia/node.cpp new file mode 100644 index 000000000..8a9d17818 --- /dev/null +++ b/library/kademlia/node.cpp @@ -0,0 +1,539 @@ +/* + +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 +#include +#include + +#include "libtorrent/io.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/random_sample.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/rpc_manager.hpp" +#include "libtorrent/kademlia/packet_iterator.hpp" +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node.hpp" + +#include "libtorrent/kademlia/refresh.hpp" +#include "libtorrent/kademlia/closest_nodes.hpp" +#include "libtorrent/kademlia/find_data.hpp" + +using boost::bind; +using boost::posix_time::second_clock; +using boost::posix_time::seconds; +using boost::posix_time::minutes; +using boost::posix_time::ptime; +using boost::posix_time::time_duration; + +namespace libtorrent { namespace dht +{ + +#ifdef _MSC_VER +namespace +{ + char rand() { return (char)std::rand(); } +} +#endif + +typedef boost::shared_ptr observer_ptr; + +// TODO: configurable? +enum { announce_interval = 30 }; + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(node) +#endif + +node_id generate_id() +{ + char random[20]; + std::srand(std::time(0)); +#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(); +} + +// 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)) < second_clock::universal_time()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "peer timed out at: " << i->addr.address(); +#endif + peers.erase(i++); + } + else + ++i; + } +} + +void nop() {} + +node_impl::node_impl(boost::function const& f + , dht_settings const& settings, boost::optional node_id) + : m_settings(settings) + , m_id(node_id ? *node_id : generate_id()) + , m_table(m_id, 8, settings) + , m_rpc(bind(&node_impl::incoming_request, this, _1) + , m_id, m_table, f) + , m_last_tracker_tick(boost::posix_time::second_clock::universal_time()) +{ + m_secret[0] = std::rand(); + m_secret[1] = std::rand(); +} + +bool node_impl::verify_token(msg const& m) +{ + if (m.write_token.type() != entry::string_t) + return false; + std::string const& token = m.write_token.string(); + if (token.length() != 4) return false; + + hasher h1; + std::string address = m.addr.address().to_string(); + h1.update(&address[0], address.length()); + h1.update((char*)&m_secret[0], sizeof(m_secret[0])); + h1.update((char*)&m.info_hash[0], 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])); + h = h2.final(); + if (std::equal(token.begin(), token.end(), (signed char*)&h[0])) + return true; + return false; +} + +entry node_impl::generate_token(msg const& m) +{ + std::string token; + token.resize(4); + hasher h; + std::string address = m.addr.address().to_string(); + h.update(&address[0], address.length()); + h.update((char*)&m_secret[0], sizeof(m_secret[0])); + h.update((char*)&m.info_hash[0], sha1_hash::size); + + sha1_hash hash = h.final(); + std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]); + return entry(token); +} + +void node_impl::refresh(node_id const& id + , boost::function0 f) +{ + // use the 'bucket size' closest nodes + // to start the refresh with + std::vector start; + start.reserve(m_table.bucket_size()); + m_table.find_node(id, start, false); + refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, f); +} + +void node_impl::bootstrap(std::vector const& nodes + , boost::function0 f) +{ + std::vector start; + start.reserve(nodes.size()); + std::copy(nodes.begin(), nodes.end(), std::back_inserter(start)); + refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, f); +} + +void node_impl::refresh() +{ + std::vector start; + start.reserve(m_table.size().get<0>()); + std::copy(m_table.begin(), m_table.end(), std::back_inserter(start)); + + refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); +} + +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::refresh_bucket(int bucket) +{ + assert(bucket >= 0 && bucket < 160); + + // generate a random node_id within the given bucket + node_id target = generate_id(); + int num_bits = 160 - bucket; + node_id mask(0); + for (int i = 0; i < num_bits; ++i) + { + int byte = i / 8; + mask[byte] |= 0x80 >> (i % 8); + } + + 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)); + + assert(distance_exp(m_id, target) == bucket); + + std::vector start; + start.reserve(m_table.bucket_size()); + m_table.find_node(target, start, false, m_table.bucket_size()); + + refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); + m_table.touch_bucket(bucket); +} + + +void node_impl::incoming(msg const& m) +{ + if (m_rpc.incoming(m)) + { + refresh(); + } +} + +namespace +{ + + class announce_observer : public observer + { + public: + announce_observer(sha1_hash const& info_hash, int listen_port + , entry const& write_token) + : m_info_hash(info_hash) + , m_listen_port(listen_port) + , m_token(write_token) + {} + + void send(msg& m) + { + m.port = m_listen_port; + m.info_hash = m_info_hash; + m.write_token = m_token; + } + + void timeout() {} + void reply(msg const&) {} + + private: + sha1_hash m_info_hash; + int m_listen_port; + entry m_token; + }; + + class get_peers_observer : public observer + { + public: + get_peers_observer(sha1_hash const& info_hash, int listen_port + , rpc_manager& rpc + , boost::function const&, sha1_hash const&)> f) + : m_info_hash(info_hash) + , m_listen_port(listen_port) + , m_rpc(rpc) + , m_fun(f) + {} + + void send(msg& m) + { + m.port = m_listen_port; + m.info_hash = m_info_hash; + } + + void timeout() {} + void reply(msg const& r) + { + m_rpc.invoke(messages::announce_peer, r.addr + , boost::shared_ptr( + new announce_observer(m_info_hash, m_listen_port, r.write_token))); + m_fun(r.peers, m_info_hash); + } + + private: + sha1_hash m_info_hash; + int m_listen_port; + rpc_manager& m_rpc; + boost::function const&, sha1_hash const&)> m_fun; + }; + + + void announce_fun(std::vector const& v, rpc_manager& rpc + , int listen_port, sha1_hash const& ih + , boost::function const&, sha1_hash const&)> f) + { + bool nodes = false; + // only store on the first k nodes + for (std::vector::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { + rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr( + new get_peers_observer(ih, listen_port, rpc, f))); + nodes = true; + } + } + +} + +namespace +{ + struct dummy_observer : observer + { + virtual void reply(msg const&) {} + virtual void timeout() {} + virtual void send(msg&) {} + }; +} + +void node_impl::add_router_node(udp::endpoint router) +{ + 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 + observer_ptr p(new dummy_observer()); + m_rpc.invoke(messages::ping, node, p); +} + +void node_impl::announce(sha1_hash const& info_hash, int listen_port + , boost::function const&, sha1_hash const&)> f) +{ + // search for nodes with ids close to id, and then invoke the + // get_peers and then announce_peer rpc on them. + closest_nodes::initiate(info_hash, m_settings.search_branching + , m_table.bucket_size(), m_table, m_rpc + , boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port + , info_hash, f)); +} + +time_duration node_impl::refresh_timeout() +{ + int refresh = -1; + ptime now = second_clock::universal_time(); + ptime next = now + minutes(15); + for (int i = 0; i < 160; ++i) + { + ptime r = m_table.next_refresh(i); + if (r <= now) + { + if (refresh == -1) refresh = i; + } + else if (r < next) + { + next = r; + } + } + if (refresh != -1) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "refreshing bucket: " << refresh; +#endif + refresh_bucket(refresh); + } + if (next < now + seconds(5)) return seconds(5); + return next - now; +} + +time_duration node_impl::connection_timeout() +{ + time_duration d = m_rpc.tick(); + + ptime now(second_clock::universal_time()); + 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 (data_iterator i = begin_data(), end(end_data()); 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::on_announce(msg const& m, msg& reply) +{ + if (!verify_token(m)) + { + reply.message_id = messages::error; + reply.error_code = 203; + reply.error_msg = "Incorrect write token in announce_peer message"; + 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(m.id, m.addr); + + torrent_entry& v = m_map[m.info_hash]; + peer_entry e; + e.addr = tcp::endpoint(m.addr.address(), m.addr.port()); + e.added = second_clock::universal_time(); + std::set::iterator i = v.peers.find(e); + if (i != v.peers.end()) v.peers.erase(i++); + v.peers.insert(i, e); +} + +namespace +{ + tcp::endpoint get_endpoint(peer_entry const& p) + { + return p.addr; + } +} + +bool node_impl::on_find(msg const& m, std::vector& peers) const +{ + table_t::const_iterator i = m_map.find(m.info_hash); + if (i == m_map.end()) return false; + + torrent_entry const& v = i->second; + + int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); + peers.clear(); + peers.reserve(num); + random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint) + , boost::make_transform_iterator(v.peers.end(), &get_endpoint) + , std::back_inserter(peers), num); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << *i; + } +#endif + return true; +} + +void node_impl::incoming_request(msg const& m) +{ + msg reply; + switch (m.message_id) + { + case messages::ping: + break; + case messages::get_peers: + { + reply.info_hash = m.info_hash; + reply.write_token = generate_token(m); + + if (!on_find(m, reply.peers)) + { + // we don't have any peers for this info_hash, + // return nodes instead + m_table.find_node(m.info_hash, reply.nodes, false); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = reply.nodes.begin() + , end(reply.nodes.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << i->id << " " << i->addr; + } +#endif + } + } + break; + case messages::find_node: + { + reply.info_hash = m.info_hash; + + m_table.find_node(m.info_hash, reply.nodes, false); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = reply.nodes.begin() + , end(reply.nodes.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << i->id << " " << i->addr; + } +#endif + } + break; + case messages::announce_peer: + { + on_announce(m, reply); + } + break; + }; + + if (m_table.need_node(m.id)) + m_rpc.reply_with_ping(reply, m); + else + m_rpc.reply(reply, m); +} + + +} } // namespace libtorrent::dht diff --git a/library/kademlia/node_id.cpp b/library/kademlia/node_id.cpp new file mode 100644 index 000000000..d435846d2 --- /dev/null +++ b/library/kademlia/node_id.cpp @@ -0,0 +1,97 @@ +/* + +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 +#include + +#include "libtorrent/kademlia/node_id.hpp" + +using boost::bind; + +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) + { + 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; +} + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/refresh.cpp b/library/kademlia/refresh.cpp new file mode 100644 index 000000000..37300d16e --- /dev/null +++ b/library/kademlia/refresh.cpp @@ -0,0 +1,190 @@ +/* + +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 +#include +#include +#include + +#include + +#include + +using boost::bind; + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(refresh) +#endif + +typedef boost::shared_ptr observer_ptr; + +class refresh_observer : public observer +{ +public: + refresh_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target + ) + : m_target(target) + , m_self(self) + , m_algorithm(algorithm) + {} + + void send(msg& m) + { + m.info_hash = m_target; + } + + void timeout(); + void reply(msg const& m); + +private: + node_id const m_target; + node_id const m_self; + boost::intrusive_ptr m_algorithm; +}; + +void refresh_observer::reply(msg const& in) +{ + if (!in.nodes.empty()) + { + for (msg::nodes_t::const_iterator i = in.nodes.begin() + , end(in.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); +} + +void refresh_observer::timeout() +{ + m_algorithm->failed(m_self); +} + +class ping_observer : public observer +{ +public: + ping_observer( + boost::intrusive_ptr const& algorithm + , node_id self + ) + : m_self(self) + , m_algorithm(algorithm) + {} + + void send(msg& p) {} + void timeout(); + void reply(msg const& m); + +private: + node_id const m_self; + boost::intrusive_ptr m_algorithm; +}; + +void ping_observer::reply(msg const& m) +{ + m_algorithm->ping_reply(m_self); +} + +void ping_observer::timeout() +{ + m_algorithm->ping_timeout(m_self); +} + +void refresh::invoke(node_id const& nid, udp::endpoint addr) +{ + observer_ptr p(new refresh_observer( + this + , nid + , m_target + )); + + m_rpc.invoke(messages::find_node, addr, p); +} + +void refresh::done() +{ + m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ? + m_results.begin() + m_max_results : m_results.end(); + + invoke_pings_or_finish(); +} + +void refresh::ping_reply(node_id nid) +{ + m_active_pings--; + invoke_pings_or_finish(); +} + +void refresh::ping_timeout(node_id nid) +{ + m_active_pings--; + invoke_pings_or_finish(); +} + +void refresh::invoke_pings_or_finish() +{ + while (m_active_pings < m_max_active_pings) + { + if (m_leftover_nodes_iterator == m_results.end()) break; + + result const& node = *m_leftover_nodes_iterator; + + // Skip initial nodes + if (node.flags & result::initial) + { + ++m_leftover_nodes_iterator; + continue; + } + + observer_ptr p(new ping_observer(this, node.id)); + + m_rpc.invoke(messages::ping, node.addr, p); + ++m_active_pings; + ++m_leftover_nodes_iterator; + } + + if (m_active_pings == 0) + { + m_done_callback(); + } +} + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/routing_table.cpp b/library/kademlia/routing_table.cpp new file mode 100644 index 000000000..32f7514f2 --- /dev/null +++ b/library/kademlia/routing_table.cpp @@ -0,0 +1,434 @@ +/* + +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 +#include +#include +#include +#include +#include + +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/session_settings.hpp" + +using boost::bind; +using boost::uint8_t; + +using boost::posix_time::second_clock; +using boost::posix_time::minutes; +using boost::posix_time::seconds; +using boost::posix_time::hours; + +namespace pt = boost::posix_time; + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; +typedef asio::ip::address_v4 address; + +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_lowest_active_bucket(160) +{ + // distribute the refresh times for the buckets in an + // attempt do even out the network load + for (int i = 0; i < 160; ++i) + m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5); +} + +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->first.size(); + replacements += i->second.size(); + } + return boost::make_tuple(nodes, replacements); +} + +void routing_table::print_state(std::ostream& os) const +{ + os << "kademlia routing table state\n" + << "bucket_size: " << m_bucket_size << "\n" + << "node_id: " << m_id << "\n\n"; + + os << "number of nodes per bucket:\n" + "live\n"; + for (int k = 0; k < 8; ++k) + { + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << (int(i->first.size()) > (7 - k) ? "|" : " "); + } + os << "\n"; + } + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << "+"; + } + os << "\n"; + for (int k = 0; k < 8; ++k) + { + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << (int(i->second.size()) > k ? "|" : " "); + } + os << "\n"; + } + os << "cached\n-----------\n"; + + os << "nodes:\n"; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + int bucket_index = int(i - m_buckets.begin()); + os << "bucket " << bucket_index << " " + << to_simple_string(m_bucket_activity[bucket_index]) + << " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive") + << "\n"; + for (bucket_t::const_iterator j = i->first.begin() + , end(i->first.end()); j != end; ++j) + { + os << "ip: " << j->addr << " fails: " << j->fail_count + << " id: " << j->id << "\n"; + } + } +} + +void routing_table::touch_bucket(int bucket) +{ + m_bucket_activity[bucket] = second_clock::universal_time(); +} + +boost::posix_time::ptime routing_table::next_refresh(int bucket) +{ + assert(bucket < 160); + assert(bucket >= 0); + // lower than or equal to since a refresh of bucket 0 will + // effectively refresh the lowest active bucket as well + if (bucket <= m_lowest_active_bucket && bucket > 0) + return second_clock::universal_time() + minutes(15); + return m_bucket_activity[bucket] + minutes(15); +} + +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->second.begin(), i->second.end() + , std::back_inserter(nodes)); + } +} + +bool routing_table::need_node(node_id const& id) +{ + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + bucket_t& rb = m_buckets[bucket_index].second; + + // 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 false; + + // if the node already exists, we don't need it + if (std::find_if(b.begin(), b.end(), bind(std::equal_to() + , bind(&node_entry::id, _1), id)) != b.end()) return false; + + if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to() + , bind(&node_entry::id, _1), id)) != rb.end()) return false; + + return true; +} + +void routing_table::node_failed(node_id const& id) +{ + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + bucket_t& rb = m_buckets[bucket_index].second; + + bucket_t::iterator i = std::find_if(b.begin(), b.end() + , bind(std::equal_to() + , bind(&node_entry::id, _1), id)); + + if (i == b.end()) return; + + // if messages to ourself fails, ignore it + if (bucket_index == 0) return; + + if (rb.empty()) + { + ++i->fail_count; + if (i->fail_count >= m_settings.max_fail_count) + { + b.erase(i); + assert(m_lowest_active_bucket <= bucket_index); + while (m_buckets[m_lowest_active_bucket].first.empty() + && m_lowest_active_bucket < 160) + { + ++m_lowest_active_bucket; + } + } + return; + } + + b.erase(i); + b.push_back(rb.back()); + rb.erase(rb.end() - 1); +} + +void routing_table::add_router_node(udp::endpoint router) +{ + m_router_nodes.insert(router); +} + +// 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 addr) +{ + if (m_router_nodes.find(addr) != m_router_nodes.end()) return false; + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + + bucket_t::iterator i = std::find_if(b.begin(), b.end() + , bind(std::equal_to() + , bind(&node_entry::id, _1), id)); + + bool ret = need_bootstrap(); + + m_bucket_activity[bucket_index] = second_clock::universal_time(); + + if (i != b.end()) + { + // TODO: what do we do if we see a node with + // the same id as a node at a different address? +// assert(i->addr == addr); + + // 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 + b.erase(i); + b.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "replacing node: " << id << " " << addr; + 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 ((int)b.size() < m_bucket_size) + { + b.push_back(node_entry(id, addr)); + // if bucket index is 0, the node is ourselves + // don't updated m_lowest_active_bucket + if (bucket_index < m_lowest_active_bucket + && bucket_index > 0) + m_lowest_active_bucket = bucket_index; +// TORRENT_LOG(table) << "inserting node: " << id << " " << addr; + return ret; + } + + // if there is no room, we look for nodes marked as stale + // in the k-bucket. If we find one, we can replace it. + // 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. + + i = std::max_element(b.begin(), b.end() + , bind(std::less() + , bind(&node_entry::fail_count, _1) + , bind(&node_entry::fail_count, _2))); + + if (i != b.end() && i->fail_count > 0) + { + // i points to a node that has been marked + // as stale. Replace it with this new one + b.erase(i); + b.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr; + return ret; + } + + // 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. + + bucket_t& rb = m_buckets[bucket_index].second; + + i = std::find_if(rb.begin(), rb.end() + , bind(std::equal_to() + , bind(&node_entry::id, _1), id)); + + // if the node is already in the replacement bucket + // just return. + if (i != rb.end()) return ret; + + if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin()); + rb.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr; + return ret; +} + +bool routing_table::need_bootstrap() const +{ + for (const_iterator i = begin(); i != end(); ++i) + { + if (i->fail_count == 0) return false; + } + return true; +} + +// fills the vector with the k nodes from our buckets that +// are nearest to the given id. +void routing_table::find_node(node_id const& target + , std::vector& l, bool include_self, int count) +{ + l.clear(); + if (count == 0) count = m_bucket_size; + l.reserve(count); + + int bucket_index = distance_exp(m_id, target); + bucket_t& b = m_buckets[bucket_index].first; + + // copy all nodes that hasn't failed into the target + // vector. + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , bind(&node_entry::fail_count, _1)); + assert((int)l.size() <= count); + + if ((int)l.size() == count) + { + assert(std::count_if(l.begin(), l.end() + , boost::bind(std::not_equal_to() + , boost::bind(&node_entry::fail_count, _1), 0)) == 0); + return; + } + + // if we didn't have enough nodes in that bucket + // we have to reply with nodes from buckets closer + // to us. i.e. all the buckets in the range + // [0, bucket_index) if we are to include ourself + // or [1, bucket_index) if not. + bucket_t tmpb; + for (int i = include_self?0:1; i < count; ++i) + { + bucket_t& b = m_buckets[i].first; + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb) + , bind(&node_entry::fail_count, _1)); + } + + std::random_shuffle(tmpb.begin(), tmpb.end()); + size_t to_copy = (std::min)(m_bucket_size - l.size() + , tmpb.size()); + std::copy(tmpb.begin(), tmpb.begin() + to_copy + , std::back_inserter(l)); + + assert((int)l.size() <= m_bucket_size); + + // return if we have enough nodes or if the bucket index + // is the biggest index available (there are no more buckets) + // to look in. + if ((int)l.size() == count + || bucket_index == (int)m_buckets.size() - 1) + { + assert(std::count_if(l.begin(), l.end() + , boost::bind(std::not_equal_to() + , boost::bind(&node_entry::fail_count, _1), 0)) == 0); + return; + } + + for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i) + { + bucket_t& b = m_buckets[i].first; + + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , bind(&node_entry::fail_count, _1)); + if ((int)l.size() >= count) + { + l.erase(l.begin() + count, l.end()); + assert(std::count_if(l.begin(), l.end() + , boost::bind(std::not_equal_to() + , boost::bind(&node_entry::fail_count, _1), 0)) == 0); + return; + } + } + assert((int)l.size() == count + || std::distance(l.begin(), l.end()) < m_bucket_size); + assert((int)l.size() <= count); + + assert(std::count_if(l.begin(), l.end() + , boost::bind(std::not_equal_to() + , boost::bind(&node_entry::fail_count, _1), 0)) == 0); +} + +routing_table::iterator routing_table::begin() const +{ + return iterator(m_buckets.begin(), m_buckets.end()); +} + +routing_table::iterator routing_table::end() const +{ + return iterator(m_buckets.end(), m_buckets.end()); +} + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/rpc_manager.cpp b/library/kademlia/rpc_manager.cpp new file mode 100644 index 000000000..770b11bf5 --- /dev/null +++ b/library/kademlia/rpc_manager.cpp @@ -0,0 +1,356 @@ +/* + +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 + +#include +#include +#include +#include +#include +#include + +#include + +using boost::posix_time::ptime; +using boost::posix_time::time_duration; +using boost::posix_time::microsec_clock; +using boost::posix_time::seconds; +using boost::posix_time::milliseconds; +using boost::shared_ptr; +using boost::bind; + +namespace libtorrent { namespace dht +{ + +namespace io = libtorrent::detail; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(rpc) +#endif + +node_id generate_id(); + +rpc_manager::rpc_manager(fun const& f, node_id const& our_id + , routing_table& table, send_fun const& sf) + : m_next_transaction_id(rand() % max_transactions) + , m_oldest_transaction_id(m_next_transaction_id) + , m_incoming(f) + , m_send(sf) + , m_our_id(our_id) + , m_table(table) + , m_timer(boost::posix_time::microsec_clock::universal_time()) + , m_random_number(generate_id()) +{ + std::srand(time(0)); +} + +rpc_manager::~rpc_manager() +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Destructing"; +#endif +} + +#ifndef NDEBUG +void rpc_manager::check_invariant() const +{ + assert(m_oldest_transaction_id >= 0); + assert(m_oldest_transaction_id < max_transactions); + assert(m_next_transaction_id >= 0); + assert(m_next_transaction_id < max_transactions); + assert(!m_transactions[m_next_transaction_id]); + + for (int i = (m_next_transaction_id + 1) % max_transactions; + i != m_oldest_transaction_id; i = (i + 1) % max_transactions) + { + assert(!m_transactions[i]); + } +} +#endif + +bool rpc_manager::incoming(msg const& m) +{ + INVARIANT_CHECK; + + if (m.reply) + { + // if we don't have the transaction id in our + // request list, ignore the packet + + if (m.transaction_id.size() != 2) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " + << m.transaction_id.size() << " from " << m.addr; +#endif + return false; + } + + std::string::const_iterator i = m.transaction_id.begin(); + int tid = io::read_uint16(i); + + if (tid >= (int)m_transactions.size() + || tid < 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with unknown transaction id: " + << tid << " from " << m.addr; +#endif + return false; + } + + boost::shared_ptr o = m_transactions[tid]; + + if (!o) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with unknown transaction id: " + << tid << " from " << m.addr; +#endif + return false; + } + + if (m.addr != o->target_addr) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: " + << tid << " from " << m.addr; +#endif + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app); + reply_stats << m.addr << "\t" << (microsec_clock::universal_time() + - o->sent).total_milliseconds() << std::endl; +#endif + o->reply(m); + m_transactions[tid].reset(); + + if (m.piggy_backed_ping) + { + // there is a ping request piggy + // backed in this reply + msg ph; + ph.message_id = messages::ping; + ph.transaction_id = m.ping_transaction_id; + ph.id = m_our_id; + ph.addr = m.addr; + + msg empty; + + reply(empty, ph); + } + return m_table.node_seen(m.id, m.addr); + } + else + { + // this is an incoming request + m_incoming(m); + } + return false; +} + +time_duration rpc_manager::tick() +{ + INVARIANT_CHECK; + + using boost::posix_time::microsec_clock; + + const int timeout_ms = 20 * 1000; + + // look for observers that has timed out + + if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms); + + for (;m_next_transaction_id != m_oldest_transaction_id; + m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions) + { + assert(m_oldest_transaction_id >= 0); + assert(m_oldest_transaction_id < max_transactions); + + boost::shared_ptr o = m_transactions[m_oldest_transaction_id]; + if (!o) continue; + + time_duration diff = o->sent + milliseconds(timeout_ms) + - microsec_clock::universal_time(); + if (diff > seconds(0)) + { + if (diff < seconds(1)) return seconds(1); + return diff; + } + + m_transactions[m_oldest_transaction_id].reset(); + o->timeout(); + } + return milliseconds(timeout_ms); +} + +unsigned int rpc_manager::new_transaction_id() +{ + INVARIANT_CHECK; + + unsigned int tid = m_next_transaction_id; + m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions; +// boost::shared_ptr o = m_transactions[m_next_transaction_id]; + if (m_transactions[m_next_transaction_id]) + { + m_transactions[m_next_transaction_id].reset(); + assert(m_oldest_transaction_id == m_next_transaction_id); + } + if (m_oldest_transaction_id == m_next_transaction_id) + { + m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent" + " messages! limit: " << (int)max_transactions; +#endif + update_oldest_transaction_id(); + } + +#ifndef NDEBUG + assert(!m_transactions[m_next_transaction_id]); + for (int i = (m_next_transaction_id + 1) % max_transactions; + i != m_oldest_transaction_id; i = (i + 1) % max_transactions) + { + assert(!m_transactions[i]); + } +#endif + +// hopefully this wouldn't happen, but unfortunately, the +// traversal algorithm will simply fail in case its connections +// are overwritten. If timeout() is called, it will likely spawn +// another connection, which in turn will close the next one +// and so on. +// if (o) o->timeout(); + return tid; +} + +void rpc_manager::update_oldest_transaction_id() +{ + INVARIANT_CHECK; + + assert(m_oldest_transaction_id != m_next_transaction_id); + while (!m_transactions[m_oldest_transaction_id]) + { + m_oldest_transaction_id = (m_oldest_transaction_id + 1) + % max_transactions; + if (m_oldest_transaction_id == m_next_transaction_id) + break; + } +} + +void rpc_manager::invoke(int message_id, udp::endpoint target_addr + , shared_ptr o) +{ + INVARIANT_CHECK; + + msg m; + m.message_id = message_id; + m.reply = false; + m.id = m_our_id; + m.addr = target_addr; + int tid = new_transaction_id(); + m.transaction_id.clear(); + std::back_insert_iterator out(m.transaction_id); + io::write_uint16(tid, out); + + o->send(m); + + m_transactions[tid] = o; + o->sent = boost::posix_time::microsec_clock::universal_time(); + o->target_addr = target_addr; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id] + << " -> " << target_addr; +#endif + m_send(m); +} + +void rpc_manager::reply(msg& m, msg const& reply_to) +{ + INVARIANT_CHECK; + + if (m.message_id != messages::error) + m.message_id = reply_to.message_id; + m.addr = reply_to.addr; + m.reply = true; + m.piggy_backed_ping = false; + m.id = m_our_id; + m.transaction_id = reply_to.transaction_id; + + m_send(m); +} + +namespace +{ + struct dummy_observer : observer + { + virtual void reply(msg const&) {} + virtual void timeout() {} + virtual void send(msg&) {} + }; +} + +void rpc_manager::reply_with_ping(msg& m, msg const& reply_to) +{ + INVARIANT_CHECK; + + if (m.message_id != messages::error) + m.message_id = reply_to.message_id; + m.addr = reply_to.addr; + m.reply = true; + m.piggy_backed_ping = true; + m.id = m_our_id; + m.transaction_id = reply_to.transaction_id; + + int ptid = new_transaction_id(); + m.ping_transaction_id.clear(); + std::back_insert_iterator out(m.ping_transaction_id); + io::write_uint16(ptid, out); + + boost::shared_ptr o(new dummy_observer); + m_transactions[ptid] = o; + o->sent = boost::posix_time::microsec_clock::universal_time(); + o->target_addr = m.addr; + + m_send(m); +} + + + +} } // namespace libtorrent::dht + diff --git a/library/kademlia/traversal_algorithm.cpp b/library/kademlia/traversal_algorithm.cpp new file mode 100644 index 000000000..88c62f836 --- /dev/null +++ b/library/kademlia/traversal_algorithm.cpp @@ -0,0 +1,162 @@ +/* + +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 +#include +#include + +#include + +using boost::bind; +using asio::ip::udp; + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(traversal) +#endif + +void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) +{ + if (m_failed.find(addr) != m_failed.end()) return; + + result const entry(id, addr, flags); + + std::vector::iterator i = std::lower_bound( + m_results.begin() + , m_results.end() + , entry + , bind( + compare_ref + , bind(&result::id, _1) + , bind(&result::id, _2) + , m_target + ) + ); + + if (i == m_results.end() || i->id != id) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "adding result: " << id << " " << addr; +#endif + m_results.insert(i, entry); + } +} + +void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) +{ + add_entry(id, addr, 0); +} + +void traversal_algorithm::finished(node_id const& id) +{ + --m_invoke_count; + add_requests(); + if (m_invoke_count == 0) done(); +} + +void traversal_algorithm::failed(node_id const& id) +{ + m_invoke_count--; + + std::vector::iterator i = std::find_if( + m_results.begin() + , m_results.end() + , bind( + std::equal_to() + , bind(&result::id, _1) + , id + ) + ); + + assert(i != m_results.end()); + + assert(i->flags & result::queried); + m_failed.insert(i->addr); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr; +#endif + m_results.erase(i); + m_table.node_failed(id); + add_requests(); + if (m_invoke_count == 0) done(); +} + +void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr) +{ + invoke(id, addr); + ++m_invoke_count; +} + +namespace +{ + bool bitwise_nand(unsigned char lhs, unsigned char rhs) + { + return (lhs & rhs) == 0; + } +} + +void traversal_algorithm::add_requests() +{ + while (m_invoke_count < m_branch_factor) + { + // Find the first node that hasn't already been queried. + // TODO: Better heuristic + std::vector::iterator i = std::find_if( + m_results.begin() + , last_iterator() + , bind( + &bitwise_nand + , bind(&result::flags, _1) + , (unsigned char)result::queried + ) + ); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i); +#endif + + if (i == last_iterator()) break; + + add_request(i->id, i->addr); + i->flags |= result::queried; + } +} + +std::vector::iterator traversal_algorithm::last_iterator() +{ + return (int)m_results.size() >= m_max_results ? + m_results.begin() + m_max_results + : m_results.end(); +} + +} } // namespace libtorrent::dht + diff --git a/library/makeit b/library/makeit new file mode 100755 index 000000000..3d7f60b8d --- /dev/null +++ b/library/makeit @@ -0,0 +1 @@ +python setup.py build diff --git a/library/peer_connection.cpp b/library/peer_connection.cpp new file mode 100755 index 000000000..3be09de21 --- /dev/null +++ b/library/peer_connection.cpp @@ -0,0 +1,2050 @@ +/* + +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 +#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/aux_/session_impl.hpp" + +using namespace boost::posix_time; +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + void intrusive_ptr_add_ref(peer_connection const* c) + { + assert(c->m_refs >= 0); + assert(c != 0); + ++c->m_refs; + } + + void intrusive_ptr_release(peer_connection const* c) + { + assert(c->m_refs > 0); + assert(c != 0); + if (--c->m_refs == 0) + delete c; + } + + peer_connection::peer_connection( + session_impl& ses + , boost::weak_ptr tor + , shared_ptr s + , tcp::endpoint const& remote) + : +#ifndef NDEBUG + m_last_choke(boost::posix_time::second_clock::universal_time() + - hours(1)) + , +#endif + m_ses(ses) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_timeout(m_ses.settings().peer_timeout) + , m_last_piece(second_clock::universal_time()) + , m_packet_size(0) + , m_recv_pos(0) + , m_current_send_buffer(0) + , m_write_pos(0) + , m_last_receive(second_clock::universal_time()) + , m_last_sent(second_clock::universal_time()) + , m_socket(s) + , m_remote(remote) + , m_torrent(tor) + , m_active(true) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_num_pieces(0) + , m_desired_queue_size(2) + , m_free_upload(0) + , m_trust_points(0) + , m_assume_fifo(false) + , m_num_invalid_requests(0) + , m_disconnecting(false) + , m_became_uninterested(second_clock::universal_time()) + , m_became_uninteresting(second_clock::universal_time()) + , m_connecting(true) + , m_queued(true) + , m_writing(false) + , m_last_write_size(0) + , m_reading(false) + , m_last_read_size(0) + , m_refs(0) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { +#ifdef TORRENT_VERBOSE_LOGGING + m_logger = m_ses.create_log(m_remote.address().to_string() + "_" + + boost::lexical_cast(m_remote.port())); + (*m_logger) << "*** OUTGOING CONNECTION\n"; +#endif + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + // these numbers are used the first second of connection. + // then the given upload limits will be applied by running + // allocate_resources(). + m_ul_bandwidth_quota.min = 10; + m_ul_bandwidth_quota.max = resource_request::inf; + + if (t->m_ul_bandwidth_quota.given == resource_request::inf) + { + m_ul_bandwidth_quota.given = resource_request::inf; + } + else + { + // just enough to get started with the handshake and bitmask + m_ul_bandwidth_quota.given = 400; + } + + m_dl_bandwidth_quota.min = 10; + m_dl_bandwidth_quota.max = resource_request::inf; + + if (t->m_dl_bandwidth_quota.given == resource_request::inf) + { + m_dl_bandwidth_quota.given = resource_request::inf; + } + else + { + // just enough to get started with the handshake and bitmask + m_dl_bandwidth_quota.given = 400; + } + + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + + if (t->ready_for_connections()) + init(); + } + + peer_connection::peer_connection( + session_impl& ses + , boost::shared_ptr s) + : +#ifndef NDEBUG + m_last_choke(boost::posix_time::second_clock::universal_time() + - hours(1)) + , +#endif + m_ses(ses) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_timeout(m_ses.settings().peer_timeout) + , m_last_piece(second_clock::universal_time()) + , m_packet_size(0) + , m_recv_pos(0) + , m_current_send_buffer(0) + , m_write_pos(0) + , m_last_receive(second_clock::universal_time()) + , m_last_sent(second_clock::universal_time()) + , m_socket(s) + , m_active(false) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_num_pieces(0) + , m_desired_queue_size(2) + , m_free_upload(0) + , m_trust_points(0) + , m_assume_fifo(false) + , m_num_invalid_requests(0) + , m_disconnecting(false) + , m_became_uninterested(second_clock::universal_time()) + , m_became_uninteresting(second_clock::universal_time()) + , m_connecting(false) + , m_queued(false) + , m_writing(false) + , m_last_write_size(0) + , m_reading(false) + , m_last_read_size(0) + , m_refs(0) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { + m_remote = m_socket->remote_endpoint(); + +#ifdef TORRENT_VERBOSE_LOGGING + assert(m_socket->remote_endpoint() == remote()); + m_logger = m_ses.create_log(remote().address().to_string() + "_" + + boost::lexical_cast(remote().port())); + (*m_logger) << "*** INCOMING CONNECTION\n"; +#endif + + + // 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. + // after one second, allocate_resources() will be called + // and the correct bandwidth limits will be set on all + // connections. + + m_ul_bandwidth_quota.min = 10; + m_ul_bandwidth_quota.max = resource_request::inf; + + if (m_ses.m_upload_rate == -1) + { + m_ul_bandwidth_quota.given = resource_request::inf; + } + else + { + // just enough to get started with the handshake and bitmask + m_ul_bandwidth_quota.given = 400; + } + + m_dl_bandwidth_quota.min = 10; + m_dl_bandwidth_quota.max = resource_request::inf; + + if (m_ses.m_download_rate == -1) + { + m_dl_bandwidth_quota.given = resource_request::inf; + } + else + { + // just enough to get started with the handshake and bitmask + m_dl_bandwidth_quota.given = 400; + } + + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + } + + void peer_connection::init() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + assert(t->valid_metadata()); + assert(t->ready_for_connections()); + + m_have_piece.resize(t->torrent_file().num_pieces(), false); + + // now that we have a piece_picker, + // update it with this peers pieces + + // build a vector of all pieces + m_num_pieces = 0; + std::vector piece_list; + for (int i = 0; i < (int)m_have_piece.size(); ++i) + { + if (m_have_piece[i]) + { + ++m_num_pieces; + piece_list.push_back(i); + } + } + + // let the torrent know which pieces the + // peer has, in a shuffled order + bool interesting = false; + for (std::vector::reverse_iterator i = piece_list.rbegin(); + i != piece_list.rend(); ++i) + { + int index = *i; + t->peer_has(index); + if (!t->have_piece(index) + && !t->picker().is_filtered(index)) + interesting = true; + } + + if (piece_list.size() == m_have_piece.size()) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** THIS IS A SEED ***\n"; +#endif + // if we're a seed too, disconnect + if (t->is_seed()) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " we're also a seed, disconnecting\n"; +#endif + throw std::runtime_error("seed to seed connection redundant, disconnecting"); + } + } + + if (interesting) + t->get_policy().peer_is_interesting(*this); + } + + peer_connection::~peer_connection() + { +// INVARIANT_CHECK; + assert(m_disconnecting); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + if (m_logger) + { + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " *** CONNECTION CLOSED\n"; + } +#endif +#ifndef NDEBUG + boost::shared_ptr t = m_torrent.lock(); + if (t) assert(t->connection_for(remote()) != this); +#endif + } + + void peer_connection::announce_piece(int index) + { + // optimization, don't send have messages + // to peers that already have the piece + if (has_piece(index)) return; + write_have(index); + } + + bool peer_connection::has_piece(int i) const + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + assert(t->valid_metadata()); + assert(i >= 0); + assert(i < t->torrent_file().num_pieces()); + return m_have_piece[i]; + } + + std::deque const& peer_connection::request_queue() const + { + return m_request_queue; + } + + std::deque const& peer_connection::download_queue() const + { + return m_download_queue; + } + + std::deque const& peer_connection::upload_queue() const + { + return m_requests; + } + + void peer_connection::add_stat(size_type downloaded, size_type uploaded) + { + INVARIANT_CHECK; + + m_statistics.add_stat(downloaded, uploaded); + } + + std::vector const& peer_connection::get_bitfield() const + { + return m_have_piece; + } + + void peer_connection::received_valid_data() + { + INVARIANT_CHECK; + + m_trust_points++; + // TODO: make this limit user settable + if (m_trust_points > 20) m_trust_points = 20; + } + + void peer_connection::received_invalid_data() + { + INVARIANT_CHECK; + + // we decrease more than we increase, to keep the + // allowed failed/passed ratio low. + // TODO: make this limit user settable + m_trust_points -= 2; + if (m_trust_points < -7) m_trust_points = -7; + } + + int peer_connection::trust_points() const + { + return m_trust_points; + } + + 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; + } + + void peer_connection::reset_upload_quota() + { + m_ul_bandwidth_quota.used = 0; + m_dl_bandwidth_quota.used = 0; + assert(m_ul_bandwidth_quota.left() >= 0); + assert(m_dl_bandwidth_quota.left() >= 0); + setup_send(); + setup_receive(); + } + + // 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 + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + + return p.piece >= 0 + && p.piece < t->torrent_file().num_pieces() + && p.length > 0 + && p.start >= 0 + && (p.length == t->block_size() + || (p.length < t->block_size() + && p.piece == t->torrent_file().num_pieces()-1 + && p.start + p.length == t->torrent_file().piece_size(p.piece))) + && p.start + p.length <= t->torrent_file().piece_size(p.piece) + && p.start % t->block_size() == 0; + } + + struct disconnect_torrent + { + disconnect_torrent(boost::weak_ptr& t): m_t(&t) {} + ~disconnect_torrent() { if (m_t) m_t->reset(); } + void cancel() { m_t = 0; } + private: + boost::weak_ptr* m_t; + }; + + void peer_connection::attach_to_torrent(sha1_hash const& ih) + { + INVARIANT_CHECK; + + assert(!m_disconnecting); + m_torrent = m_ses.find_torrent(ih); + + boost::shared_ptr t = m_torrent.lock(); + + if (t && t->is_aborted()) + { + m_torrent.reset(); + t.reset(); + } + + if (!t) + { + // we couldn't find the torrent! +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " couldn't find a torrent with the given info_hash\n"; +#endif + throw std::runtime_error("got info-hash that is not in our session"); + } + + disconnect_torrent disconnect(m_torrent); + if (t->is_paused()) + { + // paused torrents will not accept + // incoming connections +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " rejected connection to paused torrent\n"; +#endif + throw std::runtime_error("connection rejected by paused torrent"); + } + + // 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 the torrent isn't ready to accept + // connections yet, we'll have to wait with + // our initialization + if (t->ready_for_connections()) init(); + + // assume the other end has no pieces + // if we don't have valid metadata yet, + // leave the vector unallocated + assert(m_num_pieces == 0); + std::fill(m_have_piece.begin(), m_have_piece.end(), false); + disconnect.cancel(); + } + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void peer_connection::incoming_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== KEEPALIVE\n"; +#endif + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void peer_connection::incoming_choke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== CHOKE\n"; +#endif + m_peer_choked = true; + t->get_policy().choked(*this); + + // remove all pieces from this peers download queue and + // remove the 'downloading' flag from piece_picker. + for (std::deque::iterator i = m_download_queue.begin(); + i != m_download_queue.end(); ++i) + { + t->picker().abort_download(*i); + } + for (std::deque::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + // since this piece was skipped, clear it and allow it to + // be requested from other peers + t->picker().abort_download(*i); + } + m_download_queue.clear(); + m_request_queue.clear(); + +#ifndef NDEBUG +// t->picker().integrity_check(m_torrent); +#endif + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void peer_connection::incoming_unchoke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== UNCHOKE\n"; +#endif + m_peer_choked = false; + t->get_policy().unchoked(*this); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void peer_connection::incoming_interested() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== INTERESTED\n"; +#endif + m_peer_interested = true; + t->get_policy().interested(*this); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void peer_connection::incoming_not_interested() + { + INVARIANT_CHECK; + + m_became_uninterested = second_clock::universal_time(); + + // clear the request queue if the client isn't interested + m_requests.clear(); + setup_send(); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== NOT_INTERESTED\n"; +#endif + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + m_peer_interested = false; + t->get_policy().not_interested(*this); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void peer_connection::incoming_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== HAVE [ piece: " << index << "]\n"; +#endif + + // if we got an invalid message, abort + if (index >= (int)m_have_piece.size() || index < 0) + throw protocol_error("got 'have'-message with higher index " + "than the number of pieces"); + + if (m_have_piece[index]) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " got redundant HAVE message for index: " << index << "\n"; +#endif + } + else + { + m_have_piece[index] = true; + + // only update the piece_picker if + // we have the metadata + if (t->valid_metadata()) + { + ++m_num_pieces; + t->peer_has(index); + + if (!t->have_piece(index) + && !is_interesting() + && !t->picker().is_filtered(index)) + t->get_policy().peer_is_interesting(*this); + } + + if (t->is_seed() && is_seed()) + { + throw protocol_error("seed to seed connection redundant, disconnecting"); + } + } + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void peer_connection::incoming_bitfield(std::vector const& bitfield) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== BITFIELD\n"; +#endif + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && (bitfield.size() / 8) != (m_have_piece.size() / 8)) + throw protocol_error("got bitfield with invalid size"); + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + if (!t->valid_metadata()) + { + m_have_piece = bitfield; + m_num_pieces = std::count(bitfield.begin(), bitfield.end(), true); + return; + } + + // build a vector of all pieces + std::vector piece_list; + for (int i = 0; i < (int)m_have_piece.size(); ++i) + { + bool have = bitfield[i]; + if (have && !m_have_piece[i]) + { + m_have_piece[i] = true; + ++m_num_pieces; + piece_list.push_back(i); + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + t->peer_lost(i); + } + } + + // let the torrent know which pieces the + // peer has, in a shuffled order + bool interesting = false; + for (std::vector::reverse_iterator i = piece_list.rbegin(); + i != piece_list.rend(); ++i) + { + int index = *i; + t->peer_has(index); + if (!t->have_piece(index) + && !t->picker().is_filtered(index)) + interesting = true; + } + + if (piece_list.size() == m_have_piece.size()) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** THIS IS A SEED ***\n"; +#endif + // if we're a seed too, disconnect + if (t->is_seed()) + { + throw protocol_error("seed to seed connection redundant, disconnecting"); + } + } + + if (interesting) t->get_policy().peer_is_interesting(*this); + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void peer_connection::incoming_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + if (!t->valid_metadata()) + { + // if we don't have valid metadata yet, + // we shouldn't get a request +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== UNEXPECTED_REQUEST [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " ]\n"; +#endif + 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. +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== TOO MANY REQUESTS [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " ]\n"; +#endif + 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) + { + // if we have choked the client + // ignore the request + if (m_choked) + return; + + m_requests.push_back(r); + fill_send_buffer(); +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; +#endif + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== INVALID_REQUEST [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " ]\n"; +#endif + + ++m_num_invalid_requests; + + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert(invalid_request_alert( + r + , t->get_handle() + , m_remote + , m_peer_id + , "peer sent an illegal piece request, ignoring")); + } + } + } + + void peer_connection::incoming_piece_fragment() + { + m_last_piece = second_clock::universal_time(); + } + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void peer_connection::incoming_piece(peer_request const& p, char const* data) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== PIECE [ piece: " << p.piece << " | " + "b: " << p.start / t->block_size() << " | " + "s: " << p.start << " | " + "l: " << p.length << " | " + "ds: " << statistics().download_rate() << " | " + "qs: " << m_desired_queue_size << " ]\n"; +#endif + + if (!verify_piece(p)) + { +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== INVALID_PIECE [ piece: " << p.piece << " | " + "start: " << p.start << " | " + "length: " << p.length << " ]\n"; +#endif + throw protocol_error("got invalid piece packet"); + } + + using namespace boost::posix_time; + + piece_picker& picker = t->picker(); + + piece_block block_finished(p.piece, p.start / t->block_size()); + std::deque::iterator b + = std::find( + m_download_queue.begin() + , m_download_queue.end() + , block_finished); + + std::deque::iterator i; + + if (b != m_download_queue.end()) + { + if (m_assume_fifo) + { + for (i = m_download_queue.begin(); + i != b; ++i) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | " + "b: " << i->block_index << " ] ***\n"; +#endif + // since this piece was skipped, clear it and allow it to + // be requested from other peers + // TODO: send cancel? + picker.abort_download(*i); + } + + // remove the request that just finished + // from the download queue plus the + // skipped blocks. + m_download_queue.erase(m_download_queue.begin() + , boost::next(b)); + } + else + { + m_download_queue.erase(b); + } + send_block_requests(); + } + else + { + // cancel the block from the + // peer that has taken over it. + boost::optional peer + = t->picker().get_downloader(block_finished); + if (peer) + { + peer_connection* pc = t->connection_for(*peer); + if (pc && pc != this) + { + pc->cancel_request(block_finished); + } + } + else + { + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert( + peer_error_alert( + m_remote + , m_peer_id + , "got a block that was not requested")); + } +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** The block we just got was not in the " + "request queue ***\n"; +#endif + } + } + + // if the block we got is already finished, then ignore it + if (picker.is_finished(block_finished)) + { + t->received_redundant_data(p.length); + return; + } + + t->filesystem().write(data, p.piece, p.start, p.length); + + bool was_seed = t->is_seed(); + bool was_finished = picker.num_filtered() + t->num_pieces() + == t->torrent_file().num_pieces(); + + picker.mark_as_finished(block_finished, m_remote); + + t->get_policy().block_finished(*this, block_finished); + + // if the piece failed, this connection may be closed, and + // detached from the torrent. In that case m_torrent will + // be set to 0. So, we need to temporarily save it in this function + + // did we just finish the piece? + if (picker.is_piece_finished(p.piece)) + { + bool verified = t->verify_piece(p.piece); + if (verified) + { + t->announce_piece(p.piece); + assert(t->valid_metadata()); + if (!was_finished + && picker.num_filtered() + t->num_pieces() + == t->torrent_file().num_pieces()) + { + // 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) + t->finished(); + } + } + else + { + t->piece_failed(p.piece); + } + t->get_policy().piece_finished(p.piece, verified); + + if (!was_seed && t->is_seed()) + { + assert(verified); + t->completed(); + } + } + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void peer_connection::incoming_cancel(peer_request const& r) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; +#endif + + std::deque::iterator i + = std::find(m_requests.begin(), m_requests.end(), r); + + if (i != m_requests.end()) + { + m_requests.erase(i); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " *** GOT CANCEL NOT IN THE QUEUE\n"; +#endif + } + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void peer_connection::incoming_dht_port(int listen_port) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " <== DHT_PORT [ p: " << listen_port << " ]\n"; +#endif +#ifndef TORRENT_DISABLE_DHT + m_ses.add_dht_node(udp::endpoint( + m_remote.address(), listen_port)); +#endif + } + + void peer_connection::add_request(piece_block const& block) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + assert(block.piece_index >= 0); + assert(block.piece_index < t->torrent_file().num_pieces()); + assert(block.block_index >= 0); + assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); + assert(!t->picker().is_downloading(block)); + + t->picker().mark_as_downloading(block, m_remote); + m_request_queue.push_back(block); + } + + void peer_connection::cancel_request(piece_block const& block) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + + assert(block.piece_index >= 0); + assert(block.piece_index < t->torrent_file().num_pieces()); + assert(block.block_index >= 0); + assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); + assert(t->picker().is_downloading(block)); + + t->picker().abort_download(block); + + std::deque::iterator it + = std::find(m_download_queue.begin(), m_download_queue.end(), block); + if (it == m_download_queue.end()) + { + it = std::find(m_request_queue.begin(), m_request_queue.end(), block); + assert(it != m_request_queue.end()); + if (it == m_request_queue.end()) return; + m_request_queue.erase(it); + // 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; + } + else + { + m_download_queue.erase(it); + } + + send_block_requests(); + + int block_offset = block.block_index * t->block_size(); + int block_size + = std::min((int)t->torrent_file().piece_size(block.piece_index)-block_offset, + t->block_size()); + assert(block_size > 0); + assert(block_size <= t->block_size()); + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + + write_cancel(r); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> CANCEL [ piece: " << block.piece_index << " | s: " + << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; +#endif + } + + void peer_connection::send_choke() + { + INVARIANT_CHECK; + + if (m_choked) return; + write_choke(); + m_choked = true; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> CHOKE\n"; +#endif +#ifndef NDEBUG + using namespace boost::posix_time; + m_last_choke = second_clock::universal_time(); +#endif + m_num_invalid_requests = 0; + m_requests.clear(); + } + + void peer_connection::send_unchoke() + { + INVARIANT_CHECK; + +#ifndef NDEBUG + // TODO: once the policy lowers the interval for optimistic + // unchoke, increase this value that interval + // this condition cannot be guaranteed since if peers disconnect + // a new one will be unchoked ignoring when it was last choked + using namespace boost::posix_time; + //assert(second_clock::universal_time() - m_last_choke > seconds(9)); +#endif + + if (!m_choked) return; + write_unchoke(); + m_choked = false; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> UNCHOKE\n"; +#endif + } + + void peer_connection::send_interested() + { + INVARIANT_CHECK; + + if (m_interesting) return; + write_interested(); + m_interesting = true; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> INTERESTED\n"; +#endif + } + + void peer_connection::send_not_interested() + { + INVARIANT_CHECK; + + if (!m_interesting) return; + write_not_interested(); + m_interesting = false; + + m_became_uninteresting = second_clock::universal_time(); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> NOT_INTERESTED\n"; +#endif + } + + void peer_connection::send_block_requests() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(!has_peer_choked()); + + if ((int)m_download_queue.size() >= m_desired_queue_size) return; + + while (!m_request_queue.empty() + && (int)m_download_queue.size() < m_desired_queue_size) + { + piece_block block = m_request_queue.front(); + m_request_queue.pop_front(); + m_download_queue.push_back(block); + + int block_offset = block.block_index * t->block_size(); + int block_size = std::min((int)t->torrent_file().piece_size( + block.piece_index) - block_offset, t->block_size()); + assert(block_size > 0); + assert(block_size <= t->block_size()); + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + + assert(verify_piece(r)); + write_request(r); + + using namespace boost::posix_time; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> REQUEST [ " + "piece: " << block.piece_index << " | " + "b: " << block.block_index << " | " + "s: " << block_offset << " | " + "l: " << block_size << " | " + "ds: " << statistics().download_rate() << " | " + "qs: " << m_desired_queue_size << " ]\n"; +#endif + } + m_last_piece = second_clock::universal_time(); + } + + + void close_socket_ignore_error(boost::shared_ptr s) + { + s->close(asio::ignore_error()); + } + + void peer_connection::disconnect() + { + boost::intrusive_ptr me(this); + + INVARIANT_CHECK; + + if (m_disconnecting) return; + m_disconnecting = true; + m_ses.m_selector.post(boost::bind(&close_socket_ignore_error, m_socket)); + + boost::shared_ptr t = m_torrent.lock(); + + if (t) + { + if (t->valid_metadata()) + { + piece_picker& picker = t->picker(); + + while (!m_download_queue.empty()) + { + picker.abort_download(m_download_queue.back()); + m_download_queue.pop_back(); + } + while (!m_request_queue.empty()) + { + picker.abort_download(m_request_queue.back()); + m_request_queue.pop_back(); + } + } +#ifndef NDEBUG + else + { + assert(m_download_queue.empty()); + assert(m_request_queue.empty()); + } +#endif + t->remove_peer(this); + m_torrent.reset(); + } + + m_ses.close_connection(me); + } + + void peer_connection::set_upload_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < 10) limit = 10; + m_ul_bandwidth_quota.max = limit; + assert(m_ul_bandwidth_quota.max >= m_ul_bandwidth_quota.min); + } + + void peer_connection::set_download_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < 10) limit = 10; + m_dl_bandwidth_quota.max = limit; + assert(m_dl_bandwidth_quota.max >= m_dl_bandwidth_quota.min); + } + + size_type peer_connection::share_diff() const + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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(); + } + + void peer_connection::cut_receive_buffer(int size, int packet_size) + { + INVARIANT_CHECK; + + assert((int)m_recv_buffer.size() >= size); + + std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin()); + + assert(m_recv_pos >= size); + m_recv_pos -= size; + +#ifndef NDEBUG + std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0); +#endif + + m_packet_size = packet_size; + m_recv_buffer.resize(m_packet_size); + } + + void peer_connection::second_tick(float tick_interval) + { + INVARIANT_CHECK; + + ptime now(second_clock::universal_time()); + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + on_tick(); + + if (!t->valid_metadata()) return; + + // calculate the desired download queue size + const float 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 + const int block_size = t->block_size(); + assert(block_size > 0); + + m_desired_queue_size = static_cast(queue_time + * statistics().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; + + if (!m_download_queue.empty() + && now - m_last_piece > seconds(m_ses.settings().piece_timeout)) + { + // 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. +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << to_simple_string(now) + << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size() + << " " << to_simple_string(now - m_last_piece) << "] ***\n"; +#endif + + piece_picker& picker = t->picker(); + for (std::deque::const_iterator i = m_download_queue.begin() + , end(m_download_queue.end()); i != end; ++i) + { + // since this piece was skipped, clear it and allow it to + // be requested from other peers + picker.abort_download(*i); + } + for (std::deque::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + // since this piece was skipped, clear it and allow it to + // be requested from other peers + picker.abort_download(*i); + } + + m_download_queue.clear(); + m_request_queue.clear(); + + // TODO: If we have a limited number of upload + // slots, choke this peer + + m_assume_fifo = true; + + // this will trigger new picking of pieces + t->get_policy().unchoked(*this); + } + + m_statistics.second_tick(tick_interval); + m_ul_bandwidth_quota.used = std::min( + (int)ceil(statistics().upload_rate()) + , m_ul_bandwidth_quota.given); + + // 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_seed() || 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 + if(send_buffer_size() > 0 + || (!m_requests.empty() && !is_choked())) + m_ul_bandwidth_quota.max = resource_request::inf; + else + m_ul_bandwidth_quota.max = m_ul_bandwidth_quota.min; + } + else + { + size_type bias = 0x10000+2*t->block_size() + m_free_upload; + + double break_even_time = 15; // seconds. + size_type have_uploaded = m_statistics.total_payload_upload(); + size_type have_downloaded = m_statistics.total_payload_download(); + double download_speed = m_statistics.download_rate(); + + size_type soon_downloaded = + have_downloaded + (size_type)(download_speed * break_even_time*1.5); + + if(t->ratio() != 1.f) + soon_downloaded = (size_type)(soon_downloaded*(double)t->ratio()); + + double upload_speed_limit = (soon_downloaded - have_uploaded + + bias) / break_even_time; + + upload_speed_limit = std::min(upload_speed_limit, + (double)std::numeric_limits::max()); + + m_ul_bandwidth_quota.max + = std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min); + } + if (m_ul_bandwidth_quota.given > m_ul_bandwidth_quota.max) + m_ul_bandwidth_quota.given = m_ul_bandwidth_quota.max; + + if (m_ul_bandwidth_quota.used > m_ul_bandwidth_quota.given) + m_ul_bandwidth_quota.used = m_ul_bandwidth_quota.given; + + fill_send_buffer(); +/* + size_type diff = share_diff(); + + enum { block_limit = 2 }; // how many blocks difference is considered unfair + + // if the peer has been choked, send the current piece + // as fast as possible + if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked()) + { + // 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_ul_bandwidth_quota.wanted = std::numeric_limits::max(); + } + else + { + float ratio = m_torrent->ratio(); + // if we have downloaded too much, response with an + // upload rate of 10 kB/s more than we dowlload + // if we have uploaded too much, send with a rate of + // 10 kB/s less than we receive + int bias = 0; + if (diff > -block_limit*m_torrent->block_size()) + { + bias = static_cast(m_statistics.download_rate() * ratio) / 2; + if (bias < 10*1024) bias = 10*1024; + } + else + { + bias = -static_cast(m_statistics.download_rate() * ratio) / 2; + } + m_ul_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; + + // the maximum send_quota given our download rate from this peer + if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256; + } +*/ + } + + void peer_connection::fill_send_buffer() + { + INVARIANT_CHECK; + + 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! + // TODO: the buffer size should probably be dependent on the transfer speed + while (!m_requests.empty() + && (send_buffer_size() < t->block_size() * 6) + && !m_choked) + { + assert(t->valid_metadata()); + peer_request& r = m_requests.front(); + + assert(r.piece >= 0); + assert(r.piece < (int)m_have_piece.size()); + assert(t->have_piece(r.piece)); + assert(r.start + r.length <= t->torrent_file().piece_size(r.piece)); + assert(r.length > 0 && r.start >= 0); + + write_piece(r); + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << to_simple_string(second_clock::universal_time()) + << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; +#endif + + m_requests.erase(m_requests.begin()); + + if (m_requests.empty() + && m_num_invalid_requests > 0 + && is_peer_interested() + && !is_seed()) + { + // this will make the peer clear + // its download queue and re-request + // pieces. Hopefully it will not + // send invalid requests then + send_choke(); + send_unchoke(); + } + } + } + + void peer_connection::setup_send() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_writing) return; + if (!can_write()) return; + + assert(!m_writing); + + int sending_buffer = (m_current_send_buffer + 1) & 1; + if (m_send_buffer[sending_buffer].empty()) + { + // thise means we have to swap buffer, because there's no + // previous buffer we're still waiting for. + std::swap(m_current_send_buffer, sending_buffer); + m_write_pos = 0; + } + + // send the actual buffer + if (!m_send_buffer[sending_buffer].empty()) + { + int amount_to_send + = std::min(m_ul_bandwidth_quota.left() + , (int)m_send_buffer[sending_buffer].size() - m_write_pos); + + assert(amount_to_send > 0); + + assert(m_write_pos < (int)m_send_buffer[sending_buffer].size()); + m_socket->async_write_some(asio::buffer( + &m_send_buffer[sending_buffer][m_write_pos], amount_to_send) + , bind(&peer_connection::on_send_data, self(), _1, _2)); + + m_writing = true; + m_last_write_size = amount_to_send; + m_ul_bandwidth_quota.used += m_last_write_size; + } + } + + void peer_connection::setup_receive() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_reading) return; + if (!can_read()) return; + + assert(m_packet_size > 0); + int max_receive = std::min( + m_dl_bandwidth_quota.left() + , m_packet_size - m_recv_pos); + + assert(m_recv_pos >= 0); + assert(m_packet_size > 0); + assert(m_dl_bandwidth_quota.left() > 0); + assert(max_receive > 0); + + assert(can_read()); + m_socket->async_read_some(asio::buffer(&m_recv_buffer[m_recv_pos] + , max_receive), bind(&peer_connection::on_receive_data, self(), _1, _2)); + m_reading = true; + m_last_read_size = max_receive; + m_dl_bandwidth_quota.used += max_receive; + assert(m_dl_bandwidth_quota.used <= m_dl_bandwidth_quota.given); + } + + void peer_connection::reset_recv_buffer(int packet_size) + { + assert(packet_size > 0); + m_recv_pos = 0; + m_packet_size = packet_size; + if (int(m_recv_buffer.size()) < m_packet_size) + m_recv_buffer.resize(m_packet_size); + } + + void peer_connection::send_buffer(char const* begin, char const* end) + { + std::vector& buf = m_send_buffer[m_current_send_buffer]; + buf.insert(buf.end(), begin, end); + 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) + { + std::vector& buf = m_send_buffer[m_current_send_buffer]; + buf.resize(buf.size() + size); + buffer::interval ret(&buf[0] + buf.size() - size, &buf[0] + buf.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 + // -------------------------- + + // throws exception when the client should be disconnected + void peer_connection::on_receive_data(const asio::error& error + , std::size_t bytes_transferred) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + assert(m_reading); + assert(m_last_read_size > 0); + assert(m_last_read_size >= int(bytes_transferred)); + m_reading = false; + // correct the dl quota usage, if not all of the buffer was actually read + m_dl_bandwidth_quota.used -= m_last_read_size - bytes_transferred; + m_last_read_size = 0; + + if (error) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "**ERROR**: " << error.what() << "\n"; +#endif + on_receive(error, bytes_transferred); + throw std::runtime_error(error.what()); + } + + if (m_disconnecting) return; + + assert(m_packet_size > 0); + assert(bytes_transferred > 0); + + m_last_receive = second_clock::universal_time(); + m_recv_pos += bytes_transferred; + + // this will reset the m_recv_pos to 0 if the + // entire packet was received + // it is important that this is done before + // setup_receive() is called. Therefore, fire() is + // called before setup_receive(). + assert(m_recv_pos <= m_packet_size); + set_to_zero reset(m_recv_pos, m_recv_pos == m_packet_size); + + { + INVARIANT_CHECK; + on_receive(error, bytes_transferred); + } + + assert(m_packet_size > 0); + + // do the reset immediately + reset.fire(); + + setup_receive(); + } + catch (file_error& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + m_ses.connection_failed(m_socket, remote(), e.what()); + return; + } + + if (t->alerts().should_post(alert::fatal)) + { + t->alerts().post_alert( + file_error_alert(t->get_handle() + , std::string("torrent paused: ") + e.what())); + } + t->pause(); + } + catch (std::exception& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), e.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + } + + bool peer_connection::can_write() const + { + INVARIANT_CHECK; + + // if we have requests or pending data to be sent or announcements to be made + // we want to send data + return (!m_send_buffer[m_current_send_buffer].empty() + || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + && m_ul_bandwidth_quota.left() > 0 + && !m_connecting; + } + + bool peer_connection::can_read() const + { + INVARIANT_CHECK; + + return m_dl_bandwidth_quota.left() > 0 && !m_connecting; + } + + void peer_connection::connect() + { + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() << "\n"; +#endif + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + m_queued = false; + assert(m_connecting); + m_socket->open(asio::ip::tcp::v4()); + m_socket->bind(t->get_interface()); + m_socket->async_connect(m_remote + , bind(&peer_connection::on_connection_complete, self(), _1)); + + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert(peer_error_alert( + m_remote, m_peer_id, "connecting to peer")); + } + } + + void peer_connection::on_connection_complete(asio::error const& e) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTION FAILED: " << m_remote.address().to_string() << "\n"; +#endif + m_ses.connection_failed(m_socket, m_remote, e.what()); + return; + } + + if (m_disconnecting) return; + m_last_receive = second_clock::universal_time(); + + // this means the connection just succeeded + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "COMPLETED: " << m_remote.address().to_string() << "\n"; +#endif + + m_connecting = false; + m_ses.connection_completed(self()); + on_connected(); + setup_send(); + } + catch (std::exception& ex) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), ex.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + // throws exception when the client should be disconnected + void peer_connection::on_send_data(asio::error const& error + , std::size_t bytes_transferred) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + assert(m_writing); + assert(m_last_write_size > 0); + m_writing = false; + // correct the ul quota usage, if not all of the buffer was sent + m_ul_bandwidth_quota.used -= m_last_write_size - bytes_transferred; + m_last_write_size = 0; + m_write_pos += bytes_transferred; + + if (error) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "**ERROR**: " << error.what() << "\n"; +#endif + throw std::runtime_error(error.what()); + } + if (m_disconnecting) return; + + assert(!m_connecting); + assert(bytes_transferred > 0); + + int sending_buffer = (m_current_send_buffer + 1) & 1; + + assert(int(m_send_buffer[sending_buffer].size()) >= m_write_pos); + if (int(m_send_buffer[sending_buffer].size()) == m_write_pos) + { + m_send_buffer[sending_buffer].clear(); + m_write_pos = 0; + } + + m_last_sent = second_clock::universal_time(); + + on_sent(error, bytes_transferred); + fill_send_buffer(); + setup_send(); + } + catch (std::exception& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), e.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + } + + +#ifndef NDEBUG + void peer_connection::check_invariant() const + { + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + typedef session_impl::torrent_map torrent_map; + torrent_map& m = m_ses.m_torrents; + for (torrent_map::iterator i = m.begin(), end(m.end()); i != end; ++i) + { + torrent& t = *i->second; + assert(t.connection_for(m_remote) != this); + } + return; + } + + if (!m_in_constructor && t->connection_for(remote()) != this) + { + assert(false); + } + + if (t->valid_metadata()) + { + int piece_count = std::count(m_have_piece.begin() + , m_have_piece.end(), true); + if (m_num_pieces != piece_count) + { + assert(false); + } + } + + assert(m_write_pos <= int(m_send_buffer[ + (m_current_send_buffer + 1) & 1].size())); + } +#endif + + bool peer_connection::has_timed_out() const + { + // TODO: the timeout should be called by an event + INVARIANT_CHECK; + + using namespace boost::posix_time; + + ptime now(second_clock::universal_time()); + + // if the socket is still connecting, don't + // consider it timed out. Because Windows XP SP2 + // may delay connection attempts. + if (m_connecting) return false; + + // if the peer hasn't said a thing for a certain + // time, it is considered to have timed out + time_duration d; + d = second_clock::universal_time() - m_last_receive; + if (d > seconds(m_timeout)) return true; + + // 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; + // TODO: these timeouts should be user settable + if (!m_interesting + && !m_peer_interested + && d1 > minutes(10) + && d2 > minutes(10)) + { + return true; + } + + return false; + } + + + void peer_connection::keep_alive() + { + INVARIANT_CHECK; + + boost::posix_time::time_duration d; + d = second_clock::universal_time() - m_last_sent; + if (d.total_seconds() < m_timeout / 2) return; + + if (m_connecting) return; + + write_keepalive(); + } + + bool peer_connection::is_seed() const + { + INVARIANT_CHECK; + // if m_num_pieces == 0, we probably doesn't have the + // metadata yet. + return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0; + } +} + diff --git a/library/piece_picker.cpp b/library/piece_picker.cpp new file mode 100755 index 000000000..61e7df66f --- /dev/null +++ b/library/piece_picker.cpp @@ -0,0 +1,1198 @@ +/* + +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 +#include +#include + +// non-standard header, is_sorted() +//#include + +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +#ifndef NDEBUG +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#endif + +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK + +namespace libtorrent +{ + + piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks) + : m_piece_info(2) + , m_downloading_piece_info(2) + , m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece) + , m_num_filtered(0) + , m_num_have_filtered(0) + , m_sequenced_download_threshold(100) + { + assert(blocks_per_piece > 0); + assert(total_num_blocks >= 0); + + // the piece index is stored in 20 bits, which limits the allowed + // number of pieces somewhat + if (m_piece_map.size() >= piece_pos::we_have_index) + throw std::runtime_error("too many pieces in torrent"); + + m_blocks_per_piece = blocks_per_piece; + m_blocks_in_last_piece = total_num_blocks % blocks_per_piece; + if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; + + assert(m_blocks_per_piece <= max_blocks_per_piece); + assert(m_blocks_in_last_piece <= m_blocks_per_piece); + assert(m_blocks_in_last_piece <= max_blocks_per_piece); + + // allocate the piece_map to cover all pieces + // and make them invalid (as if though we already had every piece) + std::fill(m_piece_map.begin(), m_piece_map.end() + , piece_pos(0, piece_pos::we_have_index)); + } + + // pieces is a bitmask with the pieces we have + void piece_picker::files_checked( + const std::vector& pieces + , const std::vector& unfinished) + { + // build a vector of all the pieces we don't have + std::vector piece_list; + piece_list.reserve(std::count(pieces.begin(), pieces.end(), false)); + + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) continue; + int index = static_cast(i - pieces.begin()); + if (m_piece_map[index].filtered) + { + ++m_num_filtered; + --m_num_have_filtered; + m_piece_map[index].index = 0; + } + else + { + piece_list.push_back(index); + } + } + + // add the pieces to the piece_picker + for (std::vector::reverse_iterator i = piece_list.rbegin(); + i != piece_list.rend(); ++i) + { + int index = *i; + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + assert(m_piece_map[index].index == piece_pos::we_have_index); + assert(m_piece_map[index].peer_count == 0); + assert(m_piece_info.size() == 2); + + add(index); + assert(m_piece_map[index].index != piece_pos::we_have_index); + } + + // if we have fast resume info + // use it + if (!unfinished.empty()) + { + for (std::vector::const_iterator i + = unfinished.begin(); i != unfinished.end(); ++i) + { + tcp::endpoint peer; + for (int j = 0; j < m_blocks_per_piece; ++j) + { + if (i->finished_blocks[j]) + mark_as_finished(piece_block(i->index, j), peer); + } + } + } + } + + void piece_picker::set_sequenced_download_threshold( + int sequenced_download_threshold) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + if (sequenced_download_threshold == m_sequenced_download_threshold) + return; + + int old_limit = m_sequenced_download_threshold; + m_sequenced_download_threshold = sequenced_download_threshold; + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold)) + { + piece_pos& p = *i; + if (p.index == piece_pos::we_have_index) continue; + int prev_priority = p.priority(old_limit); + move(p.downloading, p.filtered, prev_priority, p.index); + } + } + + typedef std::vector info_t; + + if (old_limit < sequenced_download_threshold) + { + // the threshold was incremented, in case + // the previous max availability was reached + // we need to shuffle that bucket, if not, we + // don't have to do anything + if (int(m_piece_info.size()) > old_limit) + { + info_t& in = m_piece_info[old_limit]; + std::random_shuffle(in.begin(), in.end()); + int c = 0; + for (info_t::iterator i = in.begin() + , end(in.end()); i != end; ++i) + { + m_piece_map[*i].index = c++; + assert(m_piece_map[*i].priority(old_limit) == old_limit); + } + } + } + else if (int(m_piece_info.size()) > sequenced_download_threshold) + { + info_t& in = m_piece_info[sequenced_download_threshold]; + std::sort(in.begin(), in.end()); + int c = 0; + for (info_t::iterator i = in.begin() + , end(in.end()); i != end; ++i) + { + m_piece_map[*i].index = c++; + assert(m_piece_map[*i].priority( + sequenced_download_threshold) == sequenced_download_threshold); + } + } + } + +#ifndef NDEBUG + + void piece_picker::check_invariant(const torrent* t) const + { + assert(sizeof(piece_pos) == 4); + + if (t != 0) + assert((int)m_piece_map.size() == t->torrent_file().num_pieces()); + + int num_filtered = 0; + int num_have_filtered = 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()); + if (i->filtered) + { + if (i->index != piece_pos::we_have_index) + ++num_filtered; + else + ++num_have_filtered; + } + 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++; + } + + 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) + { + assert(num_downloaders == 1); + } + else + { + assert(num_downloaders == 0); + } +*/ + } + + if (i->index == piece_pos::we_have_index) + { + assert(t == 0 || t->have_piece(index)); + assert(i->downloading == 0); + + // make sure there's no entry + // with this index. (there shouldn't + // be since the piece_map is piece_pos::we_have_index) + for (std::vector >::const_iterator i = m_piece_info.begin(); + i != m_piece_info.end(); ++i) + { + for (std::vector::const_iterator j= i->begin(); + j != i->end(); ++j) + { + assert(*j != index); + } + } + + for (std::vector >::const_iterator i = m_downloading_piece_info.begin(); + i != m_downloading_piece_info.end(); ++i) + { + for (std::vector::const_iterator j = i->begin(); + j != i->end(); ++j) + { + assert(*j != index); + } + } + + } + else if (!i->filtered) + { + if (t != 0) + assert(!t->have_piece(index)); + + const std::vector >& c_vec = pick_piece_info_vector(i->downloading, i->filtered); + assert(i->priority(m_sequenced_download_threshold) < (int)c_vec.size()); + const std::vector& vec = c_vec[i->priority(m_sequenced_download_threshold)]; + if (i->index >= vec.size()) + { + assert(false); + } + assert(vec[i->index] == index); + } + + std::vector::const_iterator down + = std::find_if(m_downloads.begin(), + m_downloads.end(), + has_index(index)); + if (i->downloading == 1) + { + assert(down != m_downloads.end()); + } + else + { + assert(down == m_downloads.end()); + } + } + assert(num_filtered == m_num_filtered); + assert(num_have_filtered == m_num_have_filtered); + } +#endif + + float piece_picker::distributed_copies() const + { + const float num_pieces = static_cast(m_piece_map.size()); + + for (int i = 0; i < (int)m_piece_info.size(); ++i) + { + int p = (int)m_piece_info[i].size(); + assert(num_pieces == 0 || float(p) / num_pieces <= 1.f); + if (p > 0) + { + float fraction_above_count = + 1.f - float(p) / num_pieces; + return i + fraction_above_count; + } + } + return 1.f; + } + + std::vector >& piece_picker::pick_piece_info_vector( + bool downloading, bool filtered) + { + assert(!filtered); + return downloading?m_downloading_piece_info:m_piece_info; + } + + std::vector > const& piece_picker::pick_piece_info_vector( + bool downloading, bool filtered) const + { + assert(!filtered); + return downloading?m_downloading_piece_info:m_piece_info; + } + + void piece_picker::add(int index) + { + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + piece_pos& p = m_piece_map[index]; + assert(!p.filtered); + + std::vector >& dst_vec = pick_piece_info_vector( + p.downloading, p.filtered); + + int priority = p.priority(m_sequenced_download_threshold); + if ((int)dst_vec.size() <= priority) + dst_vec.resize(priority + 1); + + assert((int)dst_vec.size() > priority); + + if (p.ordered(m_sequenced_download_threshold)) + { + // the piece should be inserted ordered, not randomly + std::vector& v = dst_vec[priority]; +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + std::vector::iterator i = std::lower_bound(v.begin(), v.end() + , index/*, std::greater()*/); + p.index = i - v.begin(); + v.insert(i, index); + i = v.begin() + p.index + 1; + for (;i != v.end(); ++i) + { + ++m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + } + else if (dst_vec[priority].size() < 2) + { + p.index = dst_vec[priority].size(); + dst_vec[priority].push_back(index); + } + else + { + // find a random position in the destination vector where we will place + // this entry. + int dst_index = rand() % dst_vec[priority].size(); + + // copy the entry at that position to the back + m_piece_map[dst_vec[priority][dst_index]].index + = dst_vec[priority].size(); + dst_vec[priority].push_back(dst_vec[priority][dst_index]); + + // and then replace the one at dst_index with the one we're moving. + // this procedure is to make sure there's no ordering when pieces + // are moved in sequenced order. + p.index = dst_index; + dst_vec[priority][p.index] = index; + } + } + + // will update the piece with the given properties (downloading, filtered, + // priority, elem_index) to place it at the correct position in the + // vectors. + void piece_picker::move(bool downloading, bool filtered, int priority + , int elem_index) + { + assert(!filtered); + assert(priority >= 0); + assert(elem_index >= 0); + assert(elem_index != piece_pos::we_have_index); + std::vector >& src_vec(pick_piece_info_vector( + downloading, filtered)); + + assert((int)src_vec.size() > priority); + assert((int)src_vec[priority].size() > elem_index); + + int index = src_vec[priority][elem_index]; + // update the piece_map + piece_pos& p = m_piece_map[index]; + int new_priority = p.priority(m_sequenced_download_threshold); + + if (p.downloading == downloading + && p.filtered == filtered + && new_priority == priority) + { + assert(p.ordered(m_sequenced_download_threshold)); + return; + } + + std::vector >& dst_vec(pick_piece_info_vector( + p.downloading, p.filtered)); + + assert(&dst_vec != &src_vec || new_priority != priority); + + if ((int)dst_vec.size() <= new_priority) + { + dst_vec.resize(new_priority + 1); + assert((int)dst_vec.size() > new_priority); + } + + if (p.ordered(m_sequenced_download_threshold)) + { + // the piece should be inserted ordered, not randomly + std::vector& v = dst_vec[new_priority]; +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + std::vector::iterator i = std::lower_bound(v.begin(), v.end() + , index/*, std::greater()*/); + p.index = i - v.begin(); + v.insert(i, index); + i = v.begin() + p.index + 1; + for (;i != v.end(); ++i) + { + ++m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + } + else if (dst_vec[new_priority].size() < 2) + { + p.index = dst_vec[new_priority].size(); + dst_vec[new_priority].push_back(index); + } + else + { + // find a random position in the destination vector where we will place + // this entry. + int dst_index = rand() % dst_vec[new_priority].size(); + + // copy the entry at that position to the back + m_piece_map[dst_vec[new_priority][dst_index]].index + = dst_vec[new_priority].size(); + dst_vec[new_priority].push_back(dst_vec[new_priority][dst_index]); + + // and then replace the one at dst_index with the one we're moving. + // this procedure is to make sure there's no ordering when pieces + // are moved in sequenced order. + p.index = dst_index; + dst_vec[new_priority][p.index] = index; + } + assert(p.index < dst_vec[p.priority(m_sequenced_download_threshold)].size()); + assert(dst_vec[p.priority(m_sequenced_download_threshold)][p.index] == index); + + if (priority >= m_sequenced_download_threshold) + { + // remove the element from the source vector and preserve the order + std::vector& v = src_vec[priority]; + v.erase(v.begin() + elem_index); + for (std::vector::iterator i = v.begin() + elem_index; + i != v.end(); ++i) + { + --m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } + } + else + { + // this will remove elem from the source vector without + // preserving order, but the order is random anyway + int replace_index = src_vec[priority][elem_index] = src_vec[priority].back(); + if (index != replace_index) + { + // update the entry we moved from the back + m_piece_map[replace_index].index = elem_index; + + assert((int)src_vec[priority].size() > elem_index); + // this may not necessarily be the case. If we've just updated the threshold and are updating + // the piece map +// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority); + assert((int)m_piece_map[replace_index].index == elem_index); + assert(src_vec[priority][elem_index] == replace_index); + } + else + { + assert((int)src_vec[priority].size() == elem_index+1); + } + + src_vec[priority].pop_back(); + } + } + + void piece_picker::remove(bool downloading, bool filtered, int priority + , int elem_index) + { + assert(!filtered); + assert(priority >= 0); + assert(elem_index >= 0); + + std::vector >& src_vec(pick_piece_info_vector(downloading, filtered)); + + assert((int)src_vec.size() > priority); + assert((int)src_vec[priority].size() > elem_index); + + int index = src_vec[priority][elem_index]; + + if (downloading) + { + std::vector::iterator i + = std::find_if(m_downloads.begin(), + m_downloads.end(), + has_index(index)); + assert(i != m_downloads.end()); + m_downloads.erase(i); + } + piece_pos& p = m_piece_map[index]; + p.downloading = 0; + if (p.ordered(m_sequenced_download_threshold)) + { + std::vector& v = src_vec[priority]; +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + std::vector::iterator i = v.begin() + elem_index; + v.erase(i); + i = v.begin() + elem_index; + for (; i != v.end(); ++i) + { + --m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + } + else + { + // this will remove elem from the vector without + // preserving order + index = src_vec[priority][elem_index] = src_vec[priority].back(); + // update the entry we moved from the back + if ((int)src_vec[priority].size() > elem_index+1) + m_piece_map[index].index = elem_index; + src_vec[priority].pop_back(); + } + } + + void piece_picker::restore_piece(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + assert(m_piece_map[index].downloading == 1); + + std::vector::iterator i + = std::find_if(m_downloads.begin(), + m_downloads.end(), + has_index(index)); + assert(i != m_downloads.end()); + m_downloads.erase(i); + + m_piece_map[index].downloading = 0; + piece_pos& p = m_piece_map[index]; + if (p.filtered) return; + move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + } + + void piece_picker::inc_refcount(int i) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(i >= 0); + assert(i < (int)m_piece_map.size()); + + int index = m_piece_map[i].index; + int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold); + + assert(m_piece_map[i].peer_count < 2048); + m_piece_map[i].peer_count++; + assert(m_piece_map[i].peer_count != 0); + + piece_pos& p = m_piece_map[i]; + + // if we have the piece or if it's filtered + // we don't have to move any entries in the piece_info vector + if (index == piece_pos::we_have_index || p.filtered + || p.priority(m_sequenced_download_threshold) == prev_priority) return; + + move(p.downloading, p.filtered, prev_priority, index); + +#ifndef NDEBUG +// integrity_check(); +#endif + return; + } + + void piece_picker::dec_refcount(int i) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(i >= 0); + assert(i < (int)m_piece_map.size()); + + int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold); + int index = m_piece_map[i].index; + assert(m_piece_map[i].peer_count > 0); + + if (m_piece_map[i].peer_count > 0) + m_piece_map[i].peer_count--; + + piece_pos& p = m_piece_map[i]; + + if (index == piece_pos::we_have_index || p.filtered + || p.priority(m_sequenced_download_threshold) == prev_priority) return; + + move(p.downloading, p.filtered, prev_priority, 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) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + int info_index = m_piece_map[index].index; + int priority = m_piece_map[index].priority(m_sequenced_download_threshold); + + assert(m_piece_map[index].downloading == 1); + + assert(info_index != piece_pos::we_have_index); + piece_pos& p = m_piece_map[index]; + if (p.filtered) + { + --m_num_filtered; + ++m_num_have_filtered; + return; + } + if (info_index == piece_pos::we_have_index) return; + remove(p.downloading, p.filtered, priority, info_index); + p.index = piece_pos::we_have_index; + } + + + void piece_picker::mark_as_filtered(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + if (p.filtered == 1) return; + p.filtered = 1; + if (p.index != piece_pos::we_have_index) + { + ++m_num_filtered; + remove(p.downloading, false, p.priority(m_sequenced_download_threshold), p.index); + assert(p.filtered == 1); + } + else + { + ++m_num_have_filtered; + } + } + + // this function can be used for pieces that we don't + // have, but have marked as filtered (so we didn't + // want to download them) but later want to enable for + // downloading, then we call this function and it will + // be inserted in the available piece list again + void piece_picker::mark_as_unfiltered(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + if (p.filtered == 0) return; + p.filtered = 0; + if (p.index != piece_pos::we_have_index) + { + --m_num_filtered; + assert(m_num_filtered >= 0); + add(index); + } + else + { + --m_num_have_filtered; + assert(m_num_have_filtered >= 0); + } + } + + bool piece_picker::is_filtered(int index) const + { + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + return m_piece_map[index].filtered == 1; + } + + 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 == 1; + } + } + + void piece_picker::pick_pieces(const std::vector& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer) const + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(num_blocks > 0); + assert(pieces.size() == m_piece_map.size()); + + // free refers to pieces that are free to download, no one else + // is downloading them. + // partial is pieces that are partially being downloaded, and + // parts of them may be free for download as well, the + // partially downloaded pieces will be prioritized + assert(m_piece_info.begin() != m_piece_info.end()); + // +1 is to ignore pieces that no peer has. The bucket with index 0 contains + // pieces that 0 other peers has. + std::vector >::const_iterator free + = m_piece_info.begin() + 1; + assert(m_downloading_piece_info.begin() + != m_downloading_piece_info.end()); + + std::vector >::const_iterator partial + = m_downloading_piece_info.begin() + 1; + + std::vector backup_blocks; + + // this loop will loop from pieces with 1 peer and up + // until we either reach the end of the piece list or + // has filled the interesting_blocks with num_blocks + // blocks. + + // it iterates over two ranges simultaneously. The pieces that are + // partially downloaded or partially requested, and the pieces that + // hasn't been requested at all. The default is to prioritize pieces + // that are partially requested/downloaded, so the loop will first + // look for blocks among those pieces. And it will also take two steps + // in that range when iterating. This has the effect that partial pieces + // doesn't have to be as rare as non-requested pieces in order to be + // prefered. + + // 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. + + while((free != m_piece_info.end()) + || (partial != m_downloading_piece_info.end())) + { + if (partial != m_downloading_piece_info.end()) + { + for (int i = 0; i < 2; ++i) + { + num_blocks = add_interesting_blocks_partial(*partial, pieces + , interesting_blocks, backup_blocks, num_blocks + , prefer_whole_pieces, peer); + assert(num_blocks >= 0); + if (num_blocks == 0) return; + ++partial; + if (partial == m_downloading_piece_info.end()) break; + } + } + + if (free != m_piece_info.end()) + { + num_blocks = add_interesting_blocks_free(*free, pieces + , interesting_blocks, num_blocks, prefer_whole_pieces); + assert(num_blocks >= 0); + if (num_blocks == 0) return; + ++free; + } + } + + if (!prefer_whole_pieces) return; + assert(num_blocks > 0); + +#ifdef TORRENT_VERBOSE_LOGGING +// std::ofstream f("piece_picker.log", std::ios_base::app); +// f << "backup_blocks: " << backup_blocks.size() << "\n" +// << "used: " << std::min(num_blocks, (int)backup_blocks.size()) << "\n----\n"; +#endif + + interesting_blocks.insert(interesting_blocks.end() + , backup_blocks.begin(), backup_blocks.begin() + + (std::min)(num_blocks, (int)backup_blocks.size())); + } + + namespace + { + bool exclusively_requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, tcp::endpoint peer) + { + for (int j = 0; j < num_blocks_in_piece; ++j) + { + if ((p.finished_blocks[j] == 1 + || p.requested_blocks[j] == 1) + && p.info[j].peer != peer + && p.info[j].peer != tcp::endpoint()) + { + return false; + } + } + return true; + } + } + + int piece_picker::add_interesting_blocks_free(std::vector const& piece_list + , std::vector const& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces) const + { + for (std::vector::const_iterator i = piece_list.begin(); + i != piece_list.end(); ++i) + { + assert(*i >= 0); + assert(*i < (int)m_piece_map.size()); + assert(m_piece_map[*i].downloading == 0); + + // if the peer doesn't have the piece + // skip it + if (!pieces[*i]) continue; + + int piece_blocks = blocks_in_piece(*i); + if (!prefer_whole_pieces && piece_blocks > num_blocks) + piece_blocks = num_blocks; + for (int j = 0; j < piece_blocks; ++j) + { + interesting_blocks.push_back(piece_block(*i, j)); + } + num_blocks -= (std::min)(piece_blocks, num_blocks); + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } + return num_blocks; + } + + int piece_picker::add_interesting_blocks_partial(std::vector const& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer) const + { + assert(num_blocks > 0); + + for (std::vector::const_iterator i = piece_list.begin(); + i != piece_list.end(); ++i) + { + assert(*i >= 0); + assert(*i < (int)m_piece_map.size()); + // if the peer doesn't have the piece + // skip it + if (!pieces[*i]) continue; + + assert(m_piece_map[*i].downloading == 1); + + // calculate the number of blocks in this + // piece. It's always m_blocks_per_piece, except + // in the last piece. + int num_blocks_in_piece = blocks_in_piece(*i); + + std::vector::const_iterator p + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); + assert(p != m_downloads.end()); + + // this means that this partial piece has + // been downloaded/requested partially from + // another peer that isn't us. And since + // we prefer whole pieces, add this piece's + // blocks to the backup list. If the prioritized + // blocks aren't enough, blocks from this list + // will be picked. + if (prefer_whole_pieces + && !exclusively_requested_from(*p, num_blocks_in_piece, peer)) + { + if ((int)backup_blocks.size() >= num_blocks) continue; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + if (p->finished_blocks[j] == 1) continue; + if (p->requested_blocks[j] == 1 + && p->info[j].peer == peer) continue; + backup_blocks.push_back(piece_block(*i, j)); + } + continue; + } + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + if (p->finished_blocks[j] == 1) continue; + if (p->requested_blocks[j] == 1 + && p->info[j].peer == peer) continue; + // this block is interesting (we don't have it + // yet). But it may already have been requested + // from another peer. We have to add it anyway + // to allow the requester to determine if the + // block should be requested from more than one + // peer. If it is being downloaded, we continue + // to look for blocks until we have num_blocks + // blocks that have not been requested from any + // other peer. + interesting_blocks.push_back(piece_block(*i, j)); + if (p->requested_blocks[j] == 0) + { + // we have found a block that's free to download + num_blocks--; + if (prefer_whole_pieces) continue; + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } + } + assert(num_blocks >= 0 || prefer_whole_pieces); + if (num_blocks < 0) num_blocks = 0; + if (num_blocks == 0) return num_blocks; + } + return num_blocks; + } + + bool piece_picker::is_piece_finished(int index) const + { + assert(index < (int)m_piece_map.size()); + assert(index >= 0); + + if (m_piece_map[index].downloading == 0) return false; + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + assert(i != m_downloads.end()); + assert((int)i->finished_blocks.count() <= m_blocks_per_piece); + int max_blocks = blocks_in_piece(index); + if ((int)i->finished_blocks.count() != max_blocks) return false; + + assert((int)i->requested_blocks.count() == max_blocks); + return true; + } + + bool piece_picker::is_downloading(piece_block block) const + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < (int)max_blocks_per_piece); + + 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)); + + assert(i != m_downloads.end()); + return i->requested_blocks[block.block_index]; + } + + bool piece_picker::is_finished(piece_block block) const + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < (int)max_blocks_per_piece); + + 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)); + assert(i != m_downloads.end()); + return i->finished_blocks[block.block_index]; + } + + + void piece_picker::mark_as_downloading(piece_block block, const tcp::endpoint& peer) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { + p.downloading = 1; + move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + + downloading_piece dp; + dp.index = block.piece_index; + dp.requested_blocks[block.block_index] = 1; + dp.info[block.block_index].peer = peer; + m_downloads.push_back(dp); + } + else + { + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + assert(i->requested_blocks[block.block_index] == 0); + i->info[block.block_index].peer = peer; + i->requested_blocks[block.block_index] = 1; + } + } + + void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.index == piece_pos::we_have_index || p.filtered) return; + + if (p.downloading == 0) + { + p.downloading = 1; + move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + + downloading_piece dp; + dp.index = block.piece_index; + dp.requested_blocks[block.block_index] = 1; + dp.finished_blocks[block.block_index] = 1; + dp.info[block.block_index].peer = peer; + m_downloads.push_back(dp); + } + else + { + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + i->info[block.block_index].peer = peer; + i->requested_blocks[block.block_index] = 1; + i->finished_blocks[block.block_index] = 1; + } + } +/* + void piece_picker::mark_as_finished(piece_block block, const peer_id& peer) + { +#ifndef NDEBUG + integrity_check(); +#endif + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + assert(m_piece_map[block.piece_index].downloading == 1); + + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + i->finished_blocks[block.block_index] = 1; + // the block may have been requested, then cancled + // and requested by a peer that disconnects + // that way we can actually receive the piece + // without the requested bit is set. + i->requested_blocks[block.block_index] = 1; + i->info[block.block_index].num_downloads++; + i->info[block.block_index].peer = peer; +#ifndef NDEBUG + integrity_check(); +#endif + } +*/ + void piece_picker::get_downloaders(std::vector& d, int index) const + { + 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)); + assert(i != m_downloads.end()); + + d.clear(); + for (int j = 0; j < blocks_in_piece(index); ++j) + { + d.push_back(i->info[j].peer); + } + } + + boost::optional 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 boost::optional(); + + assert(block.block_index < max_blocks_per_piece); + assert(block.block_index >= 0); + + if (i->requested_blocks[block.block_index] == false + || i->finished_blocks[block.block_index] == true) + return boost::optional(); + + return boost::optional(i->info[block.block_index].peer); + } + + void piece_picker::abort_download(piece_block block) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + if (m_piece_map[block.piece_index].downloading == 0) + { + 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)); + assert(i != m_downloads.end()); + + if (i->finished_blocks[block.block_index]) return; + + assert(block.block_index < blocks_in_piece(block.piece_index)); +#ifndef NDEBUG + if (i->requested_blocks[block.block_index] != 1) + { + assert(false); + } +#endif + + // clear this block as being downloaded + i->requested_blocks[block.block_index] = 0; + + // if there are no other blocks in this pieces + // that's being downloaded, remove it from the list + if (i->requested_blocks.count() == 0) + { + m_downloads.erase(i); + m_piece_map[block.piece_index].downloading = 0; + piece_pos& p = m_piece_map[block.piece_index]; + move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + } + } + + 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_blocks.count(); + } + return counter; + } + +} + diff --git a/library/policy.cpp b/library/policy.cpp new file mode 100755 index 000000000..bdd90ac77 --- /dev/null +++ b/library/policy.cpp @@ -0,0 +1,1419 @@ +/* + +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/peer_connection.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#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/aux_/session_impl.hpp" + +namespace libtorrent +{ + class peer_connection; +} + +using namespace boost::posix_time; +using boost::bind; + +namespace +{ + using namespace libtorrent; + + // 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 + , std::vector ignore = std::vector()) + { + int num_requests = c.desired_queue_size() + - (int)c.download_queue().size() + - (int)c.request_queue().size(); + + // 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); + + // 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. + bool prefer_whole_pieces = c.statistics().download_payload_rate() + * t.settings().whole_pieces_threshold + > t.torrent_file().piece_length(); + + // 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. +#ifndef NDEBUG + assert(c.remote() == c.get_socket()->remote_endpoint()); +#endif + p.pick_pieces(c.get_bitfield(), interesting_pieces + , num_requests, prefer_whole_pieces, c.remote()); + + // 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(10); + + for (std::vector::iterator i = interesting_pieces.begin(); + i != interesting_pieces.end(); ++i) + { + if (p.is_downloading(*i)) + { + busy_pieces.push_back(*i); + continue; + } + + // ok, we found a piece that's not being downloaded + // by somebody else. request it from this peer + // and return + c.add_request(*i); + num_requests--; + } + + c.send_block_requests(); + + // in this case, we could not find any blocks + // that was free. If we couldn't find any busy + // blocks as well, we cannot download anything + // more from this peer. + + if (busy_pieces.empty()) return; + + // first look for blocks that are just queued + // and not actually sent to us yet + // (then we can cancel those and request them + // from this peer instead) + + while (num_requests > 0) + { + peer_connection* peer = 0; + + const int initial_queue_size = (int)c.download_queue().size() + + (int)c.request_queue().size(); + + // This peer's weight will be the minimum, to prevent + // cancelling requests from a faster peer. + float min_weight = initial_queue_size == 0 + ? std::numeric_limits::max() + : c.statistics().download_payload_rate() / initial_queue_size; + + // find the peer with the lowest download + // speed that also has a piece that this + // peer could send us + for (torrent::peer_iterator i = t.begin(); + i != t.end(); ++i) + { + // don't try to take over blocks from ourself + if (i->second == &c) + continue; + + // ignore all peers in the ignore list + if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end()) + continue; + + const std::deque& download_queue = i->second->download_queue(); + const std::deque& request_queue = i->second->request_queue(); + const int queue_size = (int)i->second->download_queue().size() + + (int)i->second->request_queue().size(); + const float weight = queue_size == 0 + ? std::numeric_limits::max() + : i->second->statistics().download_payload_rate() / queue_size; + + // if the peer's (i) weight is less than the lowest we've found so + // far (weight == priority) and it has blocks in its request- + // or download queue that we could request from this peer (c), + // replace the currently lowest ranking peer. + if (weight < min_weight + && (std::find_first_of( + busy_pieces.begin() + , busy_pieces.end() + , request_queue.begin() + , request_queue.end()) != busy_pieces.end() + || std::find_first_of( + busy_pieces.begin() + , busy_pieces.end() + , download_queue.begin() + , download_queue.end()) != busy_pieces.end())) + { + peer = i->second; + min_weight = weight; + } + } + + if (peer == 0) + { + // we probably couldn't request the block because + // we are ignoring some peers + break; + } + + // find a suitable block to take over from this peer + + std::deque::const_reverse_iterator common_block = + std::find_first_of( + peer->request_queue().rbegin() + , peer->request_queue().rend() + , busy_pieces.begin() + , busy_pieces.end()); + + if (common_block == peer->request_queue().rend()) + { + common_block = std::find_first_of( + peer->download_queue().rbegin() + , peer->download_queue().rend() + , busy_pieces.begin() + , busy_pieces.end()); + assert(common_block != peer->download_queue().rend()); + } + + piece_block block = *common_block; + peer->cancel_request(block); + c.add_request(block); + + // the one we interrupted may need to request a new piece. + // make sure it doesn't take over a block from the peer + // that just took over its block + ignore.push_back(&c); + request_a_block(t, *peer, ignore); + num_requests--; + + const int queue_size = (int)c.download_queue().size() + + (int)c.request_queue().size(); + const float weight = queue_size == 0 + ? std::numeric_limits::max() + : c.statistics().download_payload_rate() / queue_size; + + // this peer doesn't have a faster connection than the + // slowest peer. Don't take over any blocks + if (weight <= min_weight) break; + } + c.send_block_requests(); + } + + + 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->second->share_diff(); + assert(diff < std::numeric_limits::max()); + if (i->second->is_peer_interested() || diff <= 0) + continue; + + assert(diff > 0); + i->second->add_free_upload(-diff); + accumulator += diff; + assert(accumulator > 0); + } + 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->second->share_diff(); + assert(d < std::numeric_limits::max()); + total_diff += d; + if (!i->second->is_peer_interested() || i->second->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->second; + if (!p->is_peer_interested() || p->share_diff() >= 0) continue; + p->add_free_upload(upload_share); + free_upload -= upload_share; + } + return free_upload; + } + + struct match_peer_ip + { + match_peer_ip(const tcp::endpoint& ip) + : m_ip(ip) + {} + + bool operator()(const policy::peer& p) const + { return p.ip.address() == m_ip.address(); } + + tcp::endpoint m_ip; + }; + + struct match_peer_connection + { + match_peer_connection(const peer_connection& c) + : m_conn(c) + {} + + bool operator()(const policy::peer& p) const + { return p.connection == &m_conn; } + + const peer_connection& m_conn; + }; + + +} + +namespace libtorrent +{ + policy::policy(torrent* t) + : m_torrent(t) +// , m_max_uploads(std::numeric_limits::max()) +// , m_max_connections(std::numeric_limits::max()) + , m_num_unchoked(0) + , m_available_free_upload(0) + , m_last_optimistic_disconnect(boost::gregorian::date(1970,boost::gregorian::Jan,1)) + { assert(t); } + // finds the peer that has the worst download rate + // and returns it. May return 0 if all peers are + // choked. + policy::peer* policy::find_choke_candidate() + { + INVARIANT_CHECK; + + peer* worst_peer = 0; + size_type min_weight = std::numeric_limits::min(); + +#ifndef NDEBUG + int unchoked_counter = m_num_unchoked; +#endif + + // TODO: make this selection better + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + + if (c == 0) continue; + if (c->is_choked()) continue; +#ifndef NDEBUG + unchoked_counter--; +#endif + if (c->is_disconnecting()) continue; + // if the peer isn't interested, just choke it + if (!c->is_peer_interested()) + return &(*i); + + size_type diff = i->total_download() + - i->total_upload(); + + size_type weight = static_cast(c->statistics().download_rate() * 10.f) + + diff + + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; + + if (weight >= min_weight && worst_peer) continue; + + min_weight = weight; + worst_peer = &(*i); + continue; + } + assert(unchoked_counter == 0); + return worst_peer; + } + + policy::peer* policy::find_unchoke_candidate() + { + INVARIANT_CHECK; + + // if all of our peers are unchoked, there's + // no left to unchoke + if (m_num_unchoked == m_torrent->num_peers()) + return 0; + + using namespace boost::posix_time; + using namespace boost::gregorian; + + peer* unchoke_peer = 0; + ptime min_time(date(9999,Jan,1)); + float max_down_speed = 0.f; + + // TODO: make this selection better + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + if (c->is_disconnecting()) continue; + if (!c->is_choked()) continue; + if (!c->is_peer_interested()) continue; + if (c->share_diff() < -free_upload_amount + && m_torrent->ratio() != 0) continue; + if (c->statistics().download_rate() < max_down_speed) continue; +// if (i->last_optimistically_unchoked > min_time) continue; + + min_time = i->last_optimistically_unchoked; + max_down_speed = c->statistics().download_rate(); + unchoke_peer = &(*i); + } + return unchoke_peer; + } + + policy::peer* policy::find_disconnect_candidate() + { + peer *disconnect_peer = 0; + double slowest_transfer_rate = std::numeric_limits::max(); + + boost::posix_time::ptime local_time + = second_clock::universal_time(); + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if(c == 0) + continue; + if(c->is_disconnecting()) + continue; + + double transferred_amount + = (double)c->statistics().total_payload_download(); + + boost::posix_time::time_duration connected_time + = local_time - i->connected; + + double connected_time_in_seconds + = connected_time.seconds() + + connected_time.minutes()*60.0 + + connected_time.hours()*60.0*60.0; + + double transfer_rate + = transferred_amount / (connected_time_in_seconds+1); + + if (transfer_rate <= slowest_transfer_rate) + { + slowest_transfer_rate = transfer_rate; + disconnect_peer = &(*i); + } + } + return disconnect_peer; + } + + policy::peer *policy::find_connect_candidate() + { + boost::posix_time::ptime local_time=second_clock::universal_time(); + boost::posix_time::ptime ptime(local_time); + policy::peer* candidate =0; + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if(i->connection) continue; + if(i->banned) continue; + if(i->type == peer::not_connectable) continue; + + assert(i->connected <= local_time); + + boost::posix_time::ptime next_connect = i->connected; + + if (next_connect <= ptime) + { + ptime = next_connect; + candidate = &(*i); + } + } + + assert(ptime <= local_time); + + return candidate; + } + + policy::peer* policy::find_seed_choke_candidate() + { + INVARIANT_CHECK; + + assert(m_num_unchoked > 0); + // first choice candidate. + // it is a candidate we owe nothing to and which has been unchoked + // the longest. + using namespace boost::posix_time; + using namespace boost::gregorian; + + peer* candidate = 0; + + // not valid when candidate == 0 + ptime last_unchoke = ptime(date(1970, Jan, 1)); + + // second choice candidate. + // if there is no first choice candidate, this candidate will be chosen. + // it is the candidate that we owe the least to. + peer* second_candidate = 0; + size_type lowest_share_diff = 0; // not valid when secondCandidate==0 + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + // ignore peers that are choked or + // whose connection is closed + if (c == 0) continue; + + if (c->is_choked()) continue; + if (c->is_disconnecting()) continue; + + size_type share_diff = c->share_diff(); + + // select as second candidate the one that we owe the least + // to + if (!second_candidate || share_diff <= lowest_share_diff) + { + lowest_share_diff = share_diff; + second_candidate = &(*i); + } + + // select as first candidate the one that we don't owe anything to + // and has been waiting for an unchoke the longest + if (share_diff > 0) continue; + if (!candidate || last_unchoke > i->last_optimistically_unchoked) + { + last_unchoke = i->last_optimistically_unchoked; + candidate = &(*i); + } + } + if (candidate) return candidate; + if (second_candidate) return second_candidate; + assert(false); + return 0; + } + + policy::peer* policy::find_seed_unchoke_candidate() + { + INVARIANT_CHECK; + + peer* candidate = 0; + boost::posix_time::ptime last_unchoke + = second_clock::universal_time(); + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + if (!c->is_choked()) continue; + if (!c->is_peer_interested()) continue; + if (c->is_disconnecting()) continue; + if (last_unchoke < i->last_optimistically_unchoked) continue; + last_unchoke = i->last_optimistically_unchoked; + candidate = &(*i); + } + return candidate; + } + + bool policy::seed_unchoke_one_peer() + { + INVARIANT_CHECK; + + peer* p = find_seed_unchoke_candidate(); + if (p != 0) + { + assert(p->connection->is_choked()); + p->connection->send_unchoke(); + p->last_optimistically_unchoked + = second_clock::universal_time(); + ++m_num_unchoked; + } + return p != 0; + } + + void policy::seed_choke_one_peer() + { + INVARIANT_CHECK; + + peer* p = find_seed_choke_candidate(); + if (p != 0) + { + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } + } + + void policy::pulse() + { + INVARIANT_CHECK; + + if (m_torrent->is_paused()) return; + + using namespace boost::posix_time; + + // TODO: we must also remove peers that + // we failed to connect to from this list + // to avoid being part of a DDOS-attack + + // remove old disconnected peers from the list + m_peers.erase( + std::remove_if(m_peers.begin() + , m_peers.end() + , old_disconnected_peer()) + , m_peers.end()); + + // ------------------------------------- + // maintain the number of connections + // ------------------------------------- + + // count the number of connected peers except for peers + // that are currently in the process of disconnecting + int num_connected_peers = 0; + + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (i->connection && !i->connection->is_disconnecting()) + ++num_connected_peers; + } + + if (m_torrent->m_connections_quota.given != std::numeric_limits::max()) + { + + int max_connections = m_torrent->m_connections_quota.given; + + if (num_connected_peers >= max_connections) + { + // every minute, disconnect the worst peer in hope of finding a better peer + + boost::posix_time::ptime local_time = second_clock::universal_time(); + if (m_last_optimistic_disconnect + boost::posix_time::seconds(120) <= local_time) + { + m_last_optimistic_disconnect = local_time; + --max_connections; // this will have the effect of disconnecting the worst peer + } + } + else + { + // don't do a disconnect earlier than 1 minute after some peer was connected + m_last_optimistic_disconnect = second_clock::universal_time(); + } + + while (num_connected_peers > max_connections) + { + bool ret = disconnect_one_peer(); + (void)ret; + assert(ret); + --num_connected_peers; + } + } + + while (m_torrent->num_peers() < m_torrent->m_connections_quota.given) + { + if (!connect_one_peer()) + break; + } + + + // ------------------------ + // 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 (m_torrent->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( + m_torrent->begin() + , m_torrent->end()); + + // distribute the free upload among the peers + m_available_free_upload = distribute_free_upload( + m_torrent->begin() + , m_torrent->end() + , m_available_free_upload); + } + + // ------------------------ + // seed choking policy + // ------------------------ + if (m_torrent->is_seed()) + { + if (m_num_unchoked > m_torrent->m_uploads_quota.given) + { + do + { + peer* p = find_seed_choke_candidate(); + --m_num_unchoked; + assert(p != 0); + if (p == 0) break; + + assert(!p->connection->is_choked()); + p->connection->send_choke(); + } while (m_num_unchoked > m_torrent->m_uploads_quota.given); + } + else if (m_num_unchoked > 0) + { + // optimistic unchoke. trade the 'worst' + // unchoked peer with one of the choked + // TODO: This rotation should happen + // far less frequent than this! + assert(m_num_unchoked <= m_torrent->num_peers()); + peer* p = find_seed_unchoke_candidate(); + if (p) + { + assert(p->connection->is_choked()); + seed_choke_one_peer(); + p->connection->send_unchoke(); + ++m_num_unchoked; + } + + } + + // make sure we have enough + // unchoked peers + while (m_num_unchoked < m_torrent->m_uploads_quota.given) + { + if (!seed_unchoke_one_peer()) break; + } +#ifndef NDEBUG + check_invariant(); +#endif + } + + // ---------------------------- + // downloading choking policy + // ---------------------------- + else + { + if (m_torrent->ratio() != 0) + { + // choke peers that have leeched too much without giving anything back + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + + size_type diff = i->connection->share_diff(); + if (diff < -free_upload_amount + && !c->is_choked()) + { + // if we have uploaded more than a piece for free, choke peer and + // wait until we catch up with our download. + c->send_choke(); + --m_num_unchoked; + } + } + } + + if (m_torrent->m_uploads_quota.given < m_torrent->num_peers()) + { + assert(m_torrent->m_uploads_quota.given >= 0); + + // make sure we don't have too many + // unchoked peers + if (m_num_unchoked > m_torrent->m_uploads_quota.given) + { + do + { + peer* p = find_choke_candidate(); + if (!p) break; + assert(p); + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } while (m_num_unchoked > m_torrent->m_uploads_quota.given); + } + else + { + // optimistic unchoke. trade the 'worst' + // unchoked peer with one of the choked + // TODO: This rotation should happen + // far less frequent than this! + assert(m_num_unchoked <= m_torrent->num_peers()); + peer* p = find_unchoke_candidate(); + if (p) + { + assert(p->connection->is_choked()); + choke_one_peer(); + p->connection->send_unchoke(); + ++m_num_unchoked; + } + } + } + + // make sure we have enough + // unchoked peers + while (m_num_unchoked < m_torrent->m_uploads_quota.given + && unchoke_one_peer()); + } + } + + void policy::ban_peer(const peer_connection& c) + { + INVARIANT_CHECK; + + std::vector::iterator i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)); + + if (i == m_peers.end()) + { + // this is probably an http seed + if (web_peer_connection const* p = dynamic_cast(&c)) + { + m_torrent->remove_url_seed(p->url()); + } + return; + } + + i->type = peer::not_connectable; + i->ip.port(0); + i->banned = true; + } + + void policy::new_connection(peer_connection& c) + { + assert(!c.is_local()); +/* +#ifndef NDEBUG + // avoid the invariant check to fail + peer p(tcp::endpoint("0.0.0.0", 0), peer::not_connectable); + p.connection = &c; + m_peers.push_back(p); +#endif +*/ + INVARIANT_CHECK; +/* +#ifndef NDEBUG + // avoid the invariant check to fail + m_peers.erase(m_peers.end() - 1); +#endif +*/ + // 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 +#ifndef NDEBUG + assert(c.remote() == c.get_socket()->remote_endpoint()); +#endif + if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given + && c.remote().address() != m_torrent->current_tracker().address()) + { + throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect + } + +#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 + + std::vector::iterator i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_ip(c.remote())); + + + if (i != m_peers.end()) + { + if (i->banned) + throw protocol_error("ip address banned, closing"); + + if (i->connection != 0) + { + // the new connection is a local (outgoing) connection + // or the current one is already connected + if (!i->connection->is_connecting() || c.is_local()) + { + throw protocol_error("duplicate connection, closing"); + } + 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(); + i->connection = 0; + } + } + } + else + { + using namespace boost::posix_time; + using namespace boost::gregorian; + + // we don't have ny info about this peer. + // add a new entry +#ifndef NDEBUG + assert(c.remote() == c.get_socket()->remote_endpoint()); +#endif + peer p(c.remote(), peer::not_connectable); + m_peers.push_back(p); + i = m_peers.end()-1; + } + + assert(i->connection == 0); + c.add_stat(i->prev_amount_download, i->prev_amount_upload); + i->prev_amount_download = 0; + i->prev_amount_upload = 0; + i->connection = &c; + assert(i->connection); + i->connected = second_clock::universal_time(); + m_last_optimistic_disconnect = second_clock::universal_time(); + } + + void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid) + { + INVARIANT_CHECK; + + // just ignore the obviously invalid entries from the tracker + if(remote.address() == address() || remote.port() == 0) + return; + + try + { + std::vector::iterator i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_ip(remote)); + + bool just_added = false; + + if (i == m_peers.end()) + { + using namespace boost::posix_time; + using namespace boost::gregorian; + + // we don't have any info about this peer. + // add a new entry + peer p(remote, peer::connectable); + m_peers.push_back(p); + // the iterator is invalid + // because of the push_back() + i = m_peers.end() - 1; + just_added = true; + } + else + { + i->type = peer::connectable; + + // in case we got the ip from a remote connection, port is + // not known, so save it. Client may also have changed port + // for some reason. + i->ip = remote; + + if (i->connection) + { + // this means we're already connected + // to this peer. don't connect to + // it again. + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":" + + boost::lexical_cast(remote.port())); +#endif + + assert(i->connection->associated_torrent().lock().get() == m_torrent); + return; + } + } + + if (i->banned) return; + + if (m_torrent->num_peers() < m_torrent->m_connections_quota.given + && !m_torrent->is_paused()) + { + if (!connect_peer(&*i) && just_added) + { + // if this peer was just added, and it + // failed to connect. Remove it from the list + // (to keep it in sync with the session's list) + assert(i == m_peers.end() - 1); + m_peers.erase(i); + } + } + return; + } + catch(std::exception& e) + { + if (m_torrent->alerts().should_post(alert::debug)) + { + m_torrent->alerts().post_alert( + peer_error_alert(remote, pid, e.what())); + } + } + } + + // this is called when we are choked by a peer + // i.e. a peer lets us know that we will not receive + // anything for a while + void policy::choked(peer_connection&) + { + } + + void policy::piece_finished(int index, bool successfully_verified) + { + INVARIANT_CHECK; + + assert(index >= 0 && index < m_torrent->torrent_file().num_pieces()); + + if (successfully_verified) + { + // have all peers update their interested-flag + for (std::vector::iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (i->connection == 0) continue; + // if we're not interested, we will not become interested + if (!i->connection->is_interesting()) continue; + if (!i->connection->has_piece(index)) continue; + + bool interested = false; + const std::vector& peer_has = i->connection->get_bitfield(); + const std::vector& we_have = m_torrent->pieces(); + assert(we_have.size() == peer_has.size()); + for (int j = 0; j != (int)we_have.size(); ++j) + { + if (!we_have[j] && peer_has[j]) + { + interested = true; + break; + } + } + if (!interested) + i->connection->send_not_interested(); + assert(i->connection->is_interesting() == interested); + } + } + } + + // TODO: we must be able to get interested + // in a peer again, if a piece fails that + // this peer has. + void policy::block_finished(peer_connection& c, piece_block) + { + INVARIANT_CHECK; + + // if the peer hasn't choked us, ask for another piece + if (!c.has_peer_choked()) + request_a_block(*m_torrent, c); + } + + // this is called when we are unchoked by a peer + // i.e. a peer lets us know that we will receive + // data from now on + void policy::unchoked(peer_connection& c) + { + INVARIANT_CHECK; + if (c.is_interesting()) + { + request_a_block(*m_torrent, c); + } + } + + // called when a peer is interested in us + void policy::interested(peer_connection& c) + { + INVARIANT_CHECK; + + assert(std::find_if(m_peers.begin(), m_peers.end() + , boost::bind(std::equal_to(), bind(&peer::connection, _1) + , &c)) != m_peers.end()); + + // 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. + if (c.is_choked() + && m_num_unchoked < m_torrent->m_uploads_quota.given + && (m_torrent->ratio() == 0 + || c.share_diff() >= -free_upload_amount + || m_torrent->is_seed())) + { + c.send_unchoke(); + ++m_num_unchoked; + } + } + + // called when a peer is no longer interested in us + void policy::not_interested(peer_connection& c) + { + INVARIANT_CHECK; + + if (m_torrent->ratio() != 0.f) + { + assert(c.share_diff() < std::numeric_limits::max()); + size_type diff = c.share_diff(); + if (diff > 0 && c.is_seed()) + { + // the peer is a seed and has sent + // us more than we have sent it back. + // consider the download as free download + m_available_free_upload += diff; + c.add_free_upload(-diff); + } + } + if (!c.is_choked()) + { + c.send_choke(); + --m_num_unchoked; + + if (m_torrent->is_seed()) seed_unchoke_one_peer(); + else unchoke_one_peer(); + } + } + + bool policy::unchoke_one_peer() + { + peer* p = find_unchoke_candidate(); + if (p == 0) return false; + assert(p->connection); + assert(!p->connection->is_disconnecting()); + + assert(p->connection->is_choked()); + p->connection->send_unchoke(); + p->last_optimistically_unchoked = second_clock::universal_time(); + ++m_num_unchoked; + return true; + } + + void policy::choke_one_peer() + { + peer* p = find_choke_candidate(); + if (p == 0) return; + assert(p->connection); + assert(!p->connection->is_disconnecting()); + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } + + bool policy::connect_one_peer() + { + if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given) + return false; + peer* p = find_connect_candidate(); + if (p == 0) return false; + assert(!p->banned); + assert(!p->connection); + assert(p->type == peer::connectable); + + return connect_peer(p); + } + + bool policy::connect_peer(peer *p) + { + INVARIANT_CHECK; + try + { + assert(!p->connection); + p->connection = &m_torrent->connect_to_peer(p->ip); + assert(p->connection); + p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); + p->prev_amount_download = 0; + p->prev_amount_upload = 0; + p->connected = + m_last_optimistic_disconnect = + second_clock::universal_time(); + return true; + } + catch (std::exception& e) + {} + return false; + } + + bool policy::disconnect_one_peer() + { + peer *p = find_disconnect_candidate(); + if(!p) + return false; +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; +#endif + + p->connection->disconnect(); + return true; + } + + // this is called whenever a peer connection is closed + void policy::connection_closed(const peer_connection& c) try + { + INVARIANT_CHECK; + +// assert(c.is_disconnecting()); + bool unchoked = false; + + std::vector::iterator i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)); + + // if we couldn't find the connection in our list, just ignore it. + if (i == m_peers.end()) return; + assert(i->connection == &c); + + i->connected = second_clock::universal_time(); + if (!i->connection->is_choked() && !m_torrent->is_aborted()) + { + unchoked = true; + } + + if (c.failed()) + { + i->type = peer::not_connectable; + i->ip.port(0); + } + + // if the share ratio is 0 (infinite), the + // m_available_free_upload isn't used, + // because it isn't necessary. + if (m_torrent->ratio() != 0.f) + { + assert(i->connection->associated_torrent().lock().get() == m_torrent); + assert(i->connection->share_diff() < std::numeric_limits::max()); + m_available_free_upload += i->connection->share_diff(); + } + i->prev_amount_download += c.statistics().total_payload_download(); + i->prev_amount_upload += c.statistics().total_payload_upload(); + i->connection = 0; + + if (unchoked) + { + // if the peer that is diconnecting is unchoked + // then unchoke another peer in order to maintain + // the total number of unchoked peers + --m_num_unchoked; + if (m_torrent->is_seed()) seed_unchoke_one_peer(); + else unchoke_one_peer(); + } + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::string err = e.what(); +#endif + assert(false); + } + + void policy::peer_is_interesting(peer_connection& c) + { + INVARIANT_CHECK; + + c.send_interested(); + if (c.has_peer_choked()) return; + request_a_block(*m_torrent, c); + } + +#ifndef NDEBUG + bool policy::has_connection(const peer_connection* c) + { + assert(c); +#ifndef NDEBUG + assert(c->remote() == c->get_socket()->remote_endpoint()); +#endif + return std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_ip(c->remote())) != m_peers.end(); + } + + void policy::check_invariant() const + { + if (m_torrent->is_aborted()) return; + int actual_unchoked = 0; + int connected_peers = 0; + + int total_connections = 0; + int nonempty_connections = 0; + + + for (std::vector::const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + ++total_connections; + if (!i->connection) continue; + ++nonempty_connections; + if (!i->connection->is_disconnecting()) + ++connected_peers; + if (!i->connection->is_choked()) ++actual_unchoked; + } +// assert(actual_unchoked <= m_torrent->m_uploads_quota.given); + assert(actual_unchoked == m_num_unchoked); + + int num_torrent_peers = 0; + for (torrent::const_peer_iterator i = m_torrent->begin(); + i != m_torrent->end(); ++i) + { + if (i->second->is_disconnecting()) continue; + // ignore web_peer_connections since they are not managed + // by the policy class + if (dynamic_cast(i->second)) continue; + ++num_torrent_peers; + } + + // 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. + + 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)); + + // TODO: Make sure the number of peers in m_torrent is equal + // to the number of connected peers in m_peers. + } +#endif + + policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t) + : ip(ip_) + , type(t) + , last_optimistically_unchoked( + boost::gregorian::date(1970,boost::gregorian::Jan,1)) + , connected(boost::gregorian::date(1970,boost::gregorian::Jan,1)) + , prev_amount_upload(0) + , prev_amount_download(0) + , banned(false) + , connection(0) + { + assert(connected < second_clock::universal_time()); + } + + size_type policy::peer::total_download() const + { + if (connection != 0) + { + 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) + { + assert(prev_amount_upload == 0); + return connection->statistics().total_payload_upload(); + } + else + { + return prev_amount_upload; + } + } +} + diff --git a/library/preset.txt b/library/preset.txt new file mode 100644 index 000000000..e69de29bb diff --git a/library/python-libtorrent.cpp b/library/python-libtorrent.cpp new file mode 100755 index 000000000..3d38556a9 --- /dev/null +++ b/library/python-libtorrent.cpp @@ -0,0 +1,1103 @@ +/* + * Copyright © 2006 Alon Zakai ('Kripken') + * + * 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, 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. + * + * Thank You: Some code portions were derived from BSD-licensed work by + * Arvid Norberg, and GPL-licensed work by Christophe Dumez + */ + + +//------------------ +// TODO: +// +// The DHT capability requires UDP. We need to check that this port is in fact +// open, just like the normal TCP port for bittorrent. +// +// Wait for answers about what to do with the router.*torrent.com's... +// +//------------------ + + +//----------------- +// INCLUDES +//----------------- + +#include + +#include +#include + +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/ip_filter.hpp" + +using namespace libtorrent; + + +//----------------- +// CONSTANTS +//----------------- + +#ifdef AMD64 +#define python_long int +#else +#define python_long long +#endif + +#define EVENT_NULL 0 +#define EVENT_FINISHED 1 +#define EVENT_PEER_ERROR 2 +#define EVENT_INVALID_REQUEST 3 +#define EVENT_FILE_ERROR 4 +#define EVENT_HASH_FAILED_ERROR 5 +#define EVENT_PEER_BAN_ERROR 6 +#define EVENT_FASTRESUME_REJECTED_ERROR 8 +#define EVENT_TRACKER 9 +#define EVENT_OTHER 10 + +#define STATE_QUEUED 0 +#define STATE_CHECKING 1 +#define STATE_CONNECTING 2 +#define STATE_DOWNLOADING_META 3 +#define STATE_DOWNLOADING 4 +#define STATE_FINISHED 5 +#define STATE_SEEDING 6 +#define STATE_ALLOCATING 7 + +#define DHT_ROUTER_PORT 6881 + +#define ERROR_INVALID_ENCODING -10 +#define ERROR_FILESYSTEM -20 +#define ERROR_DUPLICATE_TORRENT -30 +#define ERROR_INVALID_TORRENT -40 + + +//----------------- +// TYPES +//----------------- + +typedef long unique_ID_t; +typedef std::vector filter_out_t; +typedef std::string torrent_name_t; + +struct torrent_t { + torrent_handle handle; + unique_ID_t unique_ID; + filter_out_t filter_out; + torrent_name_t name; +}; + +typedef std::vector torrents_t; +typedef torrents_t::iterator torrents_t_iterator; + + +//--------------------------- +// MODULE-GLOBAL VARIABLES +//--------------------------- + +long M_unique_counter = 0; +session_settings *M_settings = NULL; +session *M_ses = NULL; +PyObject *M_constants = NULL; +ip_filter *M_the_filter = NULL; +torrents_t *M_torrents = NULL; + +//--------------------- +// Internal functions +//--------------------- + +bool empty_name_check(const std::string & name) +{ + return 1; +} + +long handle_exists(torrent_handle &handle) +{ + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].handle == handle) + return 1; + + return 0; +} + +long get_torrent_index(torrent_handle &handle) +{ + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].handle == handle) + { +// printf("Found: %li\r\n", i); + return i; + } + + throw std::runtime_error("P-LT: Handle not found."); + return -1; +} + +long get_index_from_unique_ID(long unique_ID) +{ + assert(M_handles->size() == M_unique_IDs->size()); + + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].unique_ID == unique_ID) + return i; + + throw std::runtime_error("P-LT: No such unique_ID."); + return -1; +} + +long internal_add_torrent(std::string const& torrent_name, + float preferred_ratio, + bool compact_mode, + boost::filesystem::path const& save_path) +{ + std::ifstream in(torrent_name.c_str(), std::ios_base::binary); + in.unsetf(std::ios_base::skipws); + entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); + torrent_info t(e); + + entry resume_data; + try + { + std::stringstream s; + s << torrent_name << ".fastresume"; + boost::filesystem::ifstream resumeFile(s.str(), std::ios_base::binary); + resumeFile.unsetf(std::ios_base::skipws); + resume_data = bdecode(std::istream_iterator(resumeFile), + std::istream_iterator()); + } + catch (invalid_encoding&) {} + catch (boost::filesystem::filesystem_error&) {} + + // Create new torrent object + + torrent_t new_torrent; + + torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, compact_mode, 16 * 1024); +// h.set_max_connections(60); // at some point we should use this + h.set_max_uploads(-1); + h.set_ratio(preferred_ratio); + new_torrent.handle = h; + + new_torrent.unique_ID = M_unique_counter; + M_unique_counter++; + + long num_files = h.get_torrent_info().num_files(); + + new_torrent.filter_out.reserve(num_files); + + for (long i = 0; i < num_files; i++) + new_torrent.filter_out[i] = 0; + + new_torrent.name = torrent_name; + + M_torrents->push_back(new_torrent); + + return (new_torrent.unique_ID); +} + +void internal_remove_torrent(long index) +{ + assert(index < M_torrents->size()); + + torrent_handle& h = M_torrents->at(index).handle; + + // For valid torrents, save fastresume data + if (h.is_valid() && h.has_metadata()) + { + h.pause(); + + entry data = h.write_resume_data(); + + std::stringstream s; + s << M_torrents->at(index).name << ".fastresume"; + + boost::filesystem::ofstream out(s.str(), std::ios_base::binary); + + out.unsetf(std::ios_base::skipws); + + bencode(std::ostream_iterator(out), data); + } + + M_ses->remove_torrent(h); + + torrents_t_iterator it = M_torrents->begin() + index; + M_torrents->erase(it); +} + +long get_peer_index(tcp::endpoint addr, std::vector const& peers) +{ + long index = -1; + + for (unsigned long i = 0; i < peers.size(); i++) + if (peers[i].ip == addr) + index = i; + + return index; +} + +// The following function contains code by Christophe Dumez and Arvid Norberg +void internal_add_files(torrent_info& t, + boost::filesystem::path const& p, + boost::filesystem::path const& l) +{ + boost::filesystem::path f(p / l); // change default checker, perhaps? + if (is_directory(f)) + { + for (boost::filesystem::directory_iterator i(f), end; i != end; ++i) + internal_add_files(t, p, l / i->leaf()); + } else + t.add_file(l, file_size(f)); +} + +long count_DHT_peers(entry &state) +{ + long num_peers = 0; + entry *nodes = state.find_key("nodes"); + if (nodes) + { + entry::list_type &peers = nodes->list(); + entry::list_type::const_iterator i; + i = peers.begin(); + + while (i != peers.end()) + { + num_peers++; + i++; + } + } + + return num_peers; +} + + +//===================== +// External functions +//===================== + +static PyObject *torrent_init(PyObject *self, PyObject *args) +{ + printf("python-libtorrent, using libtorrent %s. Compiled with NDEBUG value: %d\r\n", + LIBTORRENT_VERSION, + NDEBUG); + + // Tell Boost that we are on *NIX, so bloody '.'s are ok inside a directory name! + boost::filesystem::path::default_name_check(empty_name_check); + + char *client_ID, *user_agent; + python_long v1,v2,v3,v4; + + PyArg_ParseTuple(args, "siiiis", &client_ID, &v1, &v2, &v3, &v4, &user_agent); + + M_settings = new session_settings; + M_ses = new session(fingerprint(client_ID, v1, v2, v3, v4)); + + M_torrents = new torrents_t; + M_torrents->reserve(10); // pretty cheap, just 10 + + // Init values + + M_settings->user_agent = std::string(user_agent); + + M_ses->set_max_half_open_connections(-1); + M_ses->set_download_rate_limit(-1); + M_ses->set_upload_rate_limit(-1); + + M_ses->set_settings(*M_settings); + M_ses->set_severity_level(alert::debug); + + M_constants = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}", + "EVENT_NULL", EVENT_NULL, + "EVENT_FINISHED", EVENT_FINISHED, + "EVENT_PEER_ERROR", EVENT_PEER_ERROR, + "EVENT_INVALID_REQUEST", EVENT_INVALID_REQUEST, + "EVENT_FILE_ERROR", EVENT_FILE_ERROR, + "EVENT_HASH_FAILED_ERROR", EVENT_HASH_FAILED_ERROR, + "EVENT_PEER_BAN_ERROR", EVENT_PEER_BAN_ERROR, + "EVENT_FASTRESUME_REJECTED_ERROR", EVENT_FASTRESUME_REJECTED_ERROR, + "EVENT_TRACKER", EVENT_TRACKER, + "EVENT_OTHER", EVENT_OTHER, + "STATE_QUEUED", STATE_QUEUED, + "STATE_CHECKING", STATE_CHECKING, + "STATE_CONNECTING", STATE_CONNECTING, + "STATE_DOWNLOADING_META", STATE_DOWNLOADING_META, + "STATE_DOWNLOADING", STATE_DOWNLOADING, + "STATE_FINISHED", STATE_FINISHED, + "STATE_SEEDING", STATE_SEEDING, + "STATE_ALLOCATING", STATE_ALLOCATING, + "ERROR_INVALID_ENCODING", ERROR_INVALID_ENCODING, + "ERROR_INVALID_TORRENT", ERROR_INVALID_TORRENT, + "ERROR_FILESYSTEM", ERROR_FILESYSTEM, + "ERROR_DUPLICATE_TORRENT", ERROR_DUPLICATE_TORRENT); + + Py_INCREF(Py_None); return Py_None; +}; + +static PyObject *torrent_quit(PyObject *self, PyObject *args) +{ + long Num = M_torrents->size(); + + // Shut down torrents gracefully + for (long i = 0; i < Num; i++) + internal_remove_torrent(0); + + delete M_ses; // SLOWPOKE because of waiting for the trackers before shutting down + delete M_settings; + delete M_torrents; + + Py_DECREF(M_constants); + + Py_INCREF(Py_None); return Py_None; +}; + +static PyObject *torrent_set_max_half_open(PyObject *self, PyObject *args) +{ + python_long arg; + PyArg_ParseTuple(args, "i", &arg); + + M_ses->set_max_half_open_connections(arg); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_set_download_rate_limit(PyObject *self, PyObject *args) +{ + python_long arg; + PyArg_ParseTuple(args, "i", &arg); +// printf("Capping download to %d bytes per second\r\n", (int)arg); + M_ses->set_download_rate_limit(arg); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_set_upload_rate_limit(PyObject *self, PyObject *args) +{ + python_long arg; + PyArg_ParseTuple(args, "i", &arg); +// printf("Capping upload to %d bytes per second\r\n", (int)arg); + M_ses->set_upload_rate_limit(arg); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_set_listen_on(PyObject *self, PyObject *args) +{ + python_long port_start, port_end; + PyArg_ParseTuple(args, "ii", &port_start, &port_end); + + M_ses->listen_on(std::make_pair(port_start, port_end), ""); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_is_listening(PyObject *self, PyObject *args) +{ + long ret = (M_ses->is_listening() != 0); + + return Py_BuildValue("i", ret); +} + +static PyObject *torrent_listening_port(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", (python_long)M_ses->listen_port()); +} + +static PyObject *torrent_set_max_uploads(PyObject *self, PyObject *args) +{ + python_long max_up; + PyArg_ParseTuple(args, "i", &max_up); + + M_ses->set_max_uploads(max_up); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_set_max_connections(PyObject *self, PyObject *args) +{ + python_long max_conn; + PyArg_ParseTuple(args, "i", &max_conn); + +// printf("Setting max connections: %d\r\n", max_conn); + M_ses->set_max_connections(max_conn); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_add_torrent(PyObject *self, PyObject *args) +{ + const char *name, *save_dir; + python_long compact; + PyArg_ParseTuple(args, "ssi", &name, &save_dir, &compact); + + boost::filesystem::path save_dir_2 (save_dir, empty_name_check); + + try + { + return Py_BuildValue("i", internal_add_torrent(name, 0, compact, save_dir_2)); + } + catch (invalid_encoding&) + { + return Py_BuildValue("i", ERROR_INVALID_ENCODING); + } + catch (invalid_torrent_file&) + { + return Py_BuildValue("i", ERROR_INVALID_TORRENT); + } + catch (boost::filesystem::filesystem_error&) + { + return Py_BuildValue("i", ERROR_FILESYSTEM); + } + catch (duplicate_torrent&) + { + return Py_BuildValue("i", ERROR_DUPLICATE_TORRENT); + } +} + +static PyObject *torrent_remove_torrent(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + internal_remove_torrent(index); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_get_num_torrents(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", M_torrents->size()); +} + +static PyObject *torrent_reannounce(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + M_torrents->at(index).handle.force_reannounce(); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_pause(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + M_torrents->at(index).handle.pause(); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_resume(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + M_torrents->at(index).handle.resume(); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_get_name(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + return Py_BuildValue("s", M_torrents->at(index).handle.get_torrent_info().name().c_str()); +} + +static PyObject *torrent_get_state(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + torrent_t &t = M_torrents->at(index); + torrent_status s = t.handle.status(); + const torrent_info &i = t.handle.get_torrent_info(); + + std::vector peers; + t.handle.get_peer_info(peers); + + long total_seeds = 0; + long total_peers = 0; + + for (unsigned long i = 0; i < peers.size(); i++) + if (peers[i].seed) + total_seeds++; + else + total_peers++; + + return Py_BuildValue("{s:l,s:l,s:l,s:f,s:f,s:d,s:f,s:l,s:l,s:s,s:s,s:f,s:d,s:l,s:l,s:l,s:d,s:l,s:l,s:l,s:l,s:l,s:l,s:d,s:d,s:l,s:l}", + "state", s.state, + "num_peers", s.num_peers, + "num_seeds", s.num_seeds, + "distributed_copies", s.distributed_copies, + "download_rate", s.download_rate, + "total_download", double(s.total_download), // WAS: payload in the middle there + "upload_rate", s.upload_rate, + "total_upload", long(s.total_upload), // WAS: payload +//"ratio", float(-1),//float(s.total_payload_download)/float(s.total_payload_upload), + "tracker_ok", !s.current_tracker.empty(), + "next_announce", boost::posix_time::to_simple_string(s.next_announce).c_str(), + "tracker", s.current_tracker.c_str(), + "progress", float(s.progress), + "total_done", double(s.total_done), + "pieces", long(s.pieces), + "pieces_done", long(s.num_pieces), + "block_size", long(s.block_size), + "total_size", double(i.total_size()), + "piece_length", long(i.piece_length()), + "num_pieces", long(i.num_pieces()), + "total_seeds", total_seeds, + "total_peers", total_peers, + "is_paused", long(t.handle.is_paused()), + "is_seed", long(t.handle.is_seed()), + "total_wanted", double(s.total_wanted), + "total_wanted_done", double(s.total_wanted_done), + "num_complete", long(s.num_complete), + "num_incomplete", long(s.num_incomplete)); +}; + +static PyObject *torrent_pop_event(PyObject *self, PyObject *args) +{ + std::auto_ptr a; + + a = M_ses->pop_alert(); + + alert *popped_alert = a.get(); + + if (!popped_alert) + { + Py_INCREF(Py_None); return Py_None; + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i}", "event_type", EVENT_FINISHED, + "unique_ID", + M_torrents->at(get_torrent_index(handle)).unique_ID); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + peer_id peer_ID = (dynamic_cast(popped_alert))->pid; + std::string peer_IP = + (dynamic_cast(popped_alert))->ip.address().to_string(); + + return Py_BuildValue("{s:i,s:s,s:s,s:s}", "event_type", EVENT_PEER_ERROR, + "client_ID", identify_client(peer_ID).c_str(), + "ip", peer_IP.c_str(), + "message", a->msg().c_str() ); + } else if (dynamic_cast(popped_alert)) + { + peer_id peer_ID = (dynamic_cast(popped_alert))->pid; + + return Py_BuildValue("{s:i,s:s,s:s}", + "event_type", EVENT_INVALID_REQUEST, + "client_ID", identify_client(peer_ID).c_str(), + "message", a->msg().c_str() ); + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s}", + "event_type", EVENT_FILE_ERROR, + "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID, + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:i,s:s}", + "event_type", EVENT_HASH_FAILED_ERROR, + "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID, + "piece_index", + long((dynamic_cast(popped_alert))->piece_index), + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + std::string peer_IP = (dynamic_cast(popped_alert))->ip.address().to_string(); + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_PEER_BAN_ERROR, + "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID, + "ip", peer_IP.c_str(), + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s}", + "event_type", EVENT_FASTRESUME_REJECTED_ERROR, + "unique_ID", M_torrents->at(get_torrent_index(handle)).unique_ID, + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(get_torrent_index(handle)).unique_ID, + "tracker_status", "Announce sent", + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(get_torrent_index(handle)).unique_ID, + "trackerStatus", "Bad response (status code=?)", + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(get_torrent_index(handle)).unique_ID, + "tracker_status", "Announce succeeded", + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(get_torrent_index(handle)).unique_ID, + "tracker_status", "Warning in response", + "message", a->msg().c_str() ); + else + { Py_INCREF(Py_None); return Py_None; } + } + + return Py_BuildValue("{s:i,s:s}", "event_type", EVENT_OTHER, + "message", a->msg().c_str() ); +} + +static PyObject *torrent_get_session_info(PyObject *self, PyObject *args) +{ + session_status s = M_ses->status(); + + return Py_BuildValue("{s:l,s:f,s:f,s:f,s:f,s:l}", + "has_incoming_connections", long(s.has_incoming_connections), + "upload_rate", float(s.upload_rate), + "download_rate", float(s.download_rate), + "payload_upload_rate", float(s.payload_upload_rate), + "payload_download_rate", float(s.payload_download_rate), + "num_peers", long(s.num_peers)); +} + +static PyObject *torrent_get_peer_info(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + std::vector peers; + M_torrents->at(index).handle.get_peer_info(peers); + + PyObject *peer_info; + + PyObject *ret = PyTuple_New(peers.size()); + + for (unsigned long i = 0; i < peers.size(); i++) + { + std::vector &pieces = peers[i].pieces; + unsigned long pieces_had = 0; + + for (unsigned long piece = 0; piece < pieces.size(); piece++) + if (pieces[piece]) + pieces_had++; + + peer_info = Py_BuildValue( + "{s:f,s:d,s:f,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:s,s:i,s:s,s:f}", + "download_speed", float(peers[i].down_speed), + "total_download", double(peers[i].total_download), + "upload_speed", float(peers[i].up_speed), + "total_upload", double(peers[i].total_upload), + "download_queue_length", long(peers[i].download_queue_length), + "upload_queue_length", long(peers[i].upload_queue_length), + "is_interesting", long((peers[i].flags & peer_info::interesting) != 0), + "is_choked", long((peers[i].flags & peer_info::choked) != 0), + "is_remote_interested", long((peers[i].flags & peer_info::remote_interested) != 0), + "is_remote_choked", long((peers[i].flags & peer_info::remote_choked) != 0), + "supports_extensions", long((peers[i].flags & peer_info::supports_extensions)!= 0), + "is_local_connection", long((peers[i].flags & peer_info::local_connection) != 0), + "is_awaiting_handshake", long((peers[i].flags & peer_info::handshake) != 0), + "is_connecting", long((peers[i].flags & peer_info::connecting) != 0), + "is_queued", long((peers[i].flags & peer_info::queued) != 0), + "client", peers[i].client.c_str(), + "is_seed", long(peers[i].seed), + "ip", peers[i].ip.address().to_string().c_str(), + "peer_has", float(float(pieces_had)*100.0/pieces.size()) + ); + + PyTuple_SetItem(ret, i, peer_info); + }; + + return ret; +}; + +static PyObject *torrent_get_file_info(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyArg_ParseTuple(args, "i", &unique_ID); + long index = get_index_from_unique_ID(unique_ID); + + std::vector temp_files; + + PyObject *file_info; + + std::vector progresses; + + torrent_t &t = M_torrents->at(index); + t.handle.file_progress(progresses); + + torrent_info::file_iterator start = + t.handle.get_torrent_info().begin_files(); + torrent_info::file_iterator end = + t.handle.get_torrent_info().end_files(); + + long fileIndex = 0; + + filter_out_t &filter_out = t.filter_out; + + for(torrent_info::file_iterator i = start; i != end; ++i) + { + file_entry const &currFile = (*i); + + file_info = Py_BuildValue( + "{s:s,s:d,s:d,s:f,s:i}", + "path", currFile.path.string().c_str(), + "offset", double(currFile.offset), + "size", double(currFile.size), + "progress", progresses[i - start]*100.0, + "filtered_out", long(filter_out.at(fileIndex)) + ); + + fileIndex++; + + temp_files.push_back(file_info); + }; + + PyObject *ret = PyTuple_New(temp_files.size()); + + for (unsigned long i = 0; i < temp_files.size(); i++) + PyTuple_SetItem(ret, i, temp_files[i]); + + return ret; +}; + +static PyObject *torrent_set_filter_out(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyObject *filter_out_object; + PyArg_ParseTuple(args, "iO", &unique_ID, &filter_out_object); + long index = get_index_from_unique_ID(unique_ID); + + torrent_t &t = M_torrents->at(index); + long num_files = t.handle.get_torrent_info().num_files(); + assert(PyList_Size(filter_out_object) == num_files); + + for (long i = 0; i < num_files; i++) + { + t.filter_out.at(i) = + PyInt_AsLong(PyList_GetItem(filter_out_object, i)); + }; + + t.handle.filter_files(t.filter_out); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_constants(PyObject *self, PyObject *args) +{ + Py_INCREF(M_constants); return M_constants; +} + +static PyObject *torrent_start_DHT(PyObject *self, PyObject *args) +{ + const char *DHT_path; + PyArg_ParseTuple(args, "s", &DHT_path); + + printf("Loading DHT state from %s\r\n", DHT_path); + + boost::filesystem::path tempPath(DHT_path, empty_name_check); + boost::filesystem::ifstream DHT_state_file(tempPath, std::ios_base::binary); + DHT_state_file.unsetf(std::ios_base::skipws); + + entry DHT_state; + try{ + DHT_state = bdecode(std::istream_iterator(DHT_state_file), + std::istream_iterator()); + M_ses->start_dht(DHT_state); + printf("DHT state recovered.\r\n"); + + // Print out the state data from the FILE (not the session!) + printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state)); + + } catch (std::exception&) { + printf("No DHT file to resume\r\n"); + M_ses->start_dht(); + } + +// M_ses->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), +// DHT_ROUTER_PORT)); +// M_ses->add_dht_router(std::make_pair(std::string("router.utorrent.com"), +// DHT_ROUTER_PORT)); +//// M_ses->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), +//// DHT_ROUTER_PORT)); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_stop_DHT(PyObject *self, PyObject *args) +{ + const char *DHT_path; + PyArg_ParseTuple(args, "s", &DHT_path); + + printf("Saving DHT state to %s\r\n", DHT_path); + + boost::filesystem::path tempPath = boost::filesystem::path(DHT_path, empty_name_check); + + try { + entry DHT_state = M_ses->dht_state(); + + printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state)); + + boost::filesystem::ofstream out(tempPath, std::ios_base::binary); + out.unsetf(std::ios_base::skipws); + bencode(std::ostream_iterator(out), DHT_state); + } catch (std::exception& e) { + printf("An error occured in saving DHT\r\n"); + std::cerr << e.what() << "\n"; + } + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_get_DHT_info(PyObject *self, PyObject *args) +{ + entry DHT_state = M_ses->dht_state(); + + return Py_BuildValue("l", python_long(count_DHT_peers(DHT_state))); + +/* +// DHT_state.print(cout); + entry *nodes = DHT_state.find_key("nodes"); + if (!nodes) + return Py_BuildValue("l", -1); // No nodes - we are just starting up... + + entry::list_type &peers = nodes->list(); + entry::list_type::const_iterator i; + + python_long num_peers = 0; + + i = peers.begin(); + while (i != peers.end()) + { + num_peers++; + i++; + } + + return Py_BuildValue("l", num_peers); +*/ +} + +// Create Torrents: call with something like: +// create_torrent("mytorrent.torrent", "directory or file to make a torrent out of", +// "tracker1\ntracker2\ntracker3", "no comment", 256, "Deluge"); +// That makes a torrent with pieces of 256K, with "Deluge" as the creator string. +// +// The following function contains code by Christophe Dumez and Arvid Norberg +static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) +{ + char *destination, *comment, *creator_str, *input, *trackers; + python_long piece_size; + PyArg_ParseTuple(args, "ssssis", &destination, &input, &trackers, &comment, &piece_size, &creator_str); + + piece_size = piece_size * 1024; + + try + { + torrent_info t; + boost::filesystem::path full_path = complete(boost::filesystem::path(input)); + boost::filesystem::ofstream out(complete(boost::filesystem::path(destination)), + std::ios_base::binary); + + internal_add_files(t, full_path.branch_path(), full_path.leaf()); + t.set_piece_size(piece_size); + + storage st(t, full_path.branch_path()); + + std::string stdTrackers(trackers); + unsigned long index = 0, next = stdTrackers.find("\n"); + while (1 == 1) + { + t.add_tracker(stdTrackers.substr(index, next-index)); + index = next + 1; + if (next >= stdTrackers.length()) + break; + next = stdTrackers.find("\n", index); + if (next == std::string::npos) + break; + } + + int num = t.num_pieces(); + std::vector buf(piece_size); + for (int i = 0; i < num; ++i) + { + st.read(&buf[0], i, 0, t.piece_size(i)); + hasher h(&buf[0], t.piece_size(i)); + t.set_hash(i, h.final()); + } + + t.set_creator(creator_str); + t.set_comment(comment); + + entry e = t.create_torrent(); + bencode(std::ostream_iterator(out), e); + return Py_BuildValue("l", 1); + } catch (std::exception& e) + { + std::cerr << e.what() << "\n"; + return Py_BuildValue("l", 0); + } +} + +static PyObject *torrent_apply_IP_filter(PyObject *self, PyObject *args) +{ + PyObject *ranges; + PyArg_ParseTuple(args, "O", &ranges); + + long num_ranges = PyList_Size(ranges); + +// printf("Number of ranges: %ld\r\n", num_ranges); +// Py_INCREF(Py_None); return Py_None; + + // Remove existing filter, if there is one + if (M_the_filter != NULL) + delete M_the_filter; + + M_the_filter = new ip_filter(); + + address_v4 from, to; + PyObject *curr; + +// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + for (long i = 0; i < num_ranges; i++) + { + curr = PyList_GetItem(ranges, i); +// PyObject_Print(curr, stdout, 0); + from = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 0))); + to = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 1))); +// printf("Filtering: %s - %s\r\n", from.to_string().c_str(), to.to_string().c_str()); + M_the_filter->add_rule(from, to, ip_filter::blocked); + }; + +// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + M_ses->set_ip_filter(*M_the_filter); + +// printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + Py_INCREF(Py_None); return Py_None; +} + + +//==================== +// Python Module data +//==================== + +static PyMethodDef TorrentMethods[] = { + {"init", torrent_init, METH_VARARGS, "."}, + {"quit", torrent_quit, METH_VARARGS, "."}, + {"set_max_half_open", torrent_set_max_half_open, METH_VARARGS, "."}, + {"set_download_rate_limit", torrent_set_download_rate_limit, METH_VARARGS, "."}, + {"set_upload_rate_limit", torrent_set_upload_rate_limit, METH_VARARGS, "."}, + {"set_listen_on", torrent_set_listen_on, METH_VARARGS, "."}, + {"is_listening", torrent_is_listening, METH_VARARGS, "."}, + {"listening_port", torrent_listening_port, METH_VARARGS, "."}, + {"set_max_uploads", torrent_set_max_uploads, METH_VARARGS, "."}, + {"set_max_connections", torrent_set_max_connections, METH_VARARGS, "."}, + {"add_torrent", torrent_add_torrent, METH_VARARGS, "."}, + {"remove_torrent", torrent_remove_torrent, METH_VARARGS, "."}, + {"get_num_torrents", torrent_get_num_torrents, METH_VARARGS, "."}, + {"reannounce", torrent_reannounce, METH_VARARGS, "."}, + {"pause", torrent_pause, METH_VARARGS, "."}, + {"resume", torrent_resume, METH_VARARGS, "."}, + {"get_name", torrent_get_name, METH_VARARGS, "."}, + {"get_state", torrent_get_state, METH_VARARGS, "."}, + {"pop_event", torrent_pop_event, METH_VARARGS, "."}, + {"get_session_info", torrent_get_session_info, METH_VARARGS, "."}, + {"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."}, + {"get_file_info", torrent_get_file_info, METH_VARARGS, "."}, + {"set_filter_out", torrent_set_filter_out, METH_VARARGS, "."}, + {"constants", torrent_constants, METH_VARARGS, "."}, + {"start_DHT", torrent_start_DHT, METH_VARARGS, "."}, + {"stop_DHT", torrent_stop_DHT, METH_VARARGS, "."}, + {"get_DHT_info", torrent_get_DHT_info, METH_VARARGS, "."}, + {"create_torrent", torrent_create_torrent, METH_VARARGS, "."}, + {"apply_IP_filter", torrent_apply_IP_filter, METH_VARARGS, "."}, + {NULL} /* Sentinel */ +}; + + +PyMODINIT_FUNC +inittorrent(void) +{ + Py_InitModule("torrent", TorrentMethods); +} diff --git a/library/session.cpp b/library/session.cpp new file mode 100755 index 000000000..611267427 --- /dev/null +++ b/library/session.cpp @@ -0,0 +1,287 @@ +/* + +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 +#include +#include +#include +#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/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/allocate_resources.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" + +using namespace boost::posix_time; +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + session::session( + fingerprint const& id + , std::pair listen_port_range + , char const* listen_interface) + : m_impl(new session_impl(listen_port_range, id, listen_interface)) + { + // turn off the filename checking in boost.filesystem + using namespace boost::filesystem; + if (path::default_name_check_writable()) + path::default_name_check(no_check); + assert(listen_port_range.first > 0); + assert(listen_port_range.first < listen_port_range.second); +#ifndef NDEBUG + // this test was added after it came to my attention + // that devstudios managed c++ failed to generate + // correct code for boost.function + boost::function0 test = boost::ref(*m_impl); + assert(!test.empty()); +#endif + } + + session::session(fingerprint const& id) + : m_impl(new session_impl(std::make_pair(0, 0), id)) + { +#ifndef NDEBUG + boost::function0 test = boost::ref(*m_impl); + assert(!test.empty()); +#endif + } + + session::~session() + { + 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()) + m_impl->abort(); + } + + void session::disable_extensions() + { + m_impl->disable_extensions(); + } + + void session::set_ip_filter(ip_filter const& f) + { + m_impl->set_ip_filter(f); + } + + void session::set_peer_id(peer_id const& id) + { + m_impl->set_peer_id(id); + } + + void session::set_key(int key) + { + m_impl->set_key(key); + } + + void session::enable_extension(extension_index i) + { + m_impl->enable_extension(i); + } + + std::vector session::get_torrents() const + { + return m_impl->get_torrents(); + } + + // if the torrent already exists, this will throw duplicate_torrent + torrent_handle session::add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size) + { + return m_impl->add_torrent(ti, save_path, resume_data + , compact_mode, block_size); + } + + torrent_handle session::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , entry const& e + , bool compact_mode + , int block_size) + { + return m_impl->add_torrent(tracker_url, info_hash, save_path, e + , compact_mode, block_size); + } + + void session::remove_torrent(const torrent_handle& h) + { + m_impl->remove_torrent(h); + } + + bool session::listen_on( + std::pair const& port_range + , const char* net_interface) + { + return m_impl->listen_on(port_range, net_interface); + } + + unsigned short session::listen_port() const + { + return m_impl->listen_port(); + } + + session_status session::status() const + { + return m_impl->status(); + } + +#ifndef TORRENT_DISABLE_DHT + + void session::start_dht(entry const& startup_state) + { + m_impl->start_dht(startup_state); + } + + void session::stop_dht() + { + m_impl->stop_dht(); + } + + void session::set_dht_settings(dht_settings const& settings) + { + m_impl->set_dht_settings(settings); + } + + entry session::dht_state() const + { + return m_impl->dht_state(); + } + + void session::add_dht_node(std::pair const& node) + { + m_impl->add_dht_node(node); + } + + void session::add_dht_router(std::pair const& node) + { + m_impl->add_dht_router(node); + } + +#endif + + bool session::is_listening() const + { + return m_impl->is_listening(); + } + + void session::set_settings(session_settings const& s) + { + m_impl->set_settings(s); + } + + session_settings const& session::settings() + { + return m_impl->settings(); + } + + void session::set_max_uploads(int limit) + { + m_impl->set_max_uploads(limit); + } + + void session::set_max_connections(int limit) + { + m_impl->set_max_connections(limit); + } + + void session::set_max_half_open_connections(int limit) + { + m_impl->set_max_half_open_connections(limit); + } + + void session::set_upload_rate_limit(int bytes_per_second) + { + m_impl->set_upload_rate_limit(bytes_per_second); + } + + void session::set_download_rate_limit(int bytes_per_second) + { + m_impl->set_download_rate_limit(bytes_per_second); + } + + std::auto_ptr session::pop_alert() + { + return m_impl->pop_alert(); + } + + void session::set_severity_level(alert::severity_t s) + { + m_impl->set_severity_level(s); + } + +} + diff --git a/library/session_impl.cpp b/library/session_impl.cpp new file mode 100755 index 000000000..c8005879a --- /dev/null +++ b/library/session_impl.cpp @@ -0,0 +1,1855 @@ +/* + +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 +#include +#include +#include +#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/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/allocate_resources.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" + +using namespace boost::posix_time; +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent { 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 { + // This is the checker thread + // it is looping in an infinite loop + // until the session is aborted. It will + // normally just block in a wait() call, + // waiting for a signal from session that + // there's a new torrent to check. + + void checker_impl::operator()() + { + eh_initializer(); + // if we're currently performing a full file check, + // this is the torrent being processed + boost::shared_ptr processing; + boost::shared_ptr t; + for (;;) + { + // temporary torrent used while checking fastresume data + try + { + t.reset(); + { + boost::mutex::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + // if the job queue is empty and + // we shouldn't abort + // wait for a signal + if (m_torrents.empty() && !m_abort && !processing) + m_cond.wait(l); + + if (m_abort) + { + // no lock is needed here, because the main thread + // has already been shut down by now + processing.reset(); + t.reset(); + std::for_each(m_torrents.begin(), m_torrents.end() + , boost::bind(&torrent::abort + , boost::bind(&shared_ptr::get + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_torrents.clear(); + std::for_each(m_processing.begin(), m_processing.end() + , boost::bind(&torrent::abort + , boost::bind(&shared_ptr::get + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_processing.clear(); + return; + } + + if (!m_torrents.empty()) + { + t = m_torrents.front(); + if (t->abort) + { + // make sure the locking order is + // consistent to avoid dead locks + // we need to lock the session because closing + // torrents assume to have access to it + l.unlock(); + session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex); + l.lock(); + + t->torrent_ptr->abort(); + m_torrents.pop_front(); + continue; + } + } + } + + if (t) + { + std::string error_msg; + t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() + , error_msg); + + if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.m_alerts.post_alert(fastresume_rejected_alert( + t->torrent_ptr->get_handle() + , error_msg)); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "fastresume data for " + << t->torrent_ptr->torrent_file().name() << " rejected: " + << error_msg << "\n"; +#endif + } + + // clear the resume data now that it has been used + // (the fast resume data is now parsed and stored in t) + t->resume_data = entry(); + bool up_to_date = t->torrent_ptr->check_fastresume(*t); + + if (up_to_date) + { + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + INVARIANT_CHECK; + + assert(m_torrents.front() == t); + + t->torrent_ptr->files_checked(t->unfinished_pieces); + m_torrents.pop_front(); + + // we cannot add the torrent if the session is aborted. + if (!m_ses.is_aborted()) + { + m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); + if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_finished_alert( + t->torrent_ptr->get_handle() + , "torrent is complete")); + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector::const_iterator i = t->peers.begin(); + i != t->peers.end(); ++i) + { + t->torrent_ptr->get_policy().peer_from_tracker(*i, id); + } + } + else + { + t->torrent_ptr->abort(); + } + t.reset(); + continue; + } + + // lock the checker while we move the torrent from + // m_torrents to m_processing + { + mutex::scoped_lock l(m_mutex); + assert(m_torrents.front() == t); + + m_torrents.pop_front(); + m_processing.push_back(t); + if (!processing) + { + processing = t; + processing->processing = true; + t.reset(); + } + } + } + } + catch (const std::exception& e) + { + // This will happen if the storage fails to initialize + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + t->torrent_ptr->get_handle() + , e.what())); + } + t->torrent_ptr->abort(); + + assert(!m_torrents.empty()); + m_torrents.pop_front(); + } + catch(...) + { +#ifndef NDEBUG + std::cerr << "error while checking resume data\n"; +#endif + mutex::scoped_lock l(m_mutex); + assert(!m_torrents.empty()); + m_torrents.pop_front(); + assert(false); + } + + if (!processing) continue; + + try + { + assert(processing); + + float finished = false; + float progress = 0.f; + boost::tie(finished, progress) = processing->torrent_ptr->check_files(); + + { + mutex::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + processing->progress = progress; + if (processing->abort) + { + assert(!m_processing.empty()); + assert(m_processing.front() == processing); + + processing->torrent_ptr->abort(); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + continue; + } + } + if (finished) + { + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + INVARIANT_CHECK; + + assert(!m_processing.empty()); + assert(m_processing.front() == processing); + + // TODO: factor out the adding of torrents to the session + // and to the checker thread to avoid duplicating the + // check for abortion. + if (!m_ses.is_aborted()) + { + processing->torrent_ptr->files_checked(processing->unfinished_pieces); + m_ses.m_torrents.insert(std::make_pair( + processing->info_hash, processing->torrent_ptr)); + if (processing->torrent_ptr->is_seed() + && m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_finished_alert( + processing->torrent_ptr->get_handle() + , "torrent is complete")); + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector::const_iterator i = processing->peers.begin(); + i != processing->peers.end(); ++i) + { + processing->torrent_ptr->get_policy().peer_from_tracker(*i, id); + } + } + else + { + processing->torrent_ptr->abort(); + } + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + } + } + catch(std::exception const& e) + { + // This will happen if the storage fails to initialize + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + processing->torrent_ptr->get_handle() + , e.what())); + } + assert(!m_processing.empty()); + + processing->torrent_ptr->abort(); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + } + catch(...) + { +#ifndef NDEBUG + std::cerr << "error while checking files\n"; +#endif + mutex::scoped_lock l(m_mutex); + assert(!m_processing.empty()); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + + assert(false); + } + } + } + + aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash) + { + INVARIANT_CHECK; + for (std::deque >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + if ((*i)->info_hash == info_hash) return i->get(); + } + for (std::deque >::iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + + if ((*i)->info_hash == info_hash) return i->get(); + } + + return 0; + } + + void checker_impl::remove_torrent(sha1_hash const& info_hash) + { + INVARIANT_CHECK; + for (std::deque >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + if ((*i)->info_hash == info_hash) + { + assert((*i)->processing == false); + m_torrents.erase(i); + return; + } + } + for (std::deque >::iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + if ((*i)->info_hash == info_hash) + { + assert((*i)->processing == false); + m_processing.erase(i); + return; + } + } + + assert(false); + } + +#ifndef NDEBUG + void checker_impl::check_invariant() const + { + for (std::deque >::const_iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + assert(*i); + assert((*i)->torrent_ptr); + } + for (std::deque >::const_iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + assert(*i); + assert((*i)->torrent_ptr); + } + } +#endif + + session_impl::session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface) + : m_tracker_manager(m_settings) + , m_listen_port_range(listen_port_range) + , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) + , m_abort(false) + , m_upload_rate(-1) + , m_download_rate(-1) + , m_max_uploads(-1) + , m_max_connections(-1) + , m_half_open_limit(-1) + , m_incoming_connection(false) + , m_last_tick(microsec_clock::universal_time()) + , m_timer(m_selector) + , m_checker_impl(*this) + { + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_logger = create_log("main_session", false); + using boost::posix_time::second_clock; + using boost::posix_time::to_simple_string; + (*m_logger) << to_simple_string(second_clock::universal_time()) << "\n"; +#endif + std::fill(m_extension_enabled, m_extension_enabled + + num_supported_extensions, true); + // ---- generate a peer id ---- + + std::srand((unsigned int)std::time(0)); + + m_key = rand() + (rand() << 15) + (rand() << 30); + std::string print = cl_fprint.to_string(); + assert(print.length() <= 20); + + // the client's fingerprint + std::copy( + print.begin() + , print.begin() + print.length() + , m_peer_id.begin()); + + // http-accepted characters: + static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz-_.!~*'()"; + + // the random number + for (unsigned char* i = m_peer_id.begin() + print.length(); + i != m_peer_id.end(); ++i) + { + *i = printable[rand() % (sizeof(printable)-1)]; + } + + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(bind(&session_impl::second_tick, this, _1)); + + m_thread.reset(new boost::thread(boost::ref(*this))); + m_checker_thread.reset(new boost::thread(boost::ref(m_checker_impl))); + } + +#ifndef TORRENT_DISABLE_DHT + void session_impl::add_dht_node(udp::endpoint n) + { + if (m_dht) m_dht->add_node(n); + } +#endif + + void session_impl::abort() + { + mutex_t::scoped_lock l(m_mutex); + assert(!m_abort); + // abort the main thread + m_abort = true; + m_selector.interrupt(); + l.unlock(); + + mutex::scoped_lock l2(m_checker_impl.m_mutex); + // abort the checker thread + m_checker_impl.m_abort = true; + } + + void session_impl::set_ip_filter(ip_filter const& f) + { + mutex_t::scoped_lock l(m_mutex); + m_ip_filter = f; + + // Close connections whose endpoint is filtered + // by the new ip-filter + for (session_impl::connection_map::iterator i + = m_connections.begin(); i != m_connections.end();) + { + tcp::endpoint sender = i->first->remote_endpoint(); + if (m_ip_filter.access(sender.address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*i->second->m_logger) << "*** CONNECTION FILTERED\n"; +#endif + session_impl::connection_map::iterator j = i; + ++i; + j->second->disconnect(); + } + else ++i; + } + } + + bool session_impl::extensions_enabled() const + { + const int n = num_supported_extensions; + return std::find(m_extension_enabled + , m_extension_enabled + n, true) != m_extension_enabled + n; + } + + void session_impl::set_settings(session_settings const& s) + { + mutex_t::scoped_lock l(m_mutex); + m_settings = s; + // 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 = ' '; + } + + void session_impl::open_listen_port() + { + try + { + // create listener socket + m_listen_socket = boost::shared_ptr(new socket_acceptor(m_selector)); + + for(;;) + { + try + { + m_listen_socket->open(asio::ip::tcp::v4()); + m_listen_socket->bind(m_listen_interface); + m_listen_socket->listen(); + break; + } + catch (asio::error& e) + { + // TODO: make sure this is correct + if (e.code() == asio::error::host_not_found) + { + if (m_alerts.should_post(alert::fatal)) + { + std::string msg = "cannot listen on the given interface '" + + m_listen_interface.address().to_string() + "'"; + m_alerts.post_alert(listen_failed_alert(msg)); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "cannot listen on the given interface '" + + m_listen_interface.address().to_string() + "'"; + (*m_logger) << msg << "\n"; +#endif + assert(m_listen_socket.unique()); + m_listen_socket.reset(); + break; + } + m_listen_interface.port(m_listen_interface.port() + 1); + if (m_listen_interface.port() > m_listen_port_range.second) + { + std::stringstream msg; + msg << "none of the ports in the range [" + << m_listen_port_range.first + << ", " << m_listen_port_range.second + << "] could be opened for listening"; + m_alerts.post_alert(listen_failed_alert(msg.str())); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << msg.str() << "\n"; +#endif + m_listen_socket.reset(); + break; + } + } + } + } + catch (asio::error& e) + { + if (m_alerts.should_post(alert::fatal)) + { + m_alerts.post_alert(listen_failed_alert( + std::string("failed to open listen port") + e.what())); + } + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (m_listen_socket) + { + (*m_logger) << "listening on port: " << m_listen_interface.port() << "\n"; + } +#endif + if (m_listen_socket) async_accept(); + } + + void session_impl::process_connection_queue() + { + while (!m_connection_queue.empty()) + { + if ((int)m_half_open.size() >= m_half_open_limit + && m_half_open_limit > 0) + return; + + connection_queue::value_type c = m_connection_queue.front(); + + try + { + m_connection_queue.pop_front(); + assert(c->associated_torrent().lock().get()); + c->connect(); + m_half_open.insert(std::make_pair(c->get_socket(), c)); + } + catch (std::exception& e) + { + c->disconnect(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << "connect failed [" << c->remote() << "]: " + << e.what() << "\n"; +#endif + } + } + } + + void session_impl::async_accept() + { + shared_ptr c(new stream_socket(m_selector)); + m_listen_socket->async_accept(*c + , bind(&session_impl::on_incoming_connection, this, c + , weak_ptr(m_listen_socket), _1)); + } + + void session_impl::on_incoming_connection(shared_ptr const& s + , weak_ptr const& listen_socket, asio::error const& e) try + { + if (listen_socket.expired()) + return; + + if (e == asio::error::operation_aborted) + return; + + mutex_t::scoped_lock l(m_mutex); + assert(listen_socket.lock() == m_listen_socket); + + if (m_abort) return; + + async_accept(); + if (e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "error accepting connection on '" + + m_listen_interface.address().to_string() + "'"; + (*m_logger) << msg << "\n"; +#endif + assert(m_listen_socket.unique()); + return; + } + + // we got a connection request! + m_incoming_connection = true; + tcp::endpoint endp = s->remote_endpoint(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << endp << " <== INCOMING CONNECTION\n"; +#endif + if (m_ip_filter.access(endp.address().to_v4()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << "filtered blocked ip\n"; +#endif + // TODO: issue an info-alert when an ip is blocked!! + return; + } + + boost::intrusive_ptr c( + new bt_peer_connection(*this, s)); +#ifndef NDEBUG + c->m_in_constructor = false; +#endif + + m_connections.insert(std::make_pair(s, c)); + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::string err = exc.what(); +#endif + } + + void session_impl::connection_failed(boost::shared_ptr const& s + , tcp::endpoint const& a, char const* message) +#ifndef NDEBUG + try +#endif + { + mutex_t::scoped_lock l(m_mutex); + + connection_map::iterator p = m_connections.find(s); + + // the connection may have been disconnected in the receive or send phase + if (p != m_connections.end()) + { + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert( + a + , p->second->pid() + , message)); + } + +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->second->m_logger) << "*** CONNECTION FAILED " << message << "\n"; +#endif + p->second->set_failed(); + p->second->disconnect(); + } + else + { + // the error was not in one of the connected + // conenctions. Look among the half-open ones. + p = m_half_open.find(s); + if (p != m_half_open.end()) + { + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert( + a + , p->second->pid() + , message)); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << "CLOSED: " << a.address().to_string() + << " " << message << "\n"; +#endif + p->second->set_failed(); + p->second->disconnect(); + } + } + } +#ifndef NDEBUG + catch (...) + { + assert(false); + }; +#endif + + void session_impl::close_connection(boost::intrusive_ptr const& p) + { + mutex_t::scoped_lock l(m_mutex); + + assert(p->is_disconnecting()); + + if (p->is_connecting()) + { + assert(p->is_local()); + assert(m_connections.find(p->get_socket()) == m_connections.end()); + // Since this peer is still connecting, will not be + // in the list of completed connections. + connection_map::iterator i = m_half_open.find(p->get_socket()); + if (i == m_half_open.end()) + { + // this connection is not in the half-open list, so it + // has to be in the queue, waiting to be connected. + connection_queue::iterator j = std::find( + m_connection_queue.begin(), m_connection_queue.end(), p); + + // if this connection was closed while being connected + // it has been removed from the connection queue and + // not yet put into the half-open queue. + if (j != m_connection_queue.end()) + m_connection_queue.erase(j); + } + else + { + m_half_open.erase(i); + process_connection_queue(); + } + } + else + { + assert(m_half_open.find(p->get_socket()) == m_half_open.end()); + assert(std::find(m_connection_queue.begin() + , m_connection_queue.end(), p) == m_connection_queue.end()); + connection_map::iterator i = m_connections.find(p->get_socket()); +// assert (i != m_connections.end()); + if (i != m_connections.end()) + m_connections.erase(i); + } + } + + void session_impl::set_peer_id(peer_id const& id) + { + mutex_t::scoped_lock l(m_mutex); + m_peer_id = id; + } + + void session_impl::set_key(int key) + { + mutex_t::scoped_lock l(m_mutex); + m_key = key; + } + + void session_impl::second_tick(asio::error const& e) try + { + session_impl::mutex_t::scoped_lock l(m_mutex); + + if (e) + { +#if defined(TORRENT_LOGGING) + (*m_logger) << "*** SECOND TIMER FAILED " << e.what() << "\n"; +#endif + m_abort = true; + m_selector.interrupt(); + return; + } + + if (m_abort) return; + float tick_interval = (microsec_clock::universal_time() + - m_last_tick).total_milliseconds() / 1000.f; + m_last_tick = microsec_clock::universal_time(); + + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(bind(&session_impl::second_tick, this, _1)); + + // do the second_tick() on each connection + // this will update their statistics (download and upload speeds) + // also purge sockets that have timed out + // and keep sockets open by keeping them alive. + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + // we need to do like this because j->second->disconnect() will + // erase the connection from the map we're iterating + connection_map::iterator j = i; + ++i; + // if this socket has timed out + // close it. + peer_connection& c = *j->second; + if (c.has_timed_out()) + { + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert( + c.remote() + , c.pid() + , "connection timed out")); + } +#if defined(TORRENT_VERBOSE_LOGGING) + (*c.m_logger) << "*** CONNECTION TIMED OUT\n"; +#endif + + c.set_failed(); + c.disconnect(); + continue; + } + + c.keep_alive(); + } + + // check each torrent for tracker updates + // TODO: do this in a timer-event in each torrent instead + for (std::map >::iterator i + = m_torrents.begin(); i != m_torrents.end();) + { + torrent& t = *i->second; + assert(!t.is_aborted()); + if (t.should_request()) + { + tracker_request req = t.generate_tracker_request(); + req.listen_port = m_listen_interface.port(); + req.key = m_key; + m_tracker_manager.queue_request(m_selector, req, t.tracker_login() + , i->second); + + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert( + tracker_announce_alert( + t.get_handle(), "tracker announce")); + } + } + + // second_tick() will set the used upload quota + t.second_tick(m_stat, tick_interval); + ++i; + } + + m_stat.second_tick(tick_interval); + + // distribute the maximum upload rate among the torrents + + assert(m_upload_rate >= -1); + assert(m_download_rate >= -1); + assert(m_max_uploads >= -1); + assert(m_max_connections >= -1); + + allocate_resources(m_upload_rate == -1 + ? std::numeric_limits::max() + : int(m_upload_rate * tick_interval) + , m_torrents + , &torrent::m_ul_bandwidth_quota); + + allocate_resources(m_download_rate == -1 + ? std::numeric_limits::max() + : int(m_download_rate * tick_interval) + , m_torrents + , &torrent::m_dl_bandwidth_quota); + + allocate_resources(m_max_uploads == -1 + ? std::numeric_limits::max() + : m_max_uploads + , m_torrents + , &torrent::m_uploads_quota); + + allocate_resources(m_max_connections == -1 + ? std::numeric_limits::max() + : m_max_connections + , m_torrents + , &torrent::m_connections_quota); + + for (std::map >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { +#ifndef NDEBUG + i->second->check_invariant(); +#endif + i->second->distribute_resources(); + } + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::string err = exc.what(); +#endif + }; // msvc 7.1 seems to require this + + void session_impl::connection_completed( + boost::intrusive_ptr const& p) +#ifndef NDEBUG + try +#endif + { + mutex_t::scoped_lock l(m_mutex); + + if (m_abort) return; + + connection_map::iterator i = m_half_open.find(p->get_socket()); + + m_connections.insert(std::make_pair(p->get_socket(), p)); + if (i != m_half_open.end()) m_half_open.erase(i); + process_connection_queue(); + } +#ifndef NDEBUG + catch (std::exception& e) + { + assert(false); + }; +#endif + + void session_impl::operator()() + { + eh_initializer(); + + if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0) + { + session_impl::mutex_t::scoped_lock l(m_mutex); + open_listen_port(); + } + + boost::posix_time::ptime timer = second_clock::universal_time(); + + do + { + try + { + m_selector.run(); + assert(m_abort == true); + } + catch (std::exception& e) + { + #ifndef NDEBUG + std::cerr << e.what() << "\n"; + std::string err = e.what(); + #endif + assert(false); + } + } + while (!m_abort); + + deadline_timer tracker_timer(m_selector); + + session_impl::mutex_t::scoped_lock l(m_mutex); + + m_tracker_manager.abort_all_requests(); + for (std::map >::iterator i = + m_torrents.begin(); i != m_torrents.end(); ++i) + { + i->second->abort(); + if (!i->second->is_paused() || i->second->should_request()) + { + tracker_request req = i->second->generate_tracker_request(); + req.listen_port = m_listen_interface.port(); + req.key = m_key; + std::string login = i->second->tracker_login(); + m_tracker_manager.queue_request(m_selector, req, login); + } + } + + ptime start(microsec_clock::universal_time()); + l.unlock(); + + while (microsec_clock::universal_time() - start < seconds( + m_settings.stop_tracker_timeout) + && !m_tracker_manager.empty()) + { + tracker_timer.expires_from_now(boost::posix_time::milliseconds(100)); + tracker_timer.async_wait(bind(&demuxer::interrupt, &m_selector)); + + m_selector.reset(); + m_selector.run(); + } + + l.lock(); + assert(m_abort); + m_abort = true; + + while (!m_connections.empty()) + m_connections.begin()->second->disconnect(); + + while (!m_half_open.empty()) + m_half_open.begin()->second->disconnect(); + + m_connection_queue.clear(); + +#ifndef NDEBUG + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + assert(i->second->num_peers() == 0); + } +#endif + + m_torrents.clear(); + + assert(m_torrents.empty()); + 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) + { + std::map >::iterator i + = m_torrents.find(info_hash); +#ifndef NDEBUG + for (std::map >::iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + torrent* p = boost::get_pointer(j->second); + assert(p); + } +#endif + if (i != m_torrents.end()) return i->second; + return boost::weak_ptr(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr session_impl::create_log(std::string const& name, bool append) + { + // current options are file_logger, cout_logger and null_logger + return boost::shared_ptr(new logger(name + ".log", append)); + } +#endif + + void session_impl::disable_extensions() + { + mutex_t::scoped_lock l(m_mutex); + std::fill(m_extension_enabled, m_extension_enabled + + num_supported_extensions, false); + } + + void session_impl::enable_extension(extension_index i) + { + assert(i >= 0); + assert(i < num_supported_extensions); + mutex_t::scoped_lock l(m_mutex); + m_extension_enabled[i] = true; + } + + std::vector session_impl::get_torrents() + { + mutex_t::scoped_lock l(m_mutex); + mutex::scoped_lock l2(m_checker_impl.m_mutex); + std::vector ret; + for (std::deque >::iterator i + = m_checker_impl.m_torrents.begin() + , end(m_checker_impl.m_torrents.end()); i != end; ++i) + { + if ((*i)->abort) continue; + ret.push_back(torrent_handle(this, &m_checker_impl + , (*i)->info_hash)); + } + + 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(this, &m_checker_impl + , i->first)); + } + return ret; + } + + torrent_handle session_impl::add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size) + { + // make sure the block_size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (block_size & (1 << i)) + { + assert((block_size & ~(1 << i)) == 0); + break; + } + } +#endif + + assert(!save_path.empty()); + + if (ti.begin_files() == ti.end_files()) + throw std::runtime_error("no files in torrent"); + + // lock the session and the checker thread (the order is important!) + mutex_t::scoped_lock l(m_mutex); + mutex::scoped_lock l2(m_checker_impl.m_mutex); + + if (is_aborted()) + throw std::runtime_error("session is closing"); + + // is the torrent already active? + if (!find_torrent(ti.info_hash()).expired()) + throw duplicate_torrent(); + + // is the torrent currently being checked? + if (m_checker_impl.find_torrent(ti.info_hash())) + throw duplicate_torrent(); + + // create the torrent and the data associated with + // the checker thread and store it before starting + // the thread + boost::shared_ptr torrent_ptr( + new torrent(*this, m_checker_impl, ti, save_path + , m_listen_interface, compact_mode, block_size + , settings())); + + boost::shared_ptr d( + new aux::piece_checker_data); + d->torrent_ptr = torrent_ptr; + d->save_path = save_path; + d->info_hash = ti.info_hash(); + d->resume_data = resume_data; + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + torrent_info::nodes_t const& nodes = ti.nodes(); + std::for_each(nodes.begin(), nodes.end(), bind( + (void(dht::dht_tracker::*)(std::pair const&)) + &dht::dht_tracker::add_node + , boost::ref(m_dht), _1)); + } +#endif + + // add the torrent to the queue to be checked + m_checker_impl.m_torrents.push_back(d); + // and notify the thread that it got another + // job in its queue + m_checker_impl.m_cond.notify_one(); + + return torrent_handle(this, &m_checker_impl, ti.info_hash()); + } + + torrent_handle session_impl::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , entry const& + , bool compact_mode + , int block_size) + { + // make sure the block_size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (block_size & (1 << i)) + { + assert((block_size & ~(1 << i)) == 0); + break; + } + } +#endif + + // TODO: support resume data in this case + assert(!save_path.empty()); + { + // lock the checker_thread + mutex::scoped_lock l(m_checker_impl.m_mutex); + + // is the torrent currently being checked? + if (m_checker_impl.find_torrent(info_hash)) + throw duplicate_torrent(); + } + + // lock the session + session_impl::mutex_t::scoped_lock l(m_mutex); + + // the metadata extension has to be enabled for this to work + assert(m_extension_enabled + [extended_metadata_message]); + + // is the torrent already active? + if (!find_torrent(info_hash).expired()) + throw duplicate_torrent(); + + // you cannot add new torrents to a session that is closing down + assert(!is_aborted()); + + // create the torrent and the data associated with + // the checker thread and store it before starting + // the thread + boost::shared_ptr torrent_ptr( + new torrent(*this, m_checker_impl, tracker_url, info_hash, save_path + , m_listen_interface, compact_mode, block_size + , settings())); + + m_torrents.insert( + std::make_pair(info_hash, torrent_ptr)).first; + + return torrent_handle(this, &m_checker_impl, info_hash); + } + + void session_impl::remove_torrent(const torrent_handle& h) + { + if (h.m_ses != this) return; + assert(h.m_chk == &m_checker_impl || h.m_chk == 0); + assert(h.m_ses != 0); + + mutex_t::scoped_lock l(m_mutex); + session_impl::torrent_map::iterator i = + m_torrents.find(h.m_info_hash); + if (i != m_torrents.end()) + { + torrent& t = *i->second; + t.abort(); + + if (!t.is_paused() || t.should_request()) + { + tracker_request req = t.generate_tracker_request(); + assert(req.event == tracker_request::stopped); + req.listen_port = m_listen_interface.port(); + req.key = m_key; + m_tracker_manager.queue_request(m_selector, req + , t.tracker_login()); + + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert( + tracker_announce_alert( + t.get_handle(), "tracker announce, event=stopped")); + } + } +#ifndef NDEBUG + sha1_hash i_hash = t.torrent_file().info_hash(); +#endif + m_torrents.erase(i); + assert(m_torrents.find(i_hash) == m_torrents.end()); + return; + } + l.unlock(); + + if (h.m_chk) + { + mutex::scoped_lock l(m_checker_impl.m_mutex); + + aux::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash); + if (d != 0) + { + if (d->processing) d->abort = true; + else m_checker_impl.remove_torrent(h.m_info_hash); + return; + } + } + } + + bool session_impl::listen_on( + std::pair const& port_range + , const char* net_interface) + { + session_impl::mutex_t::scoped_lock l(m_mutex); + + tcp::endpoint new_interface; + if (net_interface && std::strlen(net_interface) > 0) + new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first); + else + new_interface = tcp::endpoint(address(), port_range.first); + + m_listen_port_range = port_range; + + // if the interface is the same and the socket is open + // don't do anything + if (new_interface == m_listen_interface + && m_listen_socket) return true; + + if (m_listen_socket) + m_listen_socket.reset(); + +#ifndef TORRENT_DISABLE_DHT + if (m_listen_interface.address() != new_interface.address() + && m_dht) + { + // the listen interface changed, rebind the dht listen socket as well + m_dht->rebind(new_interface.address() + , m_dht_settings.service_port); + } +#endif + + m_incoming_connection = false; + m_listen_interface = new_interface; + + open_listen_port(); + return m_listen_socket; + } + + unsigned short session_impl::listen_port() const + { + mutex_t::scoped_lock l(m_mutex); + return m_listen_interface.port(); + } + + session_status session_impl::status() const + { + mutex_t::scoped_lock l(m_mutex); + session_status s; + s.has_incoming_connections = m_incoming_connection; + s.num_peers = (int)m_connections.size(); + + s.download_rate = m_stat.download_rate(); + s.upload_rate = m_stat.upload_rate(); + + s.payload_download_rate = m_stat.download_payload_rate(); + s.payload_upload_rate = m_stat.upload_payload_rate(); + + s.total_download = m_stat.total_protocol_download() + + m_stat.total_payload_download(); + + s.total_upload = m_stat.total_protocol_upload() + + m_stat.total_payload_upload(); + + s.total_payload_download = m_stat.total_payload_download(); + s.total_payload_upload = m_stat.total_payload_upload(); + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + m_dht->dht_status(s); + } + else + { + s.m_dht_nodes = 0; + s.m_dht_node_cache = 0; + s.m_dht_torrents = 0; + } +#endif + + return s; + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::start_dht(entry const& startup_state) + { + mutex_t::scoped_lock l(m_mutex); + m_dht.reset(new dht::dht_tracker(m_selector + , m_dht_settings, m_listen_interface.address() + , startup_state)); + } + + void session_impl::stop_dht() + { + mutex_t::scoped_lock l(m_mutex); + m_dht.reset(); + } + + void session_impl::set_dht_settings(dht_settings const& settings) + { + mutex_t::scoped_lock l(m_mutex); + if (settings.service_port != m_dht_settings.service_port + && m_dht) + { + m_dht->rebind(m_listen_interface.address() + , settings.service_port); + } + m_dht_settings = settings; + } + + entry session_impl::dht_state() const + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + return m_dht->state(); + } + + void session_impl::add_dht_node(std::pair const& node) + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + m_dht->add_node(node); + } + + void session_impl::add_dht_router(std::pair const& node) + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + m_dht->add_router_node(node); + } + +#endif + + + void session_impl::set_download_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0 || bytes_per_second == -1); + mutex_t::scoped_lock l(m_mutex); + m_download_rate = bytes_per_second; + } + bool session_impl::is_listening() const + { + mutex_t::scoped_lock l(m_mutex); + return m_listen_socket; + } + + session_impl::~session_impl() + { + { + // lock the main thread and abort it + mutex_t::scoped_lock l(m_mutex); + m_abort = true; + m_selector.interrupt(); + } + m_thread->join(); + + // it's important that the main thread is closed completely before + // the checker thread is terminated. Because all the connections + // have to be closed and removed from the torrents before they + // can be destructed. (because the weak pointers in the + // peer_connections will be invalidated when the torrents are + // destructed and then the invariant will be broken). + + { + mutex::scoped_lock l(m_checker_impl.m_mutex); + // abort the checker thread + m_checker_impl.m_abort = true; + + // abort the currently checking torrent + if (!m_checker_impl.m_torrents.empty()) + { + m_checker_impl.m_torrents.front()->abort = true; + } + m_checker_impl.m_cond.notify_one(); + } + + m_checker_thread->join(); + + assert(m_torrents.empty()); + assert(m_connections.empty()); + } + + void session_impl::set_max_uploads(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + m_max_uploads = limit; + } + + void session_impl::set_max_connections(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + m_max_connections = limit; + } + + void session_impl::set_max_half_open_connections(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + m_half_open_limit = limit; + } + + void session_impl::set_upload_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0 || bytes_per_second == -1); + mutex_t::scoped_lock l(m_mutex); + m_upload_rate = bytes_per_second; + } + + std::auto_ptr session_impl::pop_alert() + { + mutex_t::scoped_lock l(m_mutex); + if (m_alerts.pending()) + return m_alerts.get(); + return std::auto_ptr(0); + } + + void session_impl::set_severity_level(alert::severity_t s) + { + mutex_t::scoped_lock l(m_mutex); + m_alerts.set_severity(s); + } + +#ifndef NDEBUG + void session_impl::check_invariant(const char *place) + { + assert(place); + + for (connection_map::iterator i = m_half_open.begin(); + i != m_half_open.end(); ++i) + { + assert(i->second->is_connecting()); + } + + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + assert(i->second); + assert(!i->second->is_connecting()); + if (i->second->is_connecting()) + { + std::ofstream error_log("error.log", std::ios_base::app); + boost::intrusive_ptr p = i->second; + error_log << "peer_connection::is_connecting() " << p->is_connecting() << "\n"; + error_log << "peer_connection::can_write() " << p->can_write() << "\n"; + error_log << "peer_connection::can_read() " << p->can_read() << "\n"; + error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n"; + error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n"; + error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n"; + error_log << "peer_connection::get_peer_id " << p->pid() << "\n"; + error_log << "place: " << place << "\n"; + error_log.flush(); + assert(false); + } + + boost::shared_ptr t = i->second->associated_torrent().lock(); + + if (t) + { + assert(t->get_policy().has_connection(boost::get_pointer(i->second))); + } + } + } +#endif + + void piece_checker_data::parse_resume_data( + const entry& resume_data + , const torrent_info& info + , std::string& error) + { + // if we don't have any resume data, return + if (resume_data.type() == entry::undefined_t) return; + + entry rd = resume_data; + + try + { + if (rd["file-format"].string() != "libtorrent resume file") + { + error = "missing file format tag"; + return; + } + + if (rd["file-version"].integer() > 1) + { + error = "incompatible file version " + + boost::lexical_cast(rd["file-version"].integer()); + return; + } + + // verify info_hash + const std::string &hash = rd["info-hash"].string(); + std::string real_hash((char*)info.info_hash().begin(), (char*)info.info_hash().end()); + if (hash != real_hash) + { + error = "mismatching info-hash: " + hash; + return; + } + + // the peers + + if (rd.find_key("peers")) + { + entry::list_type& peer_list = rd["peers"].list(); + + std::vector tmp_peers; + tmp_peers.reserve(peer_list.size()); + for (entry::list_type::iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + tcp::endpoint a( + address::from_string((*i)["ip"].string()) + , (unsigned short)(*i)["port"].integer()); + tmp_peers.push_back(a); + } + + peers.swap(tmp_peers); + } + + // read piece map + const entry::list_type& slots = rd["slots"].list(); + if ((int)slots.size() > info.num_pieces()) + { + error = "file has more slots than torrent (slots: " + + boost::lexical_cast(slots.size()) + " size: " + + boost::lexical_cast(info.num_pieces()) + " )"; + return; + } + + std::vector tmp_pieces; + tmp_pieces.reserve(slots.size()); + for (entry::list_type::const_iterator i = slots.begin(); + i != slots.end(); ++i) + { + int index = (int)i->integer(); + if (index >= info.num_pieces() || index < -2) + { + error = "too high index number in slot map (index: " + + boost::lexical_cast(index) + " size: " + + boost::lexical_cast(info.num_pieces()) + ")"; + return; + } + tmp_pieces.push_back(index); + } + + // only bother to check the partial pieces if we have the same block size + // as in the fast resume data. If the blocksize has changed, then throw + // away all partial pieces. + std::vector tmp_unfinished; + int num_blocks_per_piece = (int)rd["blocks per piece"].integer(); + if (num_blocks_per_piece == info.piece_length() / torrent_ptr->block_size()) + { + // the unfinished pieces + + entry::list_type& unfinished = rd["unfinished"].list(); + + tmp_unfinished.reserve(unfinished.size()); + for (entry::list_type::iterator i = unfinished.begin(); + i != unfinished.end(); ++i) + { + piece_picker::downloading_piece p; + + p.index = (int)(*i)["piece"].integer(); + if (p.index < 0 || p.index >= info.num_pieces()) + { + error = "invalid piece index in unfinished piece list (index: " + + boost::lexical_cast(p.index) + " size: " + + boost::lexical_cast(info.num_pieces()) + ")"; + return; + } + + const std::string& bitmask = (*i)["bitmask"].string(); + + const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); + if ((int)bitmask.size() != num_bitmask_bytes) + { + error = "invalid size of bitmask (" + boost::lexical_cast(bitmask.size()) + ")"; + return; + } + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char bits = bitmask[j]; + for (int k = 0; k < 8; ++k) + { + const int bit = j * 8 + k; + if (bits & (1 << k)) + p.finished_blocks[bit] = true; + } + } + + if (p.finished_blocks.count() == 0) continue; + + std::vector::iterator slot_iter + = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index); + if (slot_iter == tmp_pieces.end()) + { + // this piece is marked as unfinished + // but doesn't have any storage + error = "piece " + boost::lexical_cast(p.index) + " is " + "marked as unfinished, but doesn't have any storage"; + return; + } + + assert(*slot_iter == p.index); + int slot_index = static_cast(slot_iter - tmp_pieces.begin()); + unsigned long adler + = torrent_ptr->filesystem().piece_crc( + slot_index + , torrent_ptr->block_size() + , p.finished_blocks); + + const entry& ad = (*i)["adler32"]; + + // crc's didn't match, don't use the resume data + if (ad.integer() != adler) + { + error = "checksum mismatch on piece " + boost::lexical_cast(p.index); + return; + } + + tmp_unfinished.push_back(p); + } + } + + // verify file sizes + + std::vector > file_sizes; + entry::list_type& l = rd["file sizes"].list(); + + for (entry::list_type::iterator i = l.begin(); + i != l.end(); ++i) + { + file_sizes.push_back(std::pair( + i->list().front().integer() + , i->list().back().integer())); + } + + if ((int)tmp_pieces.size() == info.num_pieces() + && std::find_if(tmp_pieces.begin(), tmp_pieces.end() + , boost::bind(std::less(), _1, 0)) == tmp_pieces.end()) + { + if (info.num_files() != (int)file_sizes.size()) + { + error = "the number of files does not match the torrent (num: " + + boost::lexical_cast(file_sizes.size()) + " actual: " + + boost::lexical_cast(info.num_files()) + ")"; + return; + } + + 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 (torrent_info::file_iterator i = info.begin_files() + , end(info.end_files()); i != end; ++i, ++fs) + { + if (i->size != fs->first) + { + error = "file size for '" + i->path.native_file_string() + "' was expected to be " + + boost::lexical_cast(i->size) + " bytes"; + return; + } + } + } + + + if (!match_filesizes(info, save_path, file_sizes, &error)) + return; + + piece_map.swap(tmp_pieces); + unfinished_pieces.swap(tmp_unfinished); + } + catch (invalid_encoding) + { + return; + } + catch (type_error) + { + return; + } + catch (file_error) + { + return; + } + } +}} + diff --git a/library/setup.py b/library/setup.py new file mode 100644 index 000000000..25215c729 --- /dev/null +++ b/library/setup.py @@ -0,0 +1,64 @@ +#/* +#Copyright: A. Zakai ('Kripken') http://6thsenseless.blogspot.com +# +#2006-15-9 +# +#This code is licensed under the terms of the GNU General Public License (GPL), +#version 2 or above; See /usr/share/common-licenses/GPL , or see +#http://www.fsf.org/licensing/licenses/gpl.html +#*/ + +import platform + +pythonVersion = platform.python_version()[0:3] + +print "=========================================" +print "Creating python-libtorrent for Python " + pythonVersion +print "=========================================" + +from distutils.core import setup, Extension + +module1 = Extension('torrent', + include_dirs = ['./include', './include/libtorrent', + '/usr/include/python' + pythonVersion], + libraries = ['boost_filesystem', 'boost_date_time', + 'boost_program_options', 'boost_regex', + 'boost_serialization', 'boost_thread', 'z', 'pthread'], + sources = ['alert.cpp', + 'allocate_resources.cpp', + 'bt_peer_connection.cpp', + 'entry.cpp', + 'escape_string.cpp', + 'file.cpp', + 'http_tracker_connection.cpp', + 'identify_client.cpp', + 'ip_filter.cpp', + 'peer_connection.cpp', + 'piece_picker.cpp', + 'policy.cpp', + 'python-libtorrent.cpp', + 'session.cpp', + 'session_impl.cpp', + 'sha1.cpp', + 'stat.cpp', + 'storage.cpp', + 'torrent.cpp', + 'torrent_handle.cpp', + 'torrent_info.cpp', + 'tracker_manager.cpp', + 'udp_tracker_connection.cpp', + 'web_peer_connection.cpp', + './kademlia/closest_nodes.cpp', + './kademlia/dht_tracker.cpp', + './kademlia/find_data.cpp', + './kademlia/node.cpp', + './kademlia/node_id.cpp', + './kademlia/refresh.cpp', + './kademlia/routing_table.cpp', + './kademlia/rpc_manager.cpp', + './kademlia/traversal_algorithm.cpp']) + +setup (name = 'Python-libtorrent', + version = '0.3.1', + description = 'Wrapper code for libtorrent C++ torrent library (Sourceforge, not Rakshasa)', + ext_modules = [module1]) diff --git a/library/sha1.cpp b/library/sha1.cpp new file mode 100755 index 000000000..1d04b4a17 --- /dev/null +++ b/library/sha1.cpp @@ -0,0 +1,314 @@ +/* +SHA-1 C++ conversion + +original version: + +SHA-1 in C +By Steve Reid +100% Public Domain + +changelog at the end of the file. +*/ + +#include +#include + +// if you don't want boost +// replace with +// #include + +#include +using boost::uint32_t; +using boost::uint8_t; + +#include "libtorrent/config.hpp" + +struct TORRENT_EXPORT SHA1_CTX +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +}; + +TORRENT_EXPORT void SHA1Init(SHA1_CTX* context); +TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len); +TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest); + +namespace +{ + union CHAR64LONG16 + { + uint8_t c[64]; + uint32_t 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 uint32_t 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 uint32_t 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(uint32_t state[5], uint8_t const buffer[64]) + { + using namespace std; + uint32_t a, b, c, d, e; + + CHAR64LONG16* block; + uint8_t 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(SHA1_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(SHA1_CTX* context, uint8_t const* data, uint32_t len) + { + using namespace std; + uint32_t 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() + { + uint32_t test = 1; + return *reinterpret_cast(&test) == 0; + } +} + +// SHA1Init - Initialize new context + +void SHA1Init(SHA1_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 SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len) +{ +#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 SHA1Final(SHA1_CTX* context, uint8_t* digest) +{ + uint8_t finalcount[8]; + + for (uint32_t i = 0; i < 8; ++i) + { + // Endian independent + finalcount[i] = static_cast( + (context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); + } + + SHA1Update(context, (uint8_t const*)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1Update(context, (uint8_t const*)"\0", 1); + SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform() + + for (uint32_t 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 + +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/library/stat.cpp b/library/stat.cpp new file mode 100755 index 000000000..c36bd3725 --- /dev/null +++ b/library/stat.cpp @@ -0,0 +1,91 @@ +/* + +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. + +*/ + +// TODO: Use two algorithms to estimate transfer rate. +// one (simple) for transfer rates that are >= 1 packet +// per second and one (low pass-filter) for rates < 1 +// packet per second. + +#include + +#include "libtorrent/stat.hpp" +#include "libtorrent/invariant_check.hpp" +#include + +#if defined _MSC_VER && _MSC_VER <= 1200 +#define for if (false) {} else for +#endif + +using namespace libtorrent; + +void libtorrent::stat::second_tick(float tick_interval) +{ + INVARIANT_CHECK; + + for (int i = history - 2; i >= 0; --i) + { + m_download_rate_history[i + 1] = m_download_rate_history[i]; + m_upload_rate_history[i + 1] = m_upload_rate_history[i]; + m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i]; + m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i]; + } + + m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol) + / tick_interval; + m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol) + / tick_interval; + m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval; + m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval; + + m_downloaded_payload = 0; + m_uploaded_payload = 0; + m_downloaded_protocol = 0; + m_uploaded_protocol = 0; + + m_mean_download_rate = 0; + m_mean_upload_rate = 0; + m_mean_download_payload_rate = 0; + m_mean_upload_payload_rate = 0; + + for (int i = 0; i < history; ++i) + { + m_mean_download_rate += m_download_rate_history[i]; + m_mean_upload_rate += m_upload_rate_history[i]; + m_mean_download_payload_rate += m_download_payload_rate_history[i]; + m_mean_upload_payload_rate += m_upload_payload_rate_history[i]; + } + + m_mean_download_rate /= history; + m_mean_upload_rate /= history; + m_mean_download_payload_rate /= history; + m_mean_upload_payload_rate /= history; +} diff --git a/library/state.txt b/library/state.txt new file mode 100644 index 000000000..e69de29bb diff --git a/library/storage.cpp b/library/storage.cpp new file mode 100755 index 000000000..33b32bad3 --- /dev/null +++ b/library/storage.cpp @@ -0,0 +1,2172 @@ +/* + +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 +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#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/aux_/session_impl.hpp" + +#ifndef NDEBUG +#include +#include +#include +#include +#endif + +#if defined(_WIN32) && defined(UNICODE) + +#include +#include +#include "libtorrent/utf8.hpp" + +namespace libtorrent +{ + std::wstring safe_convert(std::string const& s) + { + try + { + return libtorrent::utf8_wchar(s); + } + catch (std::exception) + { + std::wstring ret; + for (const char* i = &*s.begin(); i < &*s.end(); ++i) + { + wchar_t c; + c = '.'; + std::mbtowc(&c, i, 1); + ret += c; + } + return ret; + } + } +} + +namespace +{ + using libtorrent::safe_convert; + using namespace boost::filesystem; + + // based on code from Boost.Fileystem + bool create_directories_win(const path& ph) + { + if (ph.empty() || exists(ph)) + { + if ( !ph.empty() && !is_directory(ph) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::create_directories", + ph, "path exists and is not a directory", + not_directory_error ) ); + return false; + } + + // First create branch, by calling ourself recursively + create_directories_win(ph.branch_path()); + // Now that parent's path exists, create the directory + std::wstring wph(safe_convert(ph.native_directory_string())); + CreateDirectory(wph.c_str(), 0); + return true; + } + + bool exists_win( const path & ph ) + { + std::wstring wpath(safe_convert(ph.string())); + if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF) + { + UINT err = ::GetLastError(); + if((err == ERROR_FILE_NOT_FOUND) + || (err == ERROR_INVALID_PARAMETER) + || (err == ERROR_NOT_READY) + || (err == ERROR_PATH_NOT_FOUND) + || (err == ERROR_INVALID_NAME) + || (err == ERROR_BAD_NETPATH )) + return false; // GetFileAttributes failed because the path does not exist + // for any other error we assume the file does exist and fall through, + // this may not be the best policy though... (JM 20040330) + return true; + } + return true; + } + + boost::intmax_t file_size_win( const path & ph ) + { + std::wstring wpath(safe_convert(ph.string())); + // by now, intmax_t is 64-bits on all Windows compilers + WIN32_FILE_ATTRIBUTE_DATA fad; + if ( !::GetFileAttributesExW( wpath.c_str(), + ::GetFileExInfoStandard, &fad ) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::file_size", + ph, detail::system_error_code() ) ); + if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) + boost::throw_exception( filesystem_error( + "boost::filesystem::file_size", + ph, "invalid: is a directory", + is_directory_error ) ); + return (static_cast(fad.nFileSizeHigh) + << (sizeof(fad.nFileSizeLow)*8)) + + fad.nFileSizeLow; + } + + std::time_t last_write_time_win( const path & ph ) + { + struct _stat path_stat; + std::wstring wph(safe_convert(ph.native_file_string())); + if ( ::_wstat( wph.c_str(), &path_stat ) != 0 ) + boost::throw_exception( filesystem_error( + "boost::filesystem::last_write_time", + ph, detail::system_error_code() ) ); + return path_stat.st_mtime; + } + + void rename_win( const path & old_path, + const path & new_path ) + { + std::wstring wold_path(safe_convert(old_path.string())); + std::wstring wnew_path(safe_convert(new_path.string())); + if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::rename", + old_path, new_path, detail::system_error_code() ) ); + } + +} // anonymous namespace + +#endif + +#if BOOST_VERSION < 103200 +bool operator<(boost::filesystem::path const& lhs + , boost::filesystem::path const& rhs) +{ + return lhs.string() < rhs.string(); +} +#endif + +using namespace boost::filesystem; +namespace pt = boost::posix_time; +using boost::bind; +using namespace ::boost::multi_index; +using boost::multi_index::multi_index_container; + +namespace +{ + using namespace libtorrent; + + void print_to_log(const std::string& s) + { + static std::ofstream log("log.txt"); + log << s; + log.flush(); + } +/* + struct file_key + { + file_key(sha1_hash ih, path f): info_hash(ih), file_path(f) {} + file_key() {} + sha1_hash info_hash; + path file_path; + bool operator<(file_key const& fk) const + { + if (info_hash < fk.info_hash) return true; + if (fk.info_hash < info_hash) return false; + return file_path < fk.file_path; + } + }; +*/ + struct lru_file_entry + { + lru_file_entry(boost::shared_ptr const& f) + : file_ptr(f) + , last_use(pt::second_clock::universal_time()) {} + mutable boost::shared_ptr file_ptr; + path file_path; + void* key; + pt::ptime last_use; + file::open_mode mode; + }; + + struct file_pool + { + file_pool(int size): m_size(size) {} + + boost::shared_ptr open_file(void* st, path const& p, file::open_mode m) + { + assert(st != 0); + assert(p.is_complete()); + typedef nth_index::type path_view; + path_view& pt = get<0>(m_files); + path_view::iterator i = pt.find(p); + if (i != pt.end()) + { + lru_file_entry e = *i; + e.last_use = pt::second_clock::universal_time(); + + // if you hit this assert, you probably have more than one + // storage/torrent using the same file at the same time! + assert(e.key == st); + + e.key = st; + if ((e.mode & m) != m) + { + // close the file before we open it with + // the new read/write privilages + i->file_ptr.reset(); + assert(e.file_ptr.unique()); + e.file_ptr.reset(); + e.file_ptr.reset(new file(p, m)); + e.mode = m; + } + pt.replace(i, e); + 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 + typedef nth_index::type lru_view; + lru_view& lt = get<1>(m_files); + lru_view::iterator i = lt.begin(); + // the first entry in this view is the least recently used +/* for (lru_view::iterator i = lt.begin(); i != lt.end(); ++i) + { + std::cerr << i->last_use << "\n"; + } +*/ assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use)); + lt.erase(i); + } + lru_file_entry e(boost::shared_ptr(new file(p, m))); + e.mode = m; + e.key = st; + e.file_path = p; + pt.insert(e); + return e.file_ptr; + } + + void release(void* st) + { + assert(st != 0); + using boost::tie; + + typedef nth_index::type key_view; + key_view& kt = get<2>(m_files); + + key_view::iterator start, end; + tie(start, end) = kt.equal_range(st); + +/* + std::cerr << "releasing files!\n"; + for (path_view::iterator i = r.first; i != r.second; ++i) + { + std::cerr << i->key.file_path.native_file_string() << "\n"; + } +*/ + kt.erase(start, end); +/* + std::cerr << "files left: " << pt.size() << "\n"; + for (path_view::iterator i = pt.begin(); i != pt.end(); ++i) + { + std::cerr << i->key.file_path.native_file_string() << "\n"; + } +*/ + } + + private: + int m_size; + + typedef multi_index_container< + lru_file_entry, indexed_by< + ordered_unique > + , ordered_non_unique > + , ordered_non_unique > + > + > file_set; + + file_set m_files; + }; +} + +namespace libtorrent +{ + + std::vector > get_filesizes( + torrent_info const& t, path p) + { + p = complete(p); + std::vector > sizes; + for (torrent_info::file_iterator i = t.begin_files(); + i != t.end_files(); ++i) + { + size_type size = 0; + std::time_t time = 0; + try + { + path f = p / i->path; +#if defined(_WIN32) && defined(UNICODE) + size = file_size_win(f); + time = last_write_time_win(f); +#else + size = file_size(f); + time = last_write_time(f); +#endif + } + catch (std::exception&) {} + sizes.push_back(std::make_pair(size, time)); + } + return sizes; + } + + bool match_filesizes( + torrent_info const& t + , path p + , std::vector > const& sizes + , std::string* error) + { + if ((int)sizes.size() != t.num_files()) + { + if (error) *error = "mismatching number of files"; + return false; + } + p = complete(p); + + std::vector >::const_iterator s + = sizes.begin(); + for (torrent_info::file_iterator i = t.begin_files(); + i != t.end_files(); ++i, ++s) + { + size_type size = 0; + std::time_t time = 0; + try + { + path f = p / i->path; +#if defined(_WIN32) && defined(UNICODE) + size = file_size_win(f); + time = last_write_time_win(f); +#else + size = file_size(f); + time = last_write_time(f); +#endif + } + catch (std::exception&) {} + if (size != s->first) + { + if (error) *error = "filesize mismatch for file '" + + i->path.native_file_string() + + "', expected to be " + boost::lexical_cast(s->first) + + " bytes"; + return false; + } + if (time != s->second) + { + if (error) *error = "timestamp mismatch for file '" + + i->path.native_file_string() + + "', expected to have modification date " + + boost::lexical_cast(s->second); + return false; + } + } + return true; + } + + struct thread_safe_storage + { + thread_safe_storage(std::size_t n) + : slots(n, false) + {} + + boost::mutex mutex; + boost::condition condition; + std::vector slots; + }; + + struct slot_lock + { + slot_lock(thread_safe_storage& s, int slot_) + : storage_(s) + , slot(slot_) + { + assert(slot_>=0 && slot_ < (int)s.slots.size()); + boost::mutex::scoped_lock lock(storage_.mutex); + + while (storage_.slots[slot]) + storage_.condition.wait(lock); + storage_.slots[slot] = true; + } + + ~slot_lock() + { + storage_.slots[slot] = false; + storage_.condition.notify_all(); + } + + thread_safe_storage& storage_; + int slot; + }; + + class storage::impl : public thread_safe_storage, boost::noncopyable + { + public: + impl(torrent_info const& info, path const& path) + : thread_safe_storage(info.num_pieces()) + , info(info) + { + save_path = complete(path); + assert(save_path.is_complete()); + } + + impl(impl const& x) + : thread_safe_storage(x.info.num_pieces()) + , info(x.info) + , save_path(x.save_path) + {} + + ~impl() + { + files.release(this); + } + + torrent_info const& info; + path save_path; + static file_pool files; + }; + + file_pool storage::impl::files(40); + + storage::storage(torrent_info const& info, path const& path) + : m_pimpl(new impl(info, path)) + { + assert(info.begin_files() != info.end_files()); + } + + void storage::release_files() + { + m_pimpl->files.release(m_pimpl.get()); + } + + void storage::swap(storage& other) + { + m_pimpl.swap(other.m_pimpl); + } + + // returns true on success + bool storage::move_storage(path save_path) + { + path old_path; + path new_path; + + save_path = complete(save_path); + +#if defined(_WIN32) && defined(UNICODE) + std::wstring wsave_path(safe_convert(save_path.native_file_string())); + if (!exists_win(save_path)) + { + CreateDirectory(wsave_path.c_str(), 0); + } + else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + return false; + } +#else + if(!exists(save_path)) + create_directory(save_path); + else if(!is_directory(save_path)) + return false; +#endif + + m_pimpl->files.release(m_pimpl.get()); + + if (m_pimpl->info.num_files() == 1) + { + path single_file = m_pimpl->info.begin_files()->path; + if (single_file.has_branch_path()) + { +#if defined(_WIN32) && defined(UNICODE) + std::wstring wsave_path(safe_convert((save_path / single_file.branch_path()) + .native_directory_string())); + CreateDirectory(wsave_path.c_str(), 0); +#else + create_directory(save_path / single_file.branch_path()); +#endif + } + + old_path = m_pimpl->save_path / single_file; + new_path = save_path / m_pimpl->info.begin_files()->path; + } + else + { + assert(m_pimpl->info.num_files() > 1); + old_path = m_pimpl->save_path / m_pimpl->info.name(); + new_path = save_path / m_pimpl->info.name(); + } + + try + { +#if defined(_WIN32) && defined(UNICODE) + rename_win(old_path, new_path); +#else + rename(old_path, new_path); +#endif + m_pimpl->save_path = save_path; + return true; + } + catch (std::exception&) {} + return false; + } + +#ifndef NDEBUG + + void storage::shuffle() + { + int num_pieces = m_pimpl->info.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_pimpl->info.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 + + size_type storage::read( + char* buf + , int slot + , int offset + , int size) + { + assert(buf != 0); + assert(slot >= 0 && slot < m_pimpl->info.num_pieces()); + assert(offset >= 0); + assert(offset < m_pimpl->info.piece_size(slot)); + assert(size > 0); + + slot_lock lock(*m_pimpl, slot); + +#ifndef NDEBUG + std::vector slices + = m_pimpl->info.map_block(slot, offset, size); + assert(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset; + assert(start + size <= m_pimpl->info.total_size()); + + // find the file iterator and file offset + size_type file_offset = start; + std::vector::const_iterator file_iter; + + for (file_iter = m_pimpl->info.begin_files();;) + { + if (file_offset < file_iter->size) + break; + + file_offset -= file_iter->size; + ++file_iter; + } + + boost::shared_ptr in(m_pimpl->files.open_file( + m_pimpl.get() + , m_pimpl->save_path / file_iter->path + , file::in)); + + assert(file_offset < file_iter->size); + + assert(slices[0].offset == file_offset); + + size_type new_pos = in->seek(file_offset); + if (new_pos != file_offset) + { + // the file was not big enough + throw file_error("slot has no storage"); + } + +#ifndef NDEBUG + size_type in_tell = in->tell(); + assert(in_tell == file_offset); +#endif + + int left_to_read = size; + int slot_size = static_cast(m_pimpl->info.piece_size(slot)); + + if (offset + left_to_read > slot_size) + left_to_read = slot_size - offset; + + assert(left_to_read >= 0); + + size_type result = left_to_read; + int buf_pos = 0; + +#ifndef NDEBUG + int counter = 0; +#endif + + while (left_to_read > 0) + { + int read_bytes = left_to_read; + if (file_offset + read_bytes > file_iter->size) + read_bytes = static_cast(file_iter->size - file_offset); + + if (read_bytes > 0) + { +#ifndef NDEBUG + assert(int(slices.size()) > counter); + size_type slice_size = slices[counter].size; + assert(slice_size == read_bytes); + assert(m_pimpl->info.file_at(slices[counter].file_index).path + == file_iter->path); +#endif + + size_type actual_read = in->read(buf + buf_pos, read_bytes); + + if (read_bytes != actual_read) + { + // the file was not big enough + throw file_error("slot has no storage"); + } + + left_to_read -= read_bytes; + buf_pos += read_bytes; + assert(buf_pos >= 0); + file_offset += read_bytes; + } + + if (left_to_read > 0) + { + ++file_iter; +#ifndef NDEBUG + // empty files are not returned by map_block, so if + // this file was empty, don't increment the slice counter + if (read_bytes > 0) ++counter; +#endif + path path = m_pimpl->save_path / file_iter->path; + + file_offset = 0; + in = m_pimpl->files.open_file( + m_pimpl.get() + , path, file::in); + in->seek(0); + } + } + + return result; + } + + // throws file_error if it fails to write + void storage::write( + const char* buf + , int slot + , int offset + , int size) + { + assert(buf != 0); + assert(slot >= 0); + assert(slot < m_pimpl->info.num_pieces()); + assert(offset >= 0); + assert(size > 0); + + slot_lock lock(*m_pimpl, slot); + +#ifndef NDEBUG + std::vector slices + = m_pimpl->info.map_block(slot, offset, size); + assert(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset; + + // find the file iterator and file offset + size_type file_offset = start; + std::vector::const_iterator file_iter; + + for (file_iter = m_pimpl->info.begin_files();;) + { + if (file_offset < file_iter->size) + break; + + file_offset -= file_iter->size; + ++file_iter; + assert(file_iter != m_pimpl->info.end_files()); + } + + path p(m_pimpl->save_path / file_iter->path); + boost::shared_ptr out = m_pimpl->files.open_file( + m_pimpl.get() + , p, file::out | file::in); + + assert(file_offset < file_iter->size); + assert(slices[0].offset == file_offset); + + size_type pos = out->seek(file_offset); + + if (pos != file_offset) + { + std::stringstream s; + s << "no storage for slot " << slot; + throw file_error(s.str()); + } + + int left_to_write = size; + int slot_size = static_cast(m_pimpl->info.piece_size(slot)); + + if (offset + left_to_write > slot_size) + left_to_write = slot_size - offset; + + assert(left_to_write >= 0); + + int buf_pos = 0; +#ifndef NDEBUG + int counter = 0; +#endif + while (left_to_write > 0) + { + int write_bytes = left_to_write; + if (file_offset + write_bytes > file_iter->size) + { + assert(file_iter->size >= file_offset); + write_bytes = static_cast(file_iter->size - file_offset); + } + + if (write_bytes > 0) + { + assert(int(slices.size()) > counter); + assert(slices[counter].size == write_bytes); + assert(m_pimpl->info.file_at(slices[counter].file_index).path + == file_iter->path); + + assert(buf_pos >= 0); + assert(write_bytes >= 0); + size_type written = out->write(buf + buf_pos, write_bytes); + + if (written != write_bytes) + { + std::stringstream s; + s << "no storage for slot " << slot; + throw file_error(s.str()); + } + + left_to_write -= write_bytes; + buf_pos += write_bytes; + assert(buf_pos >= 0); + file_offset += write_bytes; + assert(file_offset <= file_iter->size); + } + + if (left_to_write > 0) + { + #ifndef NDEBUG + if (write_bytes > 0) ++counter; + #endif + ++file_iter; + + assert(file_iter != m_pimpl->info.end_files()); + path p = m_pimpl->save_path / file_iter->path; + file_offset = 0; + out = m_pimpl->files.open_file( + m_pimpl.get() + , p, file::out | file::in); + + out->seek(0); + } + } + } + + + + + + // -- piece_manager ----------------------------------------------------- + + class piece_manager::impl + { + friend class invariant_access; + public: + + impl( + torrent_info const& info + , path const& path); + + bool check_fastresume( + aux::piece_checker_data& d + , std::vector& pieces + , int& num_pieces + , bool compact_mode); + + std::pair check_files( + std::vector& pieces + , int& num_pieces); + + void release_files(); + + void allocate_slots(int num_slots); + void mark_failed(int index); + unsigned long piece_crc( + int slot_index + , int block_size + , const std::bitset<256>& bitmask); + + int slot_for_piece(int piece_index) const; + + size_type read( + char* buf + , int piece_index + , int offset + , int size); + + void write( + const char* buf + , int piece_index + , int offset + , int size); + + path const& save_path() const + { return m_save_path; } + + bool move_storage(path save_path) + { + if (m_storage.move_storage(save_path)) + { + m_save_path = complete(save_path); + return true; + } + return false; + } + + void export_piece_map(std::vector& p) const; + + // returns the slot currently associated with the given + // piece or assigns the given piece_index to a free slot + + int identify_data( + const std::vector& piece_data + , int current_slot + , std::vector& have_pieces + , int& num_pieces + , const std::multimap& hash_to_piece); + + int allocate_slot_for_piece(int piece_index); +#ifndef NDEBUG + void check_invariant() const; +#ifdef TORRENT_STORAGE_DEBUG + void debug_log() const; +#endif +#endif + storage m_storage; + + // if this is true, pieces are always allocated at the + // lowest possible slot index. If it is false, pieces + // are always written to their final place immediately + bool m_compact_mode; + + // if this is true, pieces that haven't been downloaded + // will be filled with zeroes. Not filling with zeroes + // will not work in some cases (where a seek cannot pass + // the end of the file). + bool m_fill_mode; + + // a bitmask representing the pieces we have + std::vector m_have_piece; + + torrent_info const& m_info; + + // 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; + + path m_save_path; + + mutable boost::recursive_mutex m_mutex; + + bool m_allocating; + boost::mutex m_allocating_monitor; + boost::condition m_allocating_condition; + + // these states are used while checking/allocating the torrent + + enum { + // the default initial state + state_none, + // the file checking is complete + state_finished, + // creating the directories + state_create_files, + // checking the files + state_full_check, + // allocating files (in non-compact mode) + state_allocating + } m_state; + int m_current_slot; + + std::vector m_piece_data; + + // 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; + + // used as temporary piece data storage in allocate_slots + // it is a member in order to avoid allocating it on + // the heap every time a new slot is allocated. (This is quite + // frequent with high download speeds) + std::vector m_scratch_buffer; + }; + + piece_manager::impl::impl( + torrent_info const& info + , path const& save_path) + : m_storage(info, save_path) + , m_compact_mode(false) + , m_fill_mode(true) + , m_info(info) + , m_save_path(complete(save_path)) + , m_allocating(false) + { + assert(m_save_path.is_complete()); + } + + piece_manager::piece_manager( + torrent_info const& info + , path const& save_path) + : m_pimpl(new impl(info, save_path)) + { + } + + piece_manager::~piece_manager() + { + } + + void piece_manager::release_files() + { + m_pimpl->release_files(); + } + + void piece_manager::impl::release_files() + { + m_storage.release_files(); + } + + void piece_manager::impl::export_piece_map( + std::vector& p) const + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + p.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) + { + p.push_back(*i); + } + } + + void piece_manager::export_piece_map( + std::vector& p) const + { + m_pimpl->export_piece_map(p); + } + + void piece_manager::impl::mark_failed(int piece_index) + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[piece_index] >= 0); + + int slot_index = m_piece_to_slot[piece_index]; + + assert(slot_index >= 0); + + m_slot_to_piece[slot_index] = unassigned; + m_piece_to_slot[piece_index] = has_no_slot; + m_free_slots.push_back(slot_index); + } + + void piece_manager::mark_failed(int index) + { + m_pimpl->mark_failed(index); + } + + bool piece_manager::is_allocating() const + { + return m_pimpl->m_state + == impl::state_allocating; + } + + int piece_manager::slot_for_piece(int piece_index) const + { + return m_pimpl->slot_for_piece(piece_index); + } + + int piece_manager::impl::slot_for_piece(int piece_index) const + { + assert(piece_index >= 0 && piece_index < m_info.num_pieces()); + return m_piece_to_slot[piece_index]; + } + + unsigned long piece_manager::piece_crc( + int index + , int block_size + , const std::bitset<256>& bitmask) + { + return m_pimpl->piece_crc(index, block_size, bitmask); + } + + unsigned long piece_manager::impl::piece_crc( + int slot_index + , int block_size + , const std::bitset<256>& bitmask) + { + assert(slot_index >= 0); + assert(slot_index < m_info.num_pieces()); + assert(block_size > 0); + + adler32_crc crc; + std::vector buf(block_size); + int num_blocks = static_cast(m_info.piece_size(slot_index)) / block_size; + int last_block_size = static_cast(m_info.piece_size(slot_index)) % block_size; + if (last_block_size == 0) last_block_size = block_size; + + for (int i = 0; i < num_blocks-1; ++i) + { + if (!bitmask[i]) continue; + m_storage.read( + &buf[0] + , slot_index + , i * block_size + , block_size); + crc.update(&buf[0], block_size); + } + if (bitmask[num_blocks - 1]) + { + m_storage.read( + &buf[0] + , slot_index + , block_size * (num_blocks - 1) + , last_block_size); + crc.update(&buf[0], last_block_size); + } + return crc.final(); + } + + size_type piece_manager::impl::read( + char* buf + , int piece_index + , int offset + , int size) + { + assert(buf); + assert(offset >= 0); + assert(size > 0); + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[piece_index] >= 0 && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size()); + int slot = m_piece_to_slot[piece_index]; + assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); + return m_storage.read(buf, slot, offset, size); + } + + size_type piece_manager::read( + char* buf + , int piece_index + , int offset + , int size) + { + return m_pimpl->read(buf, piece_index, offset, size); + } + + void piece_manager::impl::write( + const char* buf + , int piece_index + , int offset + , int size) + { + assert(buf); + assert(offset >= 0); + assert(size > 0); + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + int slot = allocate_slot_for_piece(piece_index); + assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); + m_storage.write(buf, slot, offset, size); + } + + void piece_manager::write( + const char* buf + , int piece_index + , int offset + , int size) + { + m_pimpl->write(buf, piece_index, offset, size); + } + + int piece_manager::impl::identify_data( + const std::vector& piece_data + , int current_slot + , std::vector& have_pieces + , int& num_pieces + , const std::multimap& hash_to_piece) + { + INVARIANT_CHECK; + + assert((int)have_pieces.size() == m_info.num_pieces()); + + const int piece_size = static_cast(m_info.piece_length()); + const int last_piece_size = static_cast(m_info.piece_size( + m_info.num_pieces() - 1)); + + assert((int)piece_data.size() >= last_piece_size); + + // calculate a small digest, with the same + // size as the last piece. And a large digest + // which has the same size as a normal piece + hasher small_digest; + small_digest.update(&piece_data[0], last_piece_size); + hasher large_digest(small_digest); + assert(piece_size - last_piece_size >= 0); + if (piece_size - last_piece_size > 0) + { + large_digest.update( + &piece_data[last_piece_size] + , piece_size - last_piece_size); + } + sha1_hash large_hash = large_digest.final(); + sha1_hash small_hash = small_digest.final(); + + 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) = hash_to_piece.equal_range(small_hash); + boost::tie(begin2, end2) = 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()) + { + const int piece_index = current_slot; + + if (have_pieces[piece_index]) + { + // we have already found a piece with + // this index. + int other_slot = m_piece_to_slot[piece_index]; + assert(other_slot >= 0); + + // 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 (have_pieces[*i] || *i == piece_index) continue; + other_piece = *i; + break; + } + if (other_piece >= 0) + { + // replace the old slot with 'other_piece' + assert(have_pieces[other_piece] == false); + have_pieces[other_piece] = true; + m_slot_to_piece[other_slot] = other_piece; + m_piece_to_slot[other_piece] = other_slot; + ++num_pieces; + } + 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; + m_free_slots.push_back(other_slot); + } + assert(m_piece_to_slot[piece_index] != current_slot); + assert(m_piece_to_slot[piece_index] >= 0); + m_piece_to_slot[piece_index] = has_no_slot; +#ifndef NDEBUG + // to make the assert happy, a few lines down + have_pieces[piece_index] = false; +#endif + } + else + { + ++num_pieces; + } + + assert(have_pieces[piece_index] == false); + assert(m_piece_to_slot[piece_index] == has_no_slot); + have_pieces[piece_index] = true; + + 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 (have_pieces[*i]) continue; + free_piece = *i; + break; + } + + if (free_piece >= 0) + { + assert(have_pieces[free_piece] == false); + assert(m_piece_to_slot[free_piece] == has_no_slot); + have_pieces[free_piece] = true; + ++num_pieces; + + return free_piece; + } + else + { + assert(free_piece == unassigned); + return unassigned; + } + } + + // 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 + bool piece_manager::impl::check_fastresume( + aux::piece_checker_data& data + , std::vector& pieces + , int& num_pieces, bool compact_mode) + { + assert(m_info.piece_length() > 0); + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + m_compact_mode = compact_mode; + + // This will corrupt the storage + // use while debugging to find + // states that cannot be scanned + // by check_pieces. +// m_storage.shuffle(); + + m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot); + m_slot_to_piece.resize(m_info.num_pieces(), unallocated); + m_free_slots.clear(); + m_unallocated_slots.clear(); + + pieces.clear(); + pieces.resize(m_info.num_pieces(), false); + num_pieces = 0; + + // if we have fast-resume info + // use it instead of doing the actual checking + if (!data.piece_map.empty() + && data.piece_map.size() <= m_slot_to_piece.size()) + { + for (int i = 0; i < (int)data.piece_map.size(); ++i) + { + m_slot_to_piece[i] = data.piece_map[i]; + if (data.piece_map[i] >= 0) + { + m_piece_to_slot[data.piece_map[i]] = i; + int found_piece = data.piece_map[i]; + + // if the piece is not in the unfinished list + // we have all of it + if (std::find_if( + data.unfinished_pieces.begin() + , data.unfinished_pieces.end() + , piece_picker::has_index(found_piece)) + == data.unfinished_pieces.end()) + { + ++num_pieces; + pieces[found_piece] = true; + } + } + else if (data.piece_map[i] == unassigned) + { + m_free_slots.push_back(i); + } + else + { + assert(data.piece_map[i] == unallocated); + m_unallocated_slots.push_back(i); + } + } + + m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size())); + for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i) + { + m_unallocated_slots.push_back(i); + } + + if (!m_compact_mode && !m_unallocated_slots.empty()) + { + m_state = state_allocating; + return false; + } + else + { + m_state = state_finished; + return true; + } + } + + m_state = state_create_files; + return false; + } + + // 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 + std::pair piece_manager::impl::check_files( + std::vector& pieces, int& num_pieces) + { + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + + if (m_state == state_allocating) + { + if (m_compact_mode) + { + m_state = state_finished; + return std::make_pair(true, 1.f); + } + + if (m_unallocated_slots.empty()) + { + m_state = state_finished; + return std::make_pair(true, 1.f); + } + + // if we're not in compact mode, make sure the + // pieces are spread out and placed at their + // final position. + assert(!m_unallocated_slots.empty()); + allocate_slots(1); + + return std::make_pair(false, 1.f - (float)m_unallocated_slots.size() + / (float)m_slot_to_piece.size()); + } + + if (m_state == state_create_files) + { + // first, create all missing directories + path last_path; + for (torrent_info::file_iterator file_iter = m_info.begin_files(), + end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter) + { + path dir = (m_save_path / file_iter->path).branch_path(); + + // if the file is empty, just create it. But also make sure + // the directory exits. + if (dir == last_path + && file_iter->size == 0) + file(m_save_path / file_iter->path, file::out); + + if (dir == last_path) continue; + last_path = dir; + +#if defined(_WIN32) && defined(UNICODE) + if (!exists_win(last_path)) + create_directories_win(last_path); +#else + if (!exists(last_path)) + create_directories(last_path); +#endif + + if (file_iter->size == 0) + file(m_save_path / file_iter->path, file::out); + } + m_current_slot = 0; + m_state = state_full_check; + m_piece_data.resize(int(m_info.piece_length())); + return std::make_pair(false, 0.f); + } + + assert(m_state == state_full_check); + + // ------------------------ + // DO THE FULL CHECK + // ------------------------ + + try + { + + m_storage.read( + &m_piece_data[0] + , m_current_slot + , 0 + , int(m_info.piece_size(m_current_slot))); + + if (m_hash_to_piece.empty()) + { + for (int i = 0; i < m_info.num_pieces(); ++i) + { + m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i)); + } + } + + int piece_index = identify_data( + m_piece_data + , m_current_slot + , pieces + , num_pieces + , m_hash_to_piece); + + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + 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) + { + assert(piece_index != m_current_slot); + + const int other_slot = piece_index; + 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); + assert(i != m_free_slots.end()); + m_free_slots.erase(i); + m_free_slots.push_back(m_current_slot); + } + + const int slot1_size = static_cast(m_info.piece_size(piece_index)); + const int slot2_size = other_piece >= 0 ? static_cast(m_info.piece_size(other_piece)) : 0; + std::vector buf1(slot1_size); + m_storage.read(&buf1[0], m_current_slot, 0, slot1_size); + if (slot2_size > 0) + { + std::vector buf2(slot2_size); + m_storage.read(&buf2[0], piece_index, 0, slot2_size); + m_storage.write(&buf2[0], m_current_slot, 0, slot2_size); + } + m_storage.write(&buf1[0], piece_index, 0, slot1_size); + 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) + { + assert(piece_index != m_current_slot); + + const int other_piece = m_current_slot; + const int other_slot = m_piece_to_slot[other_piece]; + 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 >= 0) m_piece_to_slot[piece_index] = other_slot; + + if (piece_index == unassigned) + { + m_free_slots.push_back(other_slot); + } + + const int slot1_size = static_cast(m_info.piece_size(other_piece)); + const int slot2_size = piece_index >= 0 ? static_cast(m_info.piece_size(piece_index)) : 0; + std::vector buf1(slot1_size); + m_storage.read(&buf1[0], other_slot, 0, slot1_size); + if (slot2_size > 0) + { + std::vector buf2(slot2_size); + m_storage.read(&buf2[0], m_current_slot, 0, slot2_size); + m_storage.write(&buf2[0], other_slot, 0, slot2_size); + } + m_storage.write(&buf1[0], m_current_slot, 0, slot1_size); + 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) + { + assert(piece_index != m_current_slot); + 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]; + + assert(slot1 >= 0); + assert(slot2 >= 0); + assert(piece2 >= 0); + + if (slot1 == slot2) + { + // this means there are only two pieces involved in the swap + 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; + + assert(piece1 == m_current_slot); + assert(piece_index == slot1); + + const int slot1_size = static_cast(m_info.piece_size(piece1)); + const int slot3_size = static_cast(m_info.piece_size(piece_index)); + std::vector buf1(static_cast(slot1_size)); + std::vector buf2(static_cast(slot3_size)); + + m_storage.read(&buf2[0], m_current_slot, 0, slot3_size); + m_storage.read(&buf1[0], slot1, 0, slot1_size); + m_storage.write(&buf1[0], m_current_slot, 0, slot1_size); + m_storage.write(&buf2[0], slot1, 0, slot3_size); + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else + { + assert(slot1 != slot2); + 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 >= 0) m_piece_to_slot[piece1] = slot2; + + if (piece1 == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), slot1); + assert(i != m_free_slots.end()); + m_free_slots.erase(i); + m_free_slots.push_back(slot2); + } + + const int slot1_size = piece1 >= 0 ? static_cast(m_info.piece_size(piece1)) : 0; + const int slot2_size = static_cast(m_info.piece_size(piece2)); + const int slot3_size = static_cast(m_info.piece_size(piece_index)); + + std::vector buf1(static_cast(m_info.piece_length())); + std::vector buf2(static_cast(m_info.piece_length())); + + m_storage.read(&buf2[0], m_current_slot, 0, slot3_size); + m_storage.read(&buf1[0], slot2, 0, slot2_size); + m_storage.write(&buf1[0], m_current_slot, 0, slot2_size); + if (slot1_size > 0) + { + m_storage.read(&buf1[0], slot1, 0, slot1_size); + m_storage.write(&buf1[0], slot2, 0, slot1_size); + } + m_storage.write(&buf2[0], slot1, 0, slot3_size); + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + else + { + assert(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); + assert(m_slot_to_piece[m_current_slot] == unallocated); + 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 + m_free_slots.push_back(m_current_slot); + + m_slot_to_piece[m_current_slot] = piece_index; + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + catch (file_error&) + { + // find the file that failed, and skip all the blocks in that file + size_type file_offset = 0; + size_type current_offset = m_current_slot * m_info.piece_length(); + for (torrent_info::file_iterator i = m_info.begin_files(); + i != m_info.end_files(); ++i) + { + file_offset += i->size; + if (file_offset > current_offset) break; + } + + assert(file_offset > current_offset); + int skip_blocks = static_cast( + (file_offset - current_offset + m_info.piece_length() - 1) + / m_info.piece_length()); + + for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i) + { + assert(m_slot_to_piece[i] == unallocated); + m_unallocated_slots.push_back(i); + } + + // current slot will increase by one at the end of the for-loop too + m_current_slot += skip_blocks - 1; + } + ++m_current_slot; + + if (m_current_slot >= m_info.num_pieces()) + { + assert(m_current_slot == m_info.num_pieces()); + + // clear the memory we've been using + std::vector().swap(m_piece_data); + std::multimap().swap(m_hash_to_piece); + m_state = state_allocating; + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + return std::make_pair(false, 1.f); + } + + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + + return std::make_pair(false, (float)m_current_slot / m_info.num_pieces()); + } + + bool piece_manager::check_fastresume( + aux::piece_checker_data& d, std::vector& pieces + , int& num_pieces, bool compact_mode) + { + return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode); + } + + std::pair piece_manager::check_files( + std::vector& pieces + , int& num_pieces) + { + return m_pimpl->check_files(pieces, num_pieces); + } + + int piece_manager::impl::allocate_slot_for_piece(int piece_index) + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + // INVARIANT_CHECK; + + assert(piece_index >= 0); + assert(piece_index < (int)m_piece_to_slot.size()); + 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) + { + assert(slot_index >= 0); + assert(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + if (m_free_slots.empty()) + { + allocate_slots(1); + 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()) + { + assert(m_slot_to_piece[piece_index] != unassigned); + 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_info.num_pieces() - 1 && piece_index != *iter) + { + if (m_free_slots.size() == 1) + allocate_slots(1); + 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); + + 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(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) + 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]; + 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]); + + const int slot_size = static_cast(m_info.piece_size(slot_index)); + std::vector buf(slot_size); + m_storage.read(&buf[0], piece_index, 0, slot_size); + m_storage.write(&buf[0], slot_index, 0, slot_size); + + assert(m_slot_to_piece[piece_index] == piece_index); + assert(m_piece_to_slot[piece_index] == piece_index); + + slot_index = piece_index; + +#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) + debug_log(); +#endif + } + + assert(slot_index >= 0); + assert(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + namespace + { + // this is used to notify potential other + // threads that the allocation-function has exited + struct allocation_syncronization + { + allocation_syncronization( + bool& flag + , boost::condition& cond + , boost::mutex& monitor) + : m_flag(flag) + , m_cond(cond) + , m_monitor(monitor) + { + boost::mutex::scoped_lock lock(m_monitor); + + while (m_flag) + m_cond.wait(lock); + + m_flag = true; + } + + ~allocation_syncronization() + { + boost::mutex::scoped_lock lock(m_monitor); + m_flag = false; + m_cond.notify_one(); + } + + bool& m_flag; + boost::condition& m_cond; + boost::mutex& m_monitor; + }; + + } + + void piece_manager::impl::allocate_slots(int num_slots) + { + assert(num_slots > 0); + + // this object will syncronize the allocation with + // potential other threads + allocation_syncronization sync_obj( + m_allocating + , m_allocating_condition + , m_allocating_monitor); + + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + // INVARIANT_CHECK; + + assert(!m_unallocated_slots.empty()); + + const int piece_size = static_cast(m_info.piece_length()); + + std::vector& buffer = m_scratch_buffer; + buffer.resize(piece_size); + + for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) + { + int pos = m_unallocated_slots.front(); + // int piece_pos = pos; + bool write_back = false; + + int new_free_slot = pos; + if (m_piece_to_slot[pos] != has_no_slot) + { + assert(m_piece_to_slot[pos] >= 0); + m_storage.read(&buffer[0], m_piece_to_slot[pos], 0, static_cast(m_info.piece_size(pos))); + new_free_slot = m_piece_to_slot[pos]; + m_slot_to_piece[pos] = pos; + m_piece_to_slot[pos] = pos; + write_back = 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 (write_back || m_fill_mode) + m_storage.write(&buffer[0], pos, 0, static_cast(m_info.piece_size(pos))); + } + + assert(m_free_slots.size() > 0); + } + + void piece_manager::allocate_slots(int num_slots) + { + m_pimpl->allocate_slots(num_slots); + } + + path const& piece_manager::save_path() const + { + return m_pimpl->save_path(); + } + + bool piece_manager::move_storage(path const& save_path) + { + return m_pimpl->move_storage(save_path); + } + +#ifndef NDEBUG + void piece_manager::impl::check_invariant() const + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + if (m_piece_to_slot.empty()) return; + + assert((int)m_piece_to_slot.size() == m_info.num_pieces()); + assert((int)m_slot_to_piece.size() == m_info.num_pieces()); + + for (std::vector::const_iterator i = m_free_slots.begin(); + i != m_free_slots.end(); ++i) + { + assert(*i < (int)m_slot_to_piece.size()); + assert(*i >= 0); + assert(m_slot_to_piece[*i] == unassigned); + 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) + { + assert(*i < (int)m_slot_to_piece.size()); + assert(*i >= 0); + assert(m_slot_to_piece[*i] == unallocated); + assert(std::find(i+1, m_unallocated_slots.end(), *i) + == m_unallocated_slots.end()); + } + + for (int i = 0; i < m_info.num_pieces(); ++i) + { + // Check domain of piece_to_slot's elements + if (m_piece_to_slot[i] != has_no_slot) + { + assert(m_piece_to_slot[i] >= 0); + 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) + { + assert(m_slot_to_piece[i] >= 0); + 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) + { + assert(m_slot_to_piece[m_piece_to_slot[i]] == i); + if (m_piece_to_slot[i] != i) + { + assert(m_slot_to_piece[i] == unallocated); + } + } + else + { + assert(m_piece_to_slot[i] == has_no_slot); + } + + // do more detailed checks on slot_to_piece + + if (m_slot_to_piece[i] >= 0) + { + assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[m_slot_to_piece[i]] == i); +#ifdef TORRENT_STORAGE_DEBUG + assert( + std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) == m_unallocated_slots.end() + ); + 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 + 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 + assert( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) != m_free_slots.end() + ); +#endif + } + else + { + assert(false && "m_slot_to_piece[i] is invalid"); + } + } + } + +#ifdef TORRENT_STORAGE_DEBUG + void piece_manager::impl::debug_log() const + { + std::stringstream s; + + s << "index\tslot\tpiece\n"; + + for (int i = 0; i < m_info.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/library/test.py b/library/test.py new file mode 100644 index 000000000..5c0532dcc --- /dev/null +++ b/library/test.py @@ -0,0 +1,24 @@ +#/* +#Copyright: A. Zakai ('Kripken') http://6thsenseless.blogspot.com +# +#2006-15-9 +# +#This code is licensed under the terms of the GNU General Public License (GPL), +#version 2 or above; See /usr/share/common-licenses/GPL , or see +#http://www.fsf.org/licensing/licenses/gpl.html +#*/ + + +import torrent +from time import sleep + +torrent.init() + +myTorrent = torrent.addTorrent("ubuntu.torrent") + +while True: + print "STATE:" + print torrent.getState(myTorrent) + print "" + + sleep(1) diff --git a/library/testit b/library/testit new file mode 100755 index 000000000..dea5ec9d1 --- /dev/null +++ b/library/testit @@ -0,0 +1,5 @@ +echo "" +echo "Run!" +echo "" +#LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH python test.py +python test.py diff --git a/library/torrent.cpp b/library/torrent.cpp new file mode 100755 index 000000000..39936ffca --- /dev/null +++ b/library/torrent.cpp @@ -0,0 +1,2186 @@ +/* + +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 +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/session.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/peer.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +using namespace libtorrent; +using namespace boost::posix_time; +using boost::tuples::tuple; +using boost::tuples::get; +using boost::tuples::make_tuple; +using boost::filesystem::complete; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +// PROFILING CODE + +#ifdef TORRENT_PROFILE +#include + +namespace libtorrent +{ + namespace + { + using boost::posix_time::ptime; + using boost::posix_time::time_duration; + using boost::posix_time::microsec_clock; + std::vector > checkpoints; + } + + void add_checkpoint(std::string const& str) + { + checkpoints.push_back(std::make_pair(microsec_clock::universal_time(), str)); + } + + void print_checkpoints() + { + for (std::vector >::iterator i + = checkpoints.begin(); i != checkpoints.end(); ++i) + { + ptime cur = i->first; + if (i + 1 != checkpoints.end()) + { + time_duration diff = (i + 1)->first - cur; + std::cout << diff.total_microseconds() << " " << i->second << "\n"; + } + else + { + std::cout << " " << i->second << "\n"; + } + } + } +} + +#endif + +namespace +{ + + enum + { + // wait 60 seconds before retrying a failed tracker + tracker_retry_delay_min = 60 + // when tracker_failed_max trackers + // has failed, wait 10 minutes instead + , tracker_retry_delay_max = 10 * 60 + , tracker_failed_max = 5 + }; + + int calculate_block_size(const torrent_info& i, int default_block_size) + { + if (default_block_size < 1024) default_block_size = 1024; + + // if pieces are too small, adjust the block size + if (i.piece_length() < default_block_size) + { + return static_cast(i.piece_length()); + } + + // if pieces are too large, adjust the block size + if (i.piece_length() / default_block_size > piece_picker::max_blocks_per_piece) + { + return static_cast(i.piece_length() / piece_picker::max_blocks_per_piece); + } + + // otherwise, go with the default + return default_block_size; + } + + struct find_peer_by_ip + { + find_peer_by_ip(tcp::endpoint const& a, const torrent* t) + : ip(a) + , tor(t) + { assert(t != 0); } + + bool operator()(const session_impl::connection_map::value_type& c) const + { + tcp::endpoint sender = c.first->remote_endpoint(); + if (sender.address() != ip.address()) return false; + if (tor != c.second->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()(const std::pair& p) const + { + if (p.second->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 (std::count(pid.begin(), pid.end(), 0) == 20) return false; + return true; + } + + peer_id const& pid; + }; +} + +namespace libtorrent +{ + torrent::torrent( + session_impl& ses + , aux::checker_impl& checker + , torrent_info const& tf + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s) + : m_torrent_file(tf) + , m_abort(false) + , m_paused(false) + , m_just_paused(false) + , m_event(tracker_request::started) + , m_block_size(0) + , m_storage(0) + , m_next_request(second_clock::universal_time()) + , m_duration(1800) + , m_complete(-1) + , m_incomplete(-1) + , m_host_resolver(ses.m_selector) +#ifndef TORRENT_DISABLE_DHT + , m_dht_announce_timer(ses.m_selector) +#endif + , m_policy() + , m_ses(ses) + , m_checker(checker) + , m_picker(0) + , m_trackers(m_torrent_file.trackers()) + , m_last_working_tracker(-1) + , m_currently_trying_tracker(0) + , m_failed_trackers(0) + , m_time_scaler(0) + , m_priority(.5) + , m_num_pieces(0) + , m_got_tracker_response(false) + , m_ratio(0.f) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_net_interface(net_interface.address(), 0) + , m_upload_bandwidth_limit(std::numeric_limits::max()) + , m_download_bandwidth_limit(std::numeric_limits::max()) + , m_save_path(complete(save_path)) + , m_compact_mode(compact_mode) + , m_metadata_progress(0) + , m_metadata_size(0) + , m_default_block_size(block_size) + , m_connections_initialized(true) + , m_settings(s) + { +#ifndef NDEBUG + m_initial_done = 0; +#endif + INVARIANT_CHECK; + + m_uploads_quota.min = 2; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); + + m_dl_bandwidth_quota.min = 100; + m_dl_bandwidth_quota.max = resource_request::inf; + + if (m_ses.m_download_rate == -1) + { + m_dl_bandwidth_quota.given = resource_request::inf; + } + else + { + m_dl_bandwidth_quota.given = 400; + } + + m_ul_bandwidth_quota.min = 100; + m_ul_bandwidth_quota.max = resource_request::inf; + + if (m_ses.m_upload_rate == -1) + { + m_ul_bandwidth_quota.given = resource_request::inf; + } + else + { + m_ul_bandwidth_quota.given = 400; + } + + m_policy.reset(new policy(this)); + init(); + +#ifndef TORRENT_DISABLE_DHT + if (!tf.priv()) + { + m_dht_announce_timer.expires_from_now(seconds(10)); + m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); + } +#endif + } + + torrent::torrent( + session_impl& ses + , aux::checker_impl& checker + , char const* tracker_url + , sha1_hash const& info_hash + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s) + : m_torrent_file(info_hash) + , m_abort(false) + , m_paused(false) + , m_just_paused(false) + , m_event(tracker_request::started) + , m_block_size(0) + , m_storage(0) + , m_next_request(second_clock::universal_time()) + , m_duration(1800) + , m_complete(-1) + , m_incomplete(-1) + , m_host_resolver(ses.m_selector) +#ifndef TORRENT_DISABLE_DHT + , m_dht_announce_timer(ses.m_selector) +#endif + , m_policy() + , m_ses(ses) + , m_checker(checker) + , m_picker(0) + , m_last_working_tracker(-1) + , m_currently_trying_tracker(0) + , m_failed_trackers(0) + , m_time_scaler(0) + , m_priority(.5) + , m_num_pieces(0) + , m_got_tracker_response(false) + , m_ratio(0.f) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_net_interface(net_interface.address(), 0) + , m_upload_bandwidth_limit(std::numeric_limits::max()) + , m_download_bandwidth_limit(std::numeric_limits::max()) + , m_save_path(complete(save_path)) + , m_compact_mode(compact_mode) + , m_metadata_progress(0) + , m_metadata_size(0) + , m_default_block_size(block_size) + , m_connections_initialized(false) + , m_settings(s) + { +#ifndef NDEBUG + m_initial_done = 0; +#endif + INVARIANT_CHECK; + + m_uploads_quota.min = 2; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); + + m_dl_bandwidth_quota.min = 100; + m_dl_bandwidth_quota.max = resource_request::inf; + + if (m_ses.m_download_rate == -1) + { + m_dl_bandwidth_quota.given = resource_request::inf; + } + else + { + m_dl_bandwidth_quota.given = 400; + } + + m_ul_bandwidth_quota.min = 100; + m_ul_bandwidth_quota.max = resource_request::inf; + + + if (m_ses.m_upload_rate == -1) + { + m_ul_bandwidth_quota.given = resource_request::inf; + } + else + { + m_ul_bandwidth_quota.given = 400; + } + + m_trackers.push_back(announce_entry(tracker_url)); + m_requested_metadata.resize(256, 0); + + m_policy.reset(new policy(this)); + m_torrent_file.add_tracker(tracker_url); +#ifndef TORRENT_DISABLE_DHT + m_dht_announce_timer.expires_from_now(seconds(10)); + m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); +#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. + + assert(m_connections.empty()); + + INVARIANT_CHECK; + + if (m_ses.is_aborted()) + m_abort = true; + if (!m_connections.empty()) + disconnect_all(); + } + + void torrent::init() + { + INVARIANT_CHECK; + + assert(m_torrent_file.is_valid()); + assert(m_torrent_file.num_files() > 0); + assert(m_torrent_file.total_size() >= 0); + + m_have_pieces.resize(m_torrent_file.num_pieces(), false); + m_storage.reset(new piece_manager(m_torrent_file, m_save_path)); + m_block_size = calculate_block_size(m_torrent_file, m_default_block_size); + m_picker.reset(new piece_picker( + static_cast(m_torrent_file.piece_length() / m_block_size) + , static_cast((m_torrent_file.total_size()+m_block_size-1)/m_block_size))); + + std::vector const& url_seeds = m_torrent_file.url_seeds(); + std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds + , m_web_seeds.begin())); + } + + void torrent::use_interface(const char* net_interface) + { + INVARIANT_CHECK; + + m_net_interface = tcp::endpoint(address::from_string(net_interface), 0); + } + +#ifndef TORRENT_DISABLE_DHT + + 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(asio::error const& e) + { + if (e) return; + m_dht_announce_timer.expires_from_now(boost::posix_time::minutes(30)); + m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); + if (!m_ses.m_dht) return; + // TODO: There should be a way to abort an announce operation on the dht. + // when the torrent is destructed + boost::weak_ptr self(shared_from_this()); + m_ses.m_dht->announce(m_torrent_file.info_hash() + , m_ses.m_listen_interface.port() + , bind(&torrent::on_dht_announce_response_disp, self, _1)); + } + + void torrent::on_dht_announce_response(std::vector const& peers) + { + std::for_each(peers.begin(), peers.end(), bind( + &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0))); + } + +#endif + + // returns true if it is time for this torrent to make another + // tracker request + bool torrent::should_request() + { + INVARIANT_CHECK; + + if (m_torrent_file.trackers().empty()) return false; + + if (m_just_paused) + { + m_just_paused = false; + return true; + } + return !m_paused && + m_next_request < second_clock::universal_time(); + } + + void torrent::tracker_warning(std::string const& msg) + { + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post(alert::warning)) + { + m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), msg)); + } + } + + void torrent::tracker_response( + tracker_request const& + , std::vector& peer_list + , int interval + , int complete + , int incomplete) + { + INVARIANT_CHECK; + + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + m_failed_trackers = 0; + // less than 5 minutes announce intervals + // are insane. + if (interval < 60 * 5) interval = 60 * 5; + + m_last_working_tracker + = prioritize_tracker(m_currently_trying_tracker); + m_currently_trying_tracker = 0; + + m_duration = interval; + m_next_request = second_clock::universal_time() + boost::posix_time::seconds(m_duration); + + if (complete >= 0) m_complete = complete; + if (incomplete >= 0) m_incomplete = incomplete; + + // connect to random peers from the list + std::random_shuffle(peer_list.begin(), peer_list.end()); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::stringstream s; + s << "TRACKER RESPONSE:\n" + "interval: " << m_duration << "\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"; + } + 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; + + tcp::endpoint a(address::from_string(i->ip), i->port); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log("blocked ip from tracker: " + i->ip); +#endif + continue; + } + + m_policy->peer_from_tracker(a, i->pid); + } + + if (m_ses.m_alerts.should_post(alert::info)) + { + std::stringstream s; + s << "Got response from tracker: " + << m_trackers[m_last_working_tracker].url; + m_ses.m_alerts.post_alert(tracker_reply_alert( + get_handle(), s.str())); + } + m_got_tracker_response = true; + } + + 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; + + assert(m_picker.get()); + + if (m_torrent_file.num_pieces() == 0) + return 0; + const int last_piece = m_torrent_file.num_pieces() - 1; + + size_type total_done + = m_num_pieces * 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_have_pieces[last_piece]) + { + int corr = m_torrent_file.piece_size(last_piece) + - m_torrent_file.piece_length(); + total_done += corr; + } + return total_done; + } + + // the first value is the total number of bytes downloaded + // the second value is the number of bytes of those that haven't + // been filtered as not wanted we have downloaded + tuple torrent::bytes_done() const + { + INVARIANT_CHECK; + + if (!valid_metadata()) return tuple(0,0); + + assert(m_picker.get()); + + if (m_torrent_file.num_pieces() == 0) + return tuple(0,0); + const int last_piece = m_torrent_file.num_pieces() - 1; + + size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered()) + * m_torrent_file.piece_length(); + + size_type total_done + = m_num_pieces * 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_have_pieces[last_piece]) + { + int corr = m_torrent_file.piece_size(last_piece) + - m_torrent_file.piece_length(); + total_done += corr; + if (!m_picker->is_filtered(last_piece)) + wanted_done += corr; + } + + const std::vector& dl_queue + = m_picker->get_download_queue(); + + const int blocks_per_piece = static_cast( + m_torrent_file.piece_length() / m_block_size); + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + int corr = 0; + assert(!m_have_pieces[i->index]); + + for (int j = 0; j < blocks_per_piece; ++j) + { + corr += (i->finished_blocks[j]) * m_block_size; + } + + // correction if this was the last piece + // and if we have the last block + if (i->index == last_piece + && i->finished_blocks[m_picker->blocks_in_last_piece()-1]) + { + corr -= m_block_size; + corr += m_torrent_file.piece_size(last_piece) % m_block_size; + } + total_done += corr; + if (!m_picker->is_filtered(i->index)) + wanted_done += corr; + } + + std::map downloading_piece; + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection* pc = i->second; + boost::optional p + = pc->downloading_piece_progress(); + if (p) + { + if (m_have_pieces[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; + } + assert(p->bytes_downloaded <= p->full_block_bytes); + } + } + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + total_done += i->second; + if (!m_picker->is_filtered(i->first.piece_index)) + wanted_done += i->second; + } + return make_tuple(total_done, wanted_done); + } + + void torrent::piece_failed(int index) + { + INVARIANT_CHECK; + + assert(m_storage.get()); + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + if (m_ses.m_alerts.should_post(alert::info)) + { + std::stringstream s; + s << "hash for piece " << index << " failed"; + m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str())); + } + // increase the total amount of failed bytes + m_total_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())); + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + p->second->received_invalid_data(); + + // either, we have received too many failed hashes + // or this was the only peer that sent us this piece. + // TODO: make this a changable setting + if (p->second->trust_points() <= -7 || peers.size() == 1) + { + // we don't trust this peer anymore + // ban it. + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_ban_alert( + p->first + , get_handle() + , "banning peer because of too many corrupt pieces")); + } + m_policy->ban_peer(*p->second); + +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n"; +#endif + p->second->disconnect(); + } + } + + // 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 + // TODO: do this more intelligently! and keep track + // of how much crap (data that failed hash-check) and + // how much redundant data we have downloaded + // if some clients has sent more than one piece + // start with redownloading the pieces that the client + // that has sent the least number of pieces + m_picker->restore_piece(index); + m_storage->mark_failed(index); + + assert(m_have_pieces[index] == false); + } + + void torrent::abort() + { + INVARIANT_CHECK; + + m_abort = true; + // if the torrent is paused, it doesn't need + // to announce with even=stopped again. + if (!m_paused) + m_event = tracker_request::stopped; + // disconnect all peers and close all + // files belonging to the torrents + disconnect_all(); + if (m_storage.get()) m_storage->release_files(); + } + + void torrent::announce_piece(int index) + { + INVARIANT_CHECK; + + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_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())); + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + p->second->received_valid_data(); + } + + m_picker->we_have(index); + for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + i->second->announce_piece(index); + } + + std::string torrent::tracker_login() const + { + if (m_username.empty() && m_password.empty()) return ""; + return m_username + ":" + m_password; + } + + void torrent::filter_piece(int index, bool filter) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + // TODO: update peer's interesting-bit + + if (filter) m_picker->mark_as_filtered(index); + else m_picker->mark_as_unfiltered(index); + } + + void torrent::filter_pieces(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + + // TODO: update peer's interesting-bit + + std::vector state; + state.reserve(100); + int index = 0; + for (std::vector::const_iterator i = bitmask.begin() + , end(bitmask.end()); i != end; ++i, ++index) + { + if (m_picker->is_filtered(index) == *i) continue; + if (*i) + m_picker->mark_as_filtered(index); + else + state.push_back(index); + } + + for (std::vector::reverse_iterator i = state.rbegin(); + i != state.rend(); ++i) + { + m_picker->mark_as_unfiltered(*i); + } + } + + bool torrent::is_piece_filtered(int index) const + { + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + return m_picker->is_filtered(index); + } + + void torrent::filtered_pieces(std::vector& bitmask) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + 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()) return; + + // the bitmask need to have exactly one bit for every file + // in the 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.file_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) + { + assert(!urls.empty()); + m_trackers = urls; + if (m_currently_trying_tracker >= (int)m_trackers.size()) + m_currently_trying_tracker = (int)m_trackers.size()-1; + m_last_working_tracker = -1; + } + + tracker_request torrent::generate_tracker_request() + { + INVARIANT_CHECK; + + m_next_request + = second_clock::universal_time() + + boost::posix_time::seconds(tracker_retry_delay_max); + + 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(); + req.uploaded = m_stat.total_payload_upload(); + req.left = bytes_left(); + if (req.left == -1) req.left = 16*1024; + req.event = m_event; + + if (m_event != tracker_request::stopped) + m_event = tracker_request::none; + req.url = m_trackers[m_currently_trying_tracker].url; + assert(m_connections_quota.given > 0); + req.num_want = std::max( + (m_connections_quota.given + - m_policy->num_peers()), 10); + // if we are aborting. we don't want any new peers + if (req.event == tracker_request::stopped) + req.num_want = 0; + + // default initialize, these should be set by caller + // before passing the request to the tracker_manager + req.listen_port = 0; + req.key = 0; + + return req; + } + + void torrent::remove_peer(peer_connection* p) try + { + INVARIANT_CHECK; + + assert(p != 0); + + peer_iterator i = m_connections.find(p->remote()); + if (i == m_connections.end()) return; + + if (ready_for_connections()) + { + assert(p->associated_torrent().lock().get() == this); + + std::vector piece_list; + const std::vector& pieces = p->get_bitfield(); + + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) piece_list.push_back(static_cast(i - pieces.begin())); + } + + for (std::vector::reverse_iterator i = piece_list.rbegin(); + i != piece_list.rend(); ++i) + { + peer_lost(*i); + } + } + + m_policy->connection_closed(*p); + m_connections.erase(i); +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::string err = e.what(); +#endif + assert(false); + }; + + void torrent::connect_to_url_seed(std::string const& url) + { + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string now(to_simple_string(second_clock::universal_time())); + (*m_ses.m_logger) << now << " resolving: " << url << "\n"; +#endif + + std::string protocol; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, hostname, port, path) + = parse_url_components(url); + + m_resolving_web_seeds.insert(url); + if (m_ses.settings().proxy_ip.empty()) + { + tcp::resolver::query q(hostname, boost::lexical_cast(port)); + m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup + , shared_from_this(), _1, _2, url)); + } + else + { + // use proxy + tcp::resolver::query q(m_ses.settings().proxy_ip + , boost::lexical_cast(m_ses.settings().proxy_port)); + m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup + , shared_from_this(), _1, _2, url)); + } + + } + + void torrent::on_name_lookup(asio::error const& e, tcp::resolver::iterator host + , std::string url) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string now(to_simple_string(second_clock::universal_time())); + (*m_ses.m_logger) << now << " completed resolve: " << url << "\n"; +#endif + + std::set::iterator i = m_resolving_web_seeds.find(url); + if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "HTTP seed hostname lookup failed: " << e.what(); + m_ses.m_alerts.post_alert( + url_seed_alert(url, msg.str())); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; +#endif + + // the name lookup failed for the http host. Don't try + // this host again + remove_url_seed(url); + return; + } + + if (m_ses.is_aborted()) return; + + tcp::endpoint a(host->endpoint()); + + boost::shared_ptr s(new stream_socket(m_ses.m_selector)); + boost::intrusive_ptr c(new web_peer_connection( + m_ses, shared_from_this(), s, a, url)); +#ifndef NDEBUG + c->m_in_constructor = false; +#endif + + try + { + m_ses.m_connection_queue.push_back(c); + + assert(m_connections.find(a) == m_connections.end()); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + // add the newly connected peer to this torrent's peer list + m_connections.insert( + std::make_pair(a, boost::get_pointer(c))); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + + m_ses.process_connection_queue(); + } + catch (std::exception& e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; +#endif + + // TODO: post an error alert! + std::map::iterator i = m_connections.find(a); + if (i != m_connections.end()) m_connections.erase(i); + m_ses.connection_failed(s, a, e.what()); + c->disconnect(); + } + } + catch (std::exception& exc) + { + assert(false); + }; + + peer_connection& torrent::connect_to_peer(const tcp::endpoint& a) + { + INVARIANT_CHECK; + + if (m_connections.find(a) != m_connections.end()) + throw protocol_error("already connected to peer"); + + boost::shared_ptr s(new stream_socket(m_ses.m_selector)); + boost::intrusive_ptr c(new bt_peer_connection( + m_ses, shared_from_this(), s, a)); +#ifndef NDEBUG + c->m_in_constructor = false; +#endif + + try + { + m_ses.m_connection_queue.push_back(c); + + assert(m_connections.find(a) == m_connections.end()); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + // add the newly connected peer to this torrent's peer list + m_connections.insert( + std::make_pair(a, boost::get_pointer(c))); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + + m_ses.process_connection_queue(); + } + catch (std::exception& e) + { + // TODO: post an error alert! + std::map::iterator i = m_connections.find(a); + if (i != m_connections.end()) m_connections.erase(i); + m_ses.connection_failed(s, a, e.what()); + c->disconnect(); + throw; + } + if (c->is_disconnecting()) throw protocol_error("failed to connect"); + return *c; + } + + void torrent::attach_peer(peer_connection* p) + { + INVARIANT_CHECK; + + assert(p != 0); + assert(!p->is_local()); + + std::map::iterator c + = m_connections.find(p->remote()); + if (c != m_connections.end()) + { + // we already have a peer_connection to this ip. + // It may currently be waiting for completing a + // connection attempt that might fail. So, + // prioritize this current connection since + // it has already succeeded. + if (!c->second->is_connecting()) + { + throw protocol_error("already connected to peer"); + } + c->second->disconnect(); + } + + if (m_ses.m_connections.find(p->get_socket()) + == m_ses.m_connections.end()) + { + throw protocol_error("peer is not properly constructed"); + } + + if (m_ses.is_aborted()) + { + throw protocol_error("session is closing"); + } + + peer_iterator i = m_connections.insert( + std::make_pair(p->remote(), p)).first; + + try + { + // if new_connection throws, we have to remove the + // it from the list. + + m_policy->new_connection(*i->second); + } + catch (std::exception& e) + { + m_connections.erase(i); + throw; + } +#ifndef NDEBUG + assert(p->remote() == p->get_socket()->remote_endpoint()); +#endif + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + } + + void torrent::disconnect_all() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + while (!m_connections.empty()) + { + peer_connection& p = *m_connections.begin()->second; + assert(p.associated_torrent().lock().get() == this); + +#if defined(TORRENT_VERBOSE_LOGGING) + if (m_abort) + (*p.m_logger) << "*** CLOSING CONNECTION 'aborting'\n"; + else + (*p.m_logger) << "*** CLOSING CONNECTION 'pausing'\n"; +#endif +#ifndef NDEBUG + std::size_t size = m_connections.size(); +#endif + p.disconnect(); + assert(m_connections.size() <= size); + } + } + + // called when torrent is finished (all interested pieces downloaded) + void torrent::finished() + { + INVARIANT_CHECK; + + if (alerts().should_post(alert::info)) + { + alerts().post_alert(torrent_finished_alert( + get_handle() + , "torrent has finished downloading")); + } + + // disconnect all seeds + std::vector seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + assert(i->second->associated_torrent().lock().get() == this); + if (i->second->is_seed()) + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*i->second->m_logger) << "*** SEED, CLOSING CONNECTION\n"; +#endif + seeds.push_back(i->second); + } + } + std::for_each(seeds.begin(), seeds.end() + , bind(&peer_connection::disconnect, _1)); + + m_storage->release_files(); + } + + // called when torrent is complete (all pieces downloaded) + void torrent::completed() + { + INVARIANT_CHECK; + +/* + if (alerts().should_post(alert::info)) + { + alerts().post_alert(torrent_complete_alert( + get_handle() + , "torrent is complete")); + } +*/ + // make the next tracker request + // be a completed-event + m_event = tracker_request::completed; + force_tracker_request(); + } + + // 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; + + assert(index >= 0); + if (index >= (int)m_trackers.size()) return (int)m_trackers.size()-1; + + while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) + { + std::swap(m_trackers[index].url, m_trackers[index-1].url); + --index; + } + return index; + } + + void torrent::try_next_tracker() + { + INVARIANT_CHECK; + + using namespace boost::posix_time; + ++m_currently_trying_tracker; + + if ((unsigned)m_currently_trying_tracker >= m_trackers.size()) + { + int delay = tracker_retry_delay_min + + std::min(m_failed_trackers, (int)tracker_failed_max) + * (tracker_retry_delay_max - tracker_retry_delay_min) + / tracker_failed_max; + + ++m_failed_trackers; + // if we've looped the tracker list, wait a bit before retrying + m_currently_trying_tracker = 0; + m_next_request = second_clock::universal_time() + seconds(delay); + } + else + { + // don't delay before trying the next tracker + m_next_request = second_clock::universal_time(); + } + + } + + bool torrent::check_fastresume(aux::piece_checker_data& data) + { + INVARIANT_CHECK; + + if (!m_storage.get()) + { + // this means we have received the metadata through the + // metadata extension, and we have to initialize + init(); + } + + assert(m_storage.get()); + bool done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces + , m_compact_mode); +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + return done; + } + + std::pair torrent::check_files() + { + INVARIANT_CHECK; + + assert(m_storage.get()); + std::pair progress = m_storage->check_files(m_have_pieces, m_num_pieces); + +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + return progress; + } + + void torrent::files_checked(std::vector const& + unfinished_pieces) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + m_picker->files_checked(m_have_pieces, unfinished_pieces); + if (!m_connections_initialized) + { + m_connections_initialized = true; + // all peer connections have to initialize themselves now that the metadata + // is available + typedef std::map conn_map; + for (conn_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + try { i->second->init(); ++i;} + catch (std::exception& e) + { + // the connection failed, close it + conn_map::iterator j = i; + ++j; + m_ses.connection_failed(i->second->get_socket() + , i->first, e.what()); + i = j; + } + } + } +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + } + + alert_manager& torrent::alerts() const + { + return m_ses.m_alerts; + } + + boost::filesystem::path torrent::save_path() const + { + return m_save_path; + } + + bool torrent::move_storage(boost::filesystem::path const& save_path) + { + INVARIANT_CHECK; + + bool ret = true; + if (m_storage.get()) + { + ret = m_storage->move_storage(save_path); + m_save_path = m_storage->save_path(); + } + else + { + m_save_path = save_path; + } + return ret; + } + + piece_manager& torrent::filesystem() + { + INVARIANT_CHECK; + + assert(m_storage.get()); + return *m_storage; + } + + + torrent_handle torrent::get_handle() const + { + INVARIANT_CHECK; + + return torrent_handle(&m_ses, 0, m_torrent_file.info_hash()); + } + + session_settings const& torrent::settings() const + { + INVARIANT_CHECK; + + return m_ses.settings(); + } + +#ifndef NDEBUG + void torrent::check_invariant() const + { +// size_type download = m_stat.total_payload_download(); +// size_type done = boost::get<0>(bytes_done()); +// assert(download >= done - m_initial_done); + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection const& p = *i->second; + torrent* associated_torrent = p.associated_torrent().lock().get(); + if (associated_torrent != this) + assert(false); + } + +// This check is very expensive. +// assert(m_num_pieces +// == std::count(m_have_pieces.begin(), m_have_pieces.end(), true)); + assert(m_priority >= 0.f && m_priority < 1.f); + assert(!valid_metadata() || m_block_size > 0); + assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0); + } +#endif + + void torrent::set_sequenced_download_threshold(int threshold) + { + if (valid_metadata()) + picker().set_sequenced_download_threshold(threshold); + } + + + void torrent::set_max_uploads(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + m_uploads_quota.max = std::max(m_uploads_quota.min, limit); + } + + void torrent::set_max_connections(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + m_connections_quota.max = std::max(m_connections_quota.min, limit); + } + + void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) + { + assert(limit >= -1); + peer_connection* p = connection_for(ip); + if (p == 0) return; + p->set_upload_limit(limit); + } + + void torrent::set_peer_download_limit(tcp::endpoint ip, int limit) + { + assert(limit >= -1); + peer_connection* p = connection_for(ip); + if (p == 0) return; + p->set_download_limit(limit); + } + + void torrent::set_upload_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_upload_bandwidth_limit = limit; + } + + void torrent::set_download_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_download_bandwidth_limit = limit; + } + + void torrent::pause() + { + INVARIANT_CHECK; + + if (m_paused) return; + disconnect_all(); + m_paused = true; + // tell the tracker that we stopped + m_event = tracker_request::stopped; + m_just_paused = true; + // this will make the storage close all + // files and flush all cached data + if (m_storage.get()) m_storage->release_files(); + } + + void torrent::resume() + { + INVARIANT_CHECK; + + if (!m_paused) return; + m_paused = false; + + // tell the tracker that we're back + m_event = tracker_request::started; + force_tracker_request(); + + // make pulse be called as soon as possible + m_time_scaler = 0; + } + + void torrent::second_tick(stat& accumulator, float tick_interval) + { + INVARIANT_CHECK; + + m_connections_quota.used = (int)m_connections.size(); + m_uploads_quota.used = m_policy->num_uploads(); + + m_ul_bandwidth_quota.used = 0; + m_ul_bandwidth_quota.max = 0; + m_ul_bandwidth_quota.min = 0; + + m_dl_bandwidth_quota.used = 0; + m_dl_bandwidth_quota.min = 0; + m_dl_bandwidth_quota.max = 0; + + if (m_paused) + { + // let the stats fade out to 0 + m_stat.second_tick(tick_interval); + return; + } + + // ---- WEB SEEDS ---- + + // if we're a seed, we don't need to connect to any web-seed + if (!is_seed() && !m_web_seeds.empty()) + { + // keep trying web-seeds if there are any + // first find out which web seeds we are connected to + std::set web_seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + web_peer_connection* p + = dynamic_cast(i->second); + if (!p) continue; + web_seeds.insert(p->url()); + } + + for (std::set::iterator i = m_resolving_web_seeds.begin() + , end(m_resolving_web_seeds.end()); i != end; ++i) + web_seeds.insert(web_seeds.begin(), *i); + + // from the list of available web seeds, subtract the ones we are + // already connected to. + std::vector not_connected_web_seeds; + std::set_difference(m_web_seeds.begin(), m_web_seeds.end(), web_seeds.begin() + , web_seeds.end(), std::back_inserter(not_connected_web_seeds)); + + // connect to all of those that we aren't connected to + std::for_each(not_connected_web_seeds.begin(), not_connected_web_seeds.end() + , bind(&torrent::connect_to_url_seed, this, _1)); + } + + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + peer_connection* p = i->second; + m_stat += p->statistics(); + // updates the peer connection's ul/dl bandwidth + // resource requests + p->second_tick(tick_interval); + + m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used; + m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min; + m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used; + m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min; + + m_ul_bandwidth_quota.max = saturated_add( + m_ul_bandwidth_quota.max + , p->m_ul_bandwidth_quota.max); + + m_dl_bandwidth_quota.max = saturated_add( + m_dl_bandwidth_quota.max + , p->m_dl_bandwidth_quota.max); + } + + m_ul_bandwidth_quota.max + = std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit); + + if (m_upload_bandwidth_limit == resource_request::inf) + m_ul_bandwidth_quota.max = resource_request::inf; + + m_dl_bandwidth_quota.max + = std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit); + + if (m_download_bandwidth_limit == resource_request::inf) + m_dl_bandwidth_quota.max = resource_request::inf; + + accumulator += m_stat; + m_stat.second_tick(tick_interval); + } + + void torrent::distribute_resources() + { + INVARIANT_CHECK; + + m_time_scaler--; + if (m_time_scaler <= 0) + { + m_time_scaler = 10; + m_policy->pulse(); + } + + assert(m_ul_bandwidth_quota.given >= 0); + assert(m_dl_bandwidth_quota.given >= 0); + + // distribute allowed upload among the peers + allocate_resources(m_ul_bandwidth_quota.given + , m_connections + , &peer_connection::m_ul_bandwidth_quota); + + // distribute allowed download among the peers + allocate_resources(m_dl_bandwidth_quota.given + , m_connections + , &peer_connection::m_dl_bandwidth_quota); + + using boost::bind; + + // tell all peers to reset their used quota. This is + // a new second and they can again use up their quota + + for (std::map::iterator i + = m_connections.begin(); i != m_connections.end(); ++i) + { + i->second->reset_upload_quota(); + assert(i->second->m_dl_bandwidth_quota.used + <= i->second->m_dl_bandwidth_quota.given); + } + } + + bool torrent::verify_piece(int piece_index) + { + INVARIANT_CHECK; + + assert(m_storage.get()); + assert(piece_index >= 0); + assert(piece_index < m_torrent_file.num_pieces()); + assert(piece_index < (int)m_have_pieces.size()); + + int size = static_cast(m_torrent_file.piece_size(piece_index)); + std::vector buffer(size); + assert(size > 0); + m_storage->read(&buffer[0], piece_index, 0, size); + + hasher h; + h.update(&buffer[0], size); + sha1_hash digest = h.final(); + + if (m_torrent_file.hash_for_piece(piece_index) != digest) + return false; + + if (!m_have_pieces[piece_index]) + m_num_pieces++; + m_have_pieces[piece_index] = true; + + assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0) + == m_num_pieces); + return true; + } + + const tcp::endpoint& torrent::current_tracker() const + { + return m_tracker_address; + } + + bool torrent::is_allocating() const + { return m_storage.get() && m_storage->is_allocating(); } + + std::vector const& torrent::metadata() const + { + INVARIANT_CHECK; + + if (m_metadata.empty()) + { + bencode(std::back_inserter(m_metadata) + , m_torrent_file.create_info_metadata()); + + assert(hasher(&m_metadata[0], m_metadata.size()).final() + == m_torrent_file.info_hash()); + } + assert(!m_metadata.empty()); + return m_metadata; + } + + void torrent::file_progress(std::vector& fp) const + { + assert(valid_metadata()); + + fp.clear(); + fp.resize(m_torrent_file.num_files(), 0.f); + + for (int i = 0; i < m_torrent_file.num_files(); ++i) + { + peer_request ret = m_torrent_file.map_file(i, 0, 0); + size_type size = m_torrent_file.file_at(i).size; + +// zero sized files are considered +// 100% done all the time + if (size == 0) + { + fp[i] = 1.f; + continue; + } + + size_type done = 0; + while (size > 0) + { + size_type bytes_step = std::min(m_torrent_file.piece_size(ret.piece) + - ret.start, size); + if (m_have_pieces[ret.piece]) done += bytes_step; + ++ret.piece; + ret.start = 0; + size -= bytes_step; + } + assert(size == 0); + + fp[i] = static_cast(done) / m_torrent_file.file_at(i).size; + } + } + + torrent_status torrent::status() const + { + INVARIANT_CHECK; + + assert(std::accumulate( + m_have_pieces.begin() + , m_have_pieces.end() + , 0) == m_num_pieces); + + torrent_status st; + + st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end(), + boost::bind(std::logical_not(), boost::bind(&peer_connection::is_connecting, + boost::bind(&std::map::value_type::second, _1)))); + + st.num_complete = m_complete; + st.num_incomplete = m_incomplete; + st.paused = m_paused; + boost::tie(st.total_done, st.total_wanted_done) = bytes_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(); + + st.next_announce = next_announce() + - second_clock::universal_time(); + if (st.next_announce.is_negative()) st.next_announce + = boost::posix_time::seconds(0); + st.announce_interval = boost::posix_time::seconds(m_duration); + + if (m_last_working_tracker >= 0) + { + st.current_tracker + = m_trackers[m_last_working_tracker].url; + } + + // if we don't have any metadata, stop here + + if (!valid_metadata()) + { + if (m_got_tracker_response == false) + st.state = torrent_status::connecting_to_tracker; + else + st.state = torrent_status::downloading_metadata; + + if (m_metadata_size == 0) st.progress = 0.f; + else st.progress = std::min(1.f, m_metadata_progress / (float)m_metadata_size); + + st.block_size = 0; + + return st; + } + + st.block_size = block_size(); + + // fill in status that depends on metadata + + st.total_wanted = m_torrent_file.total_size(); + + if (m_picker.get() && (m_picker->num_filtered() > 0 + || m_picker->num_have_filtered() > 0)) + { + int filtered_pieces = m_picker->num_filtered() + + m_picker->num_have_filtered(); + int last_piece_index = m_torrent_file.num_pieces() - 1; + if (m_picker->is_filtered(last_piece_index)) + { + st.total_wanted -= m_torrent_file.piece_size(last_piece_index); + --filtered_pieces; + } + + st.total_wanted -= filtered_pieces * m_torrent_file.piece_length(); + } + + assert(st.total_wanted >= st.total_wanted_done); + + if (st.total_wanted == 0) st.progress = 1.f; + else st.progress = st.total_wanted_done + / static_cast(st.total_wanted); + + st.pieces = &m_have_pieces; + st.num_pieces = m_num_pieces; + + if (m_got_tracker_response == false) + st.state = torrent_status::connecting_to_tracker; + else if (m_num_pieces == (int)m_have_pieces.size()) + st.state = torrent_status::seeding; + else if (st.total_wanted_done == st.total_wanted) + st.state = torrent_status::finished; + else + st.state = torrent_status::downloading; + + st.num_seeds = num_seeds(); + st.distributed_copies = m_picker->distributed_copies(); + return st; + } + + int torrent::num_seeds() const + { + INVARIANT_CHECK; + + return (int)std::count_if(m_connections.begin(), m_connections.end(), + boost::bind(&peer_connection::is_seed, + boost::bind(&std::map::value_type::second, _1))); + } + + int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + + std::pair req_to_offset(std::pair req, int total_size) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.second <= 256); + 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); + + assert(start >= 0); + assert(size > 0); + assert(start <= 256); + assert(start + size <= 256); + + // assert the identity of this function +#ifndef NDEBUG + std::pair identity = req_to_offset(ret, total_size); + assert(offset == identity); +#endif + return ret; + } + + bool torrent::received_metadata(char const* buf, int size, int offset, int total_size) + { + INVARIANT_CHECK; + + if (valid_metadata()) return false; + + if ((int)m_metadata.size() < total_size) + m_metadata.resize(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); + + 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; + + hasher h; + h.update(&m_metadata[0], (int)m_metadata.size()); + sha1_hash info_hash = h.final(); + + if (info_hash != m_torrent_file.info_hash()) + { + std::fill( + m_have_metadata.begin() + , m_have_metadata.begin() + req.first + req.second + , false); + m_metadata_progress = 0; + m_metadata_size = 0; + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(metadata_failed_alert( + get_handle(), "invalid metadata received from swarm")); + } + + return false; + } + + entry metadata = bdecode(m_metadata.begin(), m_metadata.end()); + m_torrent_file.parse_info_section(metadata); + + { + boost::mutex::scoped_lock(m_checker.m_mutex); + + boost::shared_ptr d( + new aux::piece_checker_data); + d->torrent_ptr = shared_from_this(); + d->save_path = m_save_path; + d->info_hash = m_torrent_file.info_hash(); + // add the torrent to the queue to be checked + m_checker.m_torrents.push_back(d); + typedef session_impl::torrent_map torrent_map; + torrent_map::iterator i = m_ses.m_torrents.find( + m_torrent_file.info_hash()); + assert(i != m_ses.m_torrents.end()); + m_ses.m_torrents.erase(i); + // and notify the thread that it got another + // job in its queue + m_checker.m_cond.notify_one(); + } + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle(), "metadata successfully received from swarm")); + } + + // clear the storage for the bitfield + std::vector().swap(m_have_metadata); + std::vector().swap(m_requested_metadata); + + return true; + } + + std::pair torrent::metadata_request() + { + INVARIANT_CHECK; + + // count the number of peers that supports the + // extension and that has metadata + int peers = 0; + typedef std::map conn_map; + for (conn_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + bt_peer_connection* c = dynamic_cast(i->second); + if (c == 0) continue; + if (!c->supports_extension( + extended_metadata_message)) + continue; + if (!c->has_metadata()) + continue; + ++peers; + } + + // the number of blocks to request + int num_blocks = 256 / (peers + 1); + if (num_blocks < 1) num_blocks = 1; + 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]++; + + assert(ret.first >= 0); + assert(ret.second > 0); + assert(ret.second <= 256); + assert(ret.first + ret.second <= 256); + + return ret; + } + + void torrent::cancel_metadata_request(std::pair req) + { + INVARIANT_CHECK; + + for (int i = req.first; i < req.first + req.second; ++i) + { + assert(m_requested_metadata[i] > 0); + if (m_requested_metadata[i] > 0) + --m_requested_metadata[i]; + } + } + + void torrent::tracker_request_timed_out( + tracker_request const&) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log("*** tracker timed out"); +#endif + + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream s; + s << "tracker: \"" + << m_trackers[m_currently_trying_tracker].url + << "\" timed out"; + m_ses.m_alerts.post_alert(tracker_alert(get_handle() + , m_failed_trackers + 1, 0, s.str())); + } + try_next_tracker(); + } + + // 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& + , int response_code, const std::string& str) + { + INVARIANT_CHECK; + + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log(std::string("*** tracker error: ") + str); +#endif + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream s; + s << "tracker: \"" + << m_trackers[m_currently_trying_tracker].url + << "\" " << str; + m_ses.m_alerts.post_alert(tracker_alert(get_handle() + , m_failed_trackers + 1, response_code, s.str())); + } + + try_next_tracker(); + } + + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + void torrent::debug_log(const std::string& line) + { + (*m_ses.m_logger) << line << "\n"; + } +#endif + + void torrent::metadata_progress(int total_size, int received) + { + m_metadata_progress += received; + m_metadata_size = total_size; + } + +} + diff --git a/library/torrent_handle.cpp b/library/torrent_handle.cpp new file mode 100755 index 000000000..b8fe6f415 --- /dev/null +++ b/library/torrent_handle.cpp @@ -0,0 +1,729 @@ +/* + +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 +#include +#include +#include +#include +#include +#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/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" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std +{ + using ::srand; + using ::isalnum; +}; +#endif + +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + namespace + { + void throw_invalid_handle() + { + throw invalid_handle(); + } + + template + Ret call_member( + session_impl* ses + , aux::checker_impl* chk + , sha1_hash const& hash + , F f) + { + if (ses == 0) throw_invalid_handle(); + + if (chk) + { + mutex::scoped_lock l(chk->m_mutex); + aux::piece_checker_data* d = chk->find_torrent(hash); + if (d != 0) return f(*d->torrent_ptr); + } + + { + session_impl::mutex_t::scoped_lock l(ses->m_mutex); + boost::shared_ptr t = ses->find_torrent(hash).lock(); + if (t) return f(*t); + } + + throw invalid_handle(); + } + } + +#ifndef NDEBUG + + void torrent_handle::check_invariant() const + { + assert((m_ses == 0 && m_chk == 0) || (m_ses != 0)); + } + +#endif + + void torrent_handle::set_max_uploads(int max_uploads) const + { + INVARIANT_CHECK; + + assert(max_uploads >= 2 || max_uploads == -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_max_uploads, _1, max_uploads)); + } + + void torrent_handle::use_interface(const char* net_interface) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::use_interface, _1, net_interface)); + } + + void torrent_handle::set_max_connections(int max_connections) const + { + INVARIANT_CHECK; + + assert(max_connections >= 2 || max_connections == -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_max_connections, _1, max_connections)); + } + + void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const + { + INVARIANT_CHECK; + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_peer_upload_limit, _1, ip, limit)); + } + + void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const + { + INVARIANT_CHECK; + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_peer_download_limit, _1, ip, limit)); + } + + void torrent_handle::set_upload_limit(int limit) const + { + INVARIANT_CHECK; + + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_upload_limit, _1, limit)); + } + + void torrent_handle::set_download_limit(int limit) const + { + INVARIANT_CHECK; + + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_download_limit, _1, limit)); + } + + bool torrent_handle::move_storage( + boost::filesystem::path const& save_path) const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::move_storage, _1, save_path)); + } + + bool torrent_handle::has_metadata() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::valid_metadata, _1)); + } + + bool torrent_handle::is_seed() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_seed, _1)); + } + + bool torrent_handle::is_paused() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_paused, _1)); + } + + void torrent_handle::pause() const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::pause, _1)); + } + + void torrent_handle::resume() const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::resume, _1)); + } + + void torrent_handle::set_tracker_login(std::string const& name + , std::string const& password) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_tracker_login, _1, name, password)); + } + + void torrent_handle::file_progress(std::vector& progress) + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + if (!d->processing) + { + torrent_info const& info = d->torrent_ptr->torrent_file(); + progress.clear(); + progress.resize(info.num_files(), 0.f); + return; + } + d->torrent_ptr->file_progress(progress); + return; + } + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (t) return t->file_progress(progress); + } + + throw_invalid_handle(); + } + + torrent_status torrent_handle::status() const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + torrent_status st; + + if (d->processing) + { + if (d->torrent_ptr->is_allocating()) + st.state = torrent_status::allocating; + else + st.state = torrent_status::checking_files; + } + else + st.state = torrent_status::queued_for_checking; + st.progress = d->progress; + st.paused = d->torrent_ptr->is_paused(); + return st; + } + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (t) return t->status(); + } + + throw_invalid_handle(); + return torrent_status(); + } + + void torrent_handle::set_sequenced_download_threshold(int threshold) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_sequenced_download_threshold, _1, threshold)); + } + + void torrent_handle::filter_piece(int index, bool filter) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_piece, _1, index, filter)); + } + + void torrent_handle::filter_pieces(std::vector const& pieces) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_pieces, _1, pieces)); + } + + bool torrent_handle::is_piece_filtered(int index) const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_piece_filtered, _1, index)); + } + + std::vector torrent_handle::filtered_pieces() const + { + INVARIANT_CHECK; + std::vector ret; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filtered_pieces, _1, boost::ref(ret))); + return ret; + } + + void torrent_handle::filter_files(std::vector const& files) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_files, _1, files)); + } + + std::vector const& torrent_handle::trackers() const + { + INVARIANT_CHECK; + + return call_member const&>(m_ses + , m_chk, m_info_hash, bind(&torrent::trackers, _1)); + } + + void torrent_handle::add_url_seed(std::string const& url) + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::add_url_seed, _1, url)); + } + + void torrent_handle::replace_trackers( + std::vector const& urls) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::replace_trackers, _1, urls)); + } + + const torrent_info& torrent_handle::get_torrent_info() const + { + INVARIANT_CHECK; + + if (!has_metadata()) throw_invalid_handle(); + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::torrent_file, _1)); + } + + bool torrent_handle::is_valid() const + { + INVARIANT_CHECK; + + if (m_ses == 0) return false; + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) return true; + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::weak_ptr t = m_ses->find_torrent(m_info_hash); + if (!t.expired()) return true; + } + + return false; + } + + entry torrent_handle::write_resume_data() const + { + INVARIANT_CHECK; + + std::vector piece_index; + if (m_ses == 0) return entry(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) return entry(); + + if (!t->valid_metadata()) return entry(); + + t->filesystem().export_piece_map(piece_index); + + entry ret(entry::dictionary_t); + + ret["file-format"] = "libtorrent resume file"; + ret["file-version"] = 1; + + const sha1_hash& info_hash = t->torrent_file().info_hash(); + ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); + + ret["slots"] = entry(entry::list_t); + entry::list_type& slots = ret["slots"].list(); + std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots)); + + const piece_picker& p = t->picker(); + + const std::vector& q + = p.get_download_queue(); + + // blocks per piece + int num_blocks_per_piece = + static_cast(t->torrent_file().piece_length()) / t->block_size(); + ret["blocks per piece"] = num_blocks_per_piece; + + // 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_blocks.count() == 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; + for (int k = 0; k < 8; ++k) + v |= i->finished_blocks[j*8+k]?(1 << k):0; + bitmask.insert(bitmask.end(), v); + } + piece_struct["bitmask"] = bitmask; + + assert(t->filesystem().slot_for_piece(i->index) >= 0); + unsigned long adler + = t->filesystem().piece_crc( + t->filesystem().slot_for_piece(i->index) + , t->block_size() + , i->finished_blocks); + + piece_struct["adler32"] = adler; + + // push the struct onto the unfinished-piece list + up.push_back(piece_struct); + } + + // write local peers + + ret["peers"] = entry::list_type(); + entry::list_type& peer_list = ret["peers"].list(); + + policy& pol = t->get_policy(); + + for (policy::iterator i = pol.begin_peer() + , end(pol.end_peer()); i != end; ++i) + { + // 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 (i->type == policy::peer::not_connectable + || i->banned) continue; + + tcp::endpoint ip = i->ip; + entry peer(entry::dictionary_t); + peer["ip"] = ip.address().to_string(); + peer["port"] = ip.port(); + peer_list.push_back(peer); + } + + std::vector > file_sizes + = get_filesizes(t->torrent_file(), t->save_path()); + + ret["file sizes"] = entry::list_type(); + entry::list_type& fl = ret["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 ret; + } + + + boost::filesystem::path torrent_handle::save_path() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::save_path, _1)); + } + + std::vector const& torrent_handle::metadata() const + { + INVARIANT_CHECK; + + return call_member const&>(m_ses, m_chk, m_info_hash + , bind(&torrent::metadata, _1)); + } + + void torrent_handle::connect_peer(tcp::endpoint const& adr) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + + if (!t) + { + // the torrent is being checked. Add the peer to its + // peer list. The entries in there will be connected + // once the checking is complete. + mutex::scoped_lock l2(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d == 0) throw_invalid_handle(); + d->peers.push_back(adr); + return; + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + t->get_policy().peer_from_tracker(adr, id); + } + + void torrent_handle::force_reannounce( + boost::posix_time::time_duration duration) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) throw_invalid_handle(); + + using boost::posix_time::second_clock; + t->force_tracker_request(second_clock::universal_time() + + duration); + } + + void torrent_handle::force_reannounce() const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) throw_invalid_handle(); + + t->force_tracker_request(); + } + + void torrent_handle::set_ratio(float ratio) const + { + INVARIANT_CHECK; + + assert(ratio >= 0.f); + + if (ratio < 1.f && ratio > 0.f) + ratio = 1.f; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_ratio, _1, ratio)); + } + + void torrent_handle::get_peer_info(std::vector& v) const + { + INVARIANT_CHECK; + + v.clear(); + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) return; + + for (torrent::const_peer_iterator i = t->begin(); + i != t->end(); ++i) + { + peer_connection* peer = i->second; + + // 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); + } + } + + bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const + { + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) return false; + + for (torrent::const_peer_iterator i = t->begin(); + i != t->end(); ++i) + { + peer_connection* peer = i->second; + + // peers that haven't finished the handshake should + // not be included in this list + if (peer->associated_torrent().expired()) continue; + + tcp::endpoint sender = peer->get_socket()->remote_endpoint(); + // loop until we find the required ip tcp::endpoint + if (ip != sender) continue; + + bt_peer_connection* p = dynamic_cast(peer); + if (!p) return false; + + // peers that don's support chat message extension + // should not be included either + if (!p->supports_extension(extended_chat_message)) + return false; + + // send the message + p->write_chat_message(message); + return true; + } + return false; + } + + void torrent_handle::get_download_queue(std::vector& queue) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + + queue.clear(); + if (!t) return; + if (!t->valid_metadata()) return; + + const piece_picker& p = t->picker(); + + const std::vector& q + = p.get_download_queue(); + + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i) + { + partial_piece_info pi; + pi.finished_blocks = i->finished_blocks; + pi.requested_blocks = i->requested_blocks; + for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j) + { + pi.peer[j] = i->info[j].peer; + pi.num_downloads[j] = i->info[j].num_downloads; + } + pi.piece_index = i->index; + pi.blocks_in_piece = p.blocks_in_piece(i->index); + queue.push_back(pi); + } + } + +} + diff --git a/library/torrent_info.cpp b/library/torrent_info.cpp new file mode 100755 index 000000000..6b008369b --- /dev/null +++ b/library/torrent_info.cpp @@ -0,0 +1,833 @@ +/* + +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 +#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_info.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" + +using namespace libtorrent; +using namespace boost::filesystem; + +namespace +{ + void convert_to_utf8(std::string& str, unsigned char chr) + { + str += 0xc0 | ((chr & 0xff) >> 6); + str += 0x80 | (chr & 0x3f); + } + + void verify_encoding(file_entry& target) + { + std::string tmp_path; + std::string file_path = target.path.string(); + bool valid_encoding = true; + for (std::string::iterator i = file_path.begin() + , end(file_path.end()); i != end; ++i) + { + // valid ascii-character + if ((*i & 0x80) == 0) + { + tmp_path += *i; + continue; + } + + if (std::distance(i, end) < 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 (std::distance(i, end) < 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 (std::distance(i, end) < 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.orig_path.reset(new path(target.path)); + target.path = tmp_path; + } + } + + void extract_single_file(const entry& dict, file_entry& target + , std::string const& root_dir) + { + target.size = dict["length"].integer(); + target.path = root_dir; + + + // prefer the name.utf-8 + // because if it exists, it is more + // likely to be correctly encoded + + const entry::list_type* list = 0; + if (entry const* p = dict.find_key("path.utf-8")) + { + list = &p->list(); + } + else + { + list = &dict["path"].list(); + } + + for (entry::list_type::const_iterator i = list->begin(); + i != list->end(); ++i) + { + if (i->string() != "..") + target.path /= i->string(); + } + verify_encoding(target); + if (target.path.is_complete()) throw std::runtime_error("torrent contains " + "a file with an absolute path: '" + + target.path.native_file_string() + "'"); + } + + void extract_files(const entry::list_type& list, std::vector& target + , std::string const& root_dir) + { + size_type offset = 0; + for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i) + { + target.push_back(file_entry()); + extract_single_file(*i, target.back(), root_dir); + target.back().offset = offset; + offset += target.back().size; + } + } + + void remove_dir(path& p) + { + assert(p.begin() != p.end()); + path tmp; + for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i) + tmp /= *i; + p = tmp; + } +} + +namespace libtorrent +{ + + using namespace boost::gregorian; + using namespace boost::posix_time; + + // standard constructor that parses a torrent file + torrent_info::torrent_info(const entry& torrent_file) + : m_creation_date(date(not_a_date_time)) + , m_multifile(false) + , m_private(false) + , m_extra_info(entry::dictionary_t) + { + try + { + read_torrent_info(torrent_file); + } + catch(type_error&) + { + throw invalid_torrent_file(); + } + } + + // 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) + : m_piece_length(256 * 1024) + , m_total_size(0) + , m_info_hash(info_hash) + , m_name() + , m_creation_date(second_clock::universal_time()) + , m_multifile(false) + , m_extra_info(entry::dictionary_t) + { + } + + torrent_info::torrent_info() + : m_piece_length(256 * 1024) + , m_total_size(0) + , m_info_hash(0) + , m_name() + , m_creation_date(second_clock::universal_time()) + , m_multifile(false) + , m_extra_info(entry::dictionary_t) + { + } + + torrent_info::~torrent_info() + {} + + void torrent_info::set_piece_size(int size) + { + // make sure the size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (size & (1 << i)) + { + assert((size & ~(1 << i)) == 0); + break; + } + } +#endif + m_piece_length = size; + + + int num_pieces = static_cast( + (m_total_size + m_piece_length - 1) / m_piece_length); + int old_num_pieces = static_cast(m_piece_hash.size()); + + m_piece_hash.resize(num_pieces); + for (int i = old_num_pieces; i < num_pieces; ++i) + { + m_piece_hash[i].clear(); + } + } + + void torrent_info::parse_info_section(entry const& info) + { + // encode the info-field in order to calculate it's sha1-hash + std::vector buf; + bencode(std::back_inserter(buf), info); + hasher h; + h.update(&buf[0], (int)buf.size()); + m_info_hash = h.final(); + + // extract piece length + m_piece_length = (int)info["piece length"].integer(); + if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0"); + + // extract file name (or the directory name if it's a multifile libtorrent) + if (entry const* e = info.find_key("name.utf-8")) + { m_name = e->string(); } + else + { m_name = info["name"].string(); } + + path tmp = m_name; + if (tmp.is_complete()) throw std::runtime_error("torrent contains " + "a file with an absolute path: '" + m_name + "'"); + if (tmp.has_branch_path()) throw std::runtime_error( + "torrent contains name with directories: '" + m_name + "'"); + + // extract file list + entry const* i = info.find_key("files"); + if (i == 0) + { + // if there's no list of files, there has to be a length + // field. + file_entry e; + e.path = m_name; + e.offset = 0; + e.size = info["length"].integer(); + m_files.push_back(e); + } + else + { + extract_files(i->list(), m_files, m_name); + m_multifile = true; + } + + // calculate total size of all pieces + m_total_size = 0; + for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i) + m_total_size += i->size; + + // extract sha-1 hashes for all pieces + // we want this division to round upwards, that's why we have the + // extra addition + + int num_pieces = static_cast((m_total_size + m_piece_length - 1) / m_piece_length); + m_piece_hash.resize(num_pieces); + const std::string& hash_string = info["pieces"].string(); + + if ((int)hash_string.length() != num_pieces * 20) + throw invalid_torrent_file(); + + for (int i = 0; i < num_pieces; ++i) + std::copy( + hash_string.begin() + i*20 + , hash_string.begin() + (i+1)*20 + , m_piece_hash[i].begin()); + + for (entry::dictionary_type::const_iterator i = info.dict().begin() + , end(info.dict().end()); i != end; ++i) + { + if (i->first == "pieces" + || i->first == "piece length" + || i->first == "length") + continue; + m_extra_info[i->first] = i->second; + } + + if (entry const* priv = info.find_key("private")) + { + if (priv->type() != entry::int_t + || priv->integer() != 0) + { + // this key exists and it's not 0. + // consider the torrent private + m_private = true; + } + } + +#ifndef NDEBUG + std::vector info_section_buf; + entry gen_info_section = create_info_metadata(); + bencode(std::back_inserter(info_section_buf), gen_info_section); + assert(hasher(&info_section_buf[0], info_section_buf.size()).final() + == m_info_hash); +#endif + } + + // extracts information from a libtorrent file and fills in the structures in + // the torrent object + void torrent_info::read_torrent_info(const entry& torrent_file) + { + // extract the url of the tracker + if (entry const* i = torrent_file.find_key("announce-list")) + { + const entry::list_type& l = i->list(); + for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j) + { + const entry::list_type& ll = j->list(); + for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k) + { + announce_entry e(k->string()); + e.tier = (int)std::distance(l.begin(), j); + m_urls.push_back(e); + } + } + + if (m_urls.size() == 0) + { + // the announce-list is empty + // fall back to look for announce + m_urls.push_back(announce_entry( + torrent_file["announce"].string())); + } + // shuffle each tier + std::vector::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); + } + else if (entry const* i = torrent_file.find_key("announce")) + { + m_urls.push_back(announce_entry(i->string())); + } + + if (entry const* i = torrent_file.find_key("nodes")) + { + entry::list_type const& list = i->list(); + for (entry::list_type::const_iterator i(list.begin()) + , end(list.end()); i != end; ++i) + { + if (i->type() != entry::list_t) continue; + entry::list_type const& l = i->list(); + entry::list_type::const_iterator iter = l.begin(); + if (l.size() < 1) continue; + std::string const& hostname = iter->string(); + ++iter; + int port = 6881; + if (l.end() != iter) port = iter->integer(); + m_nodes.push_back(std::make_pair(hostname, port)); + } + } + + // extract creation date + try + { + m_creation_date = ptime(date(1970, Jan, 1)) + + seconds(long(torrent_file["creation date"].integer())); + } + catch (type_error) {} + + // if there are any url-seeds, extract them + try + { + entry const& url_seeds = torrent_file["url-list"]; + if (url_seeds.type() == entry::string_t) + { + m_url_seeds.push_back(url_seeds.string()); + } + else if (url_seeds.type() == entry::list_t) + { + entry::list_type const& l = url_seeds.list(); + for (entry::list_type::const_iterator i = l.begin(); + i != l.end(); ++i) + { + m_url_seeds.push_back(i->string()); + } + } + } + catch (type_error&) {} + + // extract comment + if (entry const* e = torrent_file.find_key("comment.utf-8")) + { m_comment = e->string(); } + else if (entry const* e = torrent_file.find_key("comment")) + { m_comment = e->string(); } + + if (entry const* e = torrent_file.find_key("created by.utf-8")) + { m_created_by = e->string(); } + else if (entry const* e = torrent_file.find_key("created by")) + { m_created_by = e->string(); } + + parse_info_section(torrent_file["info"]); + } + + boost::optional + torrent_info::creation_date() const + { + if (m_creation_date != ptime(date(not_a_date_time))) + { + 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; + m_urls.push_back(e); + + using boost::bind; + std::sort(m_urls.begin(), m_urls.end(), boost::bind(std::less() + , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2))); + } + + void torrent_info::add_file(boost::filesystem::path file, size_type size) + { + assert(file.begin() != file.end()); + + if (!file.has_branch_path()) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + assert(m_files.empty()); + assert(!m_multifile); + m_name = file.string(); + } + else + { +#ifndef NDEBUG + if (!m_files.empty()) + assert(m_name == *file.begin()); +#endif + m_multifile = true; + m_name = *file.begin(); + } + + file_entry e; + e.path = file; + e.size = size; + m_files.push_back(e); + + m_total_size += size; + + int num_pieces = static_cast( + (m_total_size + m_piece_length - 1) / m_piece_length); + int old_num_pieces = static_cast(m_piece_hash.size()); + + m_piece_hash.resize(num_pieces); + for (std::vector::iterator i = m_piece_hash.begin() + old_num_pieces; + i != m_piece_hash.end(); ++i) + { + i->clear(); + } + + } + + void torrent_info::add_url_seed(std::string const& url) + { + m_url_seeds.push_back(url); + } + + void torrent_info::set_comment(char const* str) + { + m_comment = str; + } + + void torrent_info::set_creator(char const* str) + { + m_created_by = str; + } + + entry torrent_info::create_info_metadata() const + { + namespace fs = boost::filesystem; + + // you have to add files to the torrent first + assert(!m_files.empty()); + + entry info(m_extra_info); + + if (!info.find_key("name")) + info["name"] = m_name; + + if (!m_multifile) + { + info["length"] = m_files.front().size; + } + else + { + if (!info.find_key("files")) + { + entry& files = info["files"]; + files = entry(entry::list_t); + + for (std::vector::const_iterator i = m_files.begin(); + i != m_files.end(); ++i) + { + files.list().push_back(entry(entry::dictionary_t)); + entry& file_e = files.list().back(); + file_e["length"] = i->size; + entry& path_e = file_e["path"]; + path_e = entry(entry::list_t); + + fs::path const* file_path; + if (i->orig_path) file_path = &(*i->orig_path); + else file_path = &i->path; + assert(file_path->has_branch_path()); + assert(*file_path->begin() == m_name); + + for (fs::path::iterator j = boost::next(file_path->begin()); + j != file_path->end(); ++j) + { + path_e.list().push_back(entry(*j)); + } + } + } + } + + info["piece length"] = piece_length(); + entry& pieces = info["pieces"]; + pieces = entry(entry::string_t); + + std::string& p = pieces.string(); + + for (std::vector::const_iterator i = m_piece_hash.begin(); + i != m_piece_hash.end(); ++i) + { + p.append((char*)i->begin(), (char*)i->end()); + } + + return info; + } + + entry torrent_info::create_torrent() const + { + assert(m_piece_length > 0); + + using namespace boost::gregorian; + using namespace boost::posix_time; + + namespace fs = boost::filesystem; + + entry dict(entry::dictionary_t); + + if ((m_urls.empty() && m_nodes.empty()) || m_files.empty()) + { + // TODO: throw something here + // throw + return entry(); + } + + if (m_private) dict["private"] = 1; + + if (!m_urls.empty()) + dict["announce"] = m_urls.front().url; + + if (!m_nodes.empty()) + { + entry& nodes = dict["nodes"]; + nodes = entry(entry::list_t); + 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().tier; + for (std::vector::const_iterator i = m_urls.begin(); + i != m_urls.end(); ++i) + { + if (i->tier != current_tier) + { + current_tier = i->tier; + trackers.list().push_back(tier); + tier.list().clear(); + } + tier.list().push_back(entry(i->url)); + } + trackers.list().push_back(tier); + dict["announce-list"] = trackers; + } + + if (!m_comment.empty()) + dict["comment"] = m_comment; + + dict["creation date"] = + (m_creation_date - ptime(date(1970, Jan, 1))).total_seconds(); + + 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"]; + list = entry(entry::list_t); + for (std::vector::const_iterator i + = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + dict["info"] = create_info_metadata(); + + entry const& info_section = dict["info"]; + std::vector buf; + bencode(std::back_inserter(buf), info_section); + m_info_hash = hasher(&buf[0], buf.size()).final(); + + return dict; + } + + void torrent_info::set_hash(int index, const sha1_hash& h) + { + assert(index >= 0); + assert(index < (int)m_piece_hash.size()); + m_piece_hash[index] = h; + } + + void torrent_info::convert_file_names() + { + assert(false); + } + + 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"; + if (m_creation_date != ptime(date(not_a_date_time))) + os << "creation date: " << to_simple_string(m_creation_date) << "\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_iterator i = begin_files(); i != end_files(); ++i) + os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n"; + } + + size_type torrent_info::piece_size(int index) const + { + assert(index >= 0 && index < num_pieces()); + if (index == num_pieces()-1) + { + size_type size = total_size() + - (num_pieces() - 1) * piece_length(); + assert(size > 0); + assert(size <= piece_length()); + return size; + } + else + return piece_length(); + } + + void torrent_info::add_node(std::pair const& node) + { + m_nodes.push_back(node); + } + + std::vector torrent_info::map_block(int piece, size_type offset + , int size) const + { + assert(num_files() > 0); + std::vector ret; + + size_type start = piece * (size_type)m_piece_length + offset; + assert(start + size <= m_total_size); + + // find the file iterator and file offset + // TODO: make a vector that can map piece -> file index in O(1) + size_type file_offset = start; + std::vector::const_iterator file_iter; + + int counter = 0; + for (file_iter = begin_files();; ++counter, ++file_iter) + { + assert(file_iter != end_files()); + if (file_offset < file_iter->size) + { + file_slice f; + f.file_index = counter; + f.offset = file_offset; + f.size = (std::min)(file_iter->size - file_offset, (size_type)size); + size -= f.size; + file_offset += f.size; + ret.push_back(f); + } + + assert(size >= 0); + if (size <= 0) break; + + file_offset -= file_iter->size; + } + return ret; + } + + peer_request torrent_info::map_file(int file_index, size_type file_offset + , int size) const + { + assert(file_index < (int)m_files.size()); + assert(file_index >= 0); + size_type offset = file_offset + m_files[file_index].offset; + + peer_request ret; + ret.piece = offset / piece_length(); + ret.start = offset - ret.piece * piece_length(); + ret.length = size; + return ret; + } + +} diff --git a/library/tracker_manager.cpp b/library/tracker_manager.cpp new file mode 100755 index 000000000..20e234c1f --- /dev/null +++ b/library/tracker_manager.cpp @@ -0,0 +1,569 @@ +/* + +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 +#include +#include +#include + +#include "zlib.h" + +#include + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" + +using namespace libtorrent; +using boost::tuples::make_tuple; +using boost::tuples::tuple; +using boost::bind; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + using boost::posix_time::second_clock; + using boost::posix_time::seconds; + using boost::posix_time::ptime; + using boost::posix_time::time_duration; + + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size) + { + assert(buf != 0); + assert(size > 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) 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 != Z_DEFLATED || (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( + std::vector& buffer + , tracker_request const& req + , request_callback* requester + , int maximum_tracker_response_length) + { + assert(maximum_tracker_response_length > 0); + + int header_len = gzip_header(&buffer[0], (int)buffer.size()); + if (header_len < 0) + { + requester->tracker_request_error(req, 200, "invalid gzip header in tracker response"); + return true; + } + + // start off wth one kilobyte and grow + // if needed + std::vector inflate_buffer(1024); + + // initialize the zlib-stream + z_stream str; + + // subtract 8 from the end of the buffer since that's CRC32 and input size + // and those belong to the gzip file + str.avail_in = (int)buffer.size() - header_len - 8; + str.next_in = reinterpret_cast(&buffer[header_len]); + str.next_out = reinterpret_cast(&inflate_buffer[0]); + str.avail_out = (int)inflate_buffer.size(); + str.zalloc = Z_NULL; + str.zfree = Z_NULL; + str.opaque = 0; + // -15 is really important. It will make inflate() not look for a zlib header + // and just deflate the buffer + if (inflateInit2(&str, -15) != Z_OK) + { + requester->tracker_request_error(req, 200, "gzip out of memory"); + return true; + } + + // inflate and grow inflate_buffer as needed + int ret = inflate(&str, Z_SYNC_FLUSH); + while (ret == Z_OK) + { + if (str.avail_out == 0) + { + if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length) + { + inflateEnd(&str); + requester->tracker_request_error(req, 200 + , "tracker response too large"); + return true; + } + int new_size = (int)inflate_buffer.size() * 2; + if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length; + int old_size = (int)inflate_buffer.size(); + + inflate_buffer.resize(new_size); + str.next_out = reinterpret_cast(&inflate_buffer[old_size]); + str.avail_out = new_size - old_size; + } + + ret = inflate(&str, Z_SYNC_FLUSH); + } + + inflate_buffer.resize(inflate_buffer.size() - str.avail_out); + inflateEnd(&str); + + if (ret != Z_STREAM_END) + { + requester->tracker_request_error(req, 200, "gzip error"); + return true; + } + + // commit the resulting buffer + std::swap(buffer, inflate_buffer); + return false; + } + + 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)std::distance(i, s.end())); + + // clear input buffer + std::fill(inbuf, inbuf+3, 0); + + // read a chunk of input into inbuf + for (int j = 0; j < available_input; ++j) + { + inbuf[j] = *i; + ++i; + } + + // 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; + } + + void intrusive_ptr_add_ref(timeout_handler const* c) + { + assert(c != 0); + assert(c->m_refs >= 0); + timeout_handler::mutex_t::scoped_lock l(c->m_mutex); + ++c->m_refs; + } + + void intrusive_ptr_release(timeout_handler const* c) + { + assert(c != 0); + assert(c->m_refs > 0); + timeout_handler::mutex_t::scoped_lock l(c->m_mutex); + --c->m_refs; + if (c->m_refs == 0) + { + l.unlock(); + delete c; + } + } + + + timeout_handler::timeout_handler(demuxer& d) + : m_demuxer(d) + , m_start_time(second_clock::universal_time()) + , m_read_time(second_clock::universal_time()) + , m_timeout(d) + , m_completion_timeout(0) + , m_read_timeout(0) + , m_refs(0) + {} + + void timeout_handler::set_timeout(int completion_timeout, int read_timeout) + { + m_completion_timeout = completion_timeout; + m_read_timeout = read_timeout; + m_start_time = second_clock::universal_time(); + m_read_time = second_clock::universal_time(); + + m_timeout.expires_at(std::min( + m_read_time + seconds(m_read_timeout) + , m_start_time + seconds(m_completion_timeout))); + m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1)); + } + + void timeout_handler::restart_read_timeout() + { + m_read_time = second_clock::universal_time(); + } + + void timeout_handler::cancel() + { + m_completion_timeout = 0; + m_timeout.cancel(); + } + + void timeout_handler::timeout_callback(asio::error const& error) try + { + if (error) return; + if (m_completion_timeout == 0) return; + + ptime now(second_clock::universal_time()); + time_duration receive_timeout = now - m_read_time; + time_duration completion_timeout = now - m_start_time; + + if (m_read_timeout + < receive_timeout.total_seconds() + || m_completion_timeout + < completion_timeout.total_seconds()) + { + on_timeout(); + return; + } + + m_timeout.expires_at(std::min( + m_read_time + seconds(m_read_timeout) + , m_start_time + seconds(m_completion_timeout))); + m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1)); + } + catch (std::exception& e) + { + assert(false); + } + + tracker_connection::tracker_connection( + tracker_manager& man + , tracker_request req + , demuxer& d + , boost::weak_ptr r) + : timeout_handler(d) + , m_requester(r) + , m_man(man) + , m_req(req) + {} + + request_callback& tracker_connection::requester() + { + boost::shared_ptr r = m_requester.lock(); + assert(r); + return *r; + } + + void tracker_connection::fail(int code, char const* msg) + { + if (has_requester()) requester().tracker_request_error( + m_req, code, msg); + close(); + } + + void tracker_connection::fail_timeout() + { + if (has_requester()) requester().tracker_request_timed_out(m_req); + close(); + } + + void tracker_connection::close() + { + cancel(); + m_man.remove_request(this); + } + + 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); + } + + tuple + parse_url_components(std::string url) + { + std::string hostname; // hostname only + std::string protocol; // should be http + int port = 80; + + // 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 = std::string(start, end); + + if (end == url.end()) throw std::runtime_error("invalid url"); + ++end; + if (end == url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + if (end == url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + start = end; + + end = std::find(start, url.end(), '/'); + std::string::iterator port_pos + = std::find(start, url.end(), ':'); + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + try + { + port = boost::lexical_cast(std::string(port_pos, end)); + } + catch(boost::bad_lexical_cast&) + { + throw std::runtime_error("invalid url: \"" + url + + "\", port number expected"); + } + } + else + { + hostname.assign(start, end); + } + + start = end; + return make_tuple(protocol, hostname, port + , std::string(start, url.end())); + } + + void tracker_manager::queue_request( + demuxer& d + , tracker_request req + , std::string const& auth + , boost::weak_ptr c) + { + mutex_t::scoped_lock l(m_mutex); + assert(req.num_want >= 0); + if (req.event == tracker_request::stopped) + req.num_want = 0; + + try + { + std::string protocol; + std::string hostname; + int port; + std::string request_string; + + boost::tie(protocol, hostname, port, request_string) + = parse_url_components(req.url); + + boost::intrusive_ptr con; + + if (protocol == "http") + { + con = new http_tracker_connection( + d + , *this + , req + , hostname + , port + , request_string + , c + , m_settings + , auth); + } + else if (protocol == "udp") + { + con = new udp_tracker_connection( + d + , *this + , req + , hostname + , port + , c + , m_settings); + } + else + { + throw std::runtime_error("unkown protocol in tracker url"); + } + + m_connections.push_back(con); + + if (con->has_requester()) con->requester().m_manager = this; + } + catch (std::exception& e) + { + if (boost::shared_ptr r = c.lock()) + r->tracker_request_error(req, -1, e.what()); + } + } + + void tracker_manager::abort_all_requests() + { + // removes all connections from m_connections + // except those with a requester == 0 (since those are + // 'event=stopped'-requests) + mutex_t::scoped_lock l(m_mutex); + + tracker_connections_t keep_connections; + + for (tracker_connections_t::const_iterator i = + m_connections.begin(); i != m_connections.end(); ++i) + { + tracker_request const& req = (*i)->tracker_req(); + if (req.event == tracker_request::stopped) + keep_connections.push_back(*i); + } + + std::swap(m_connections, keep_connections); + } + + bool tracker_manager::empty() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.empty(); + } + +} diff --git a/library/udp_tracker_connection.cpp b/library/udp_tracker_connection.cpp new file mode 100755 index 000000000..2a1e97d1a --- /dev/null +++ b/library/udp_tracker_connection.cpp @@ -0,0 +1,522 @@ +/* + +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 +#include +#include +#include + +#include "zlib.h" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/io.hpp" + +namespace +{ + enum + { + udp_connection_retries = 4, + udp_announce_retries = 15, + udp_connect_timeout = 15, + udp_announce_timeout = 10, + udp_buffer_size = 2048 + }; +} + +using namespace boost::posix_time; +using boost::bind; +using boost::lexical_cast; + +namespace libtorrent +{ + + udp_tracker_connection::udp_tracker_connection( + demuxer& d + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , boost::weak_ptr c + , session_settings const& stn) + : tracker_connection(man, req, d, c) + , m_man(man) + , m_name_lookup(d) + , m_port(port) + , m_transaction_id(0) + , m_connection_id(0) + , m_settings(stn) + , m_attempts(0) + { + m_socket.reset(new datagram_socket(d)); + tcp::resolver::query q(hostname, "0"); + m_name_lookup.async_resolve(q + , boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2)); + set_timeout(m_settings.tracker_completion_timeout + , m_settings.tracker_receive_timeout); + } + + void udp_tracker_connection::name_lookup(asio::error const& error + , tcp::resolver::iterator i) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error || i == tcp::resolver::iterator()) + { + fail(-1, error.what()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("udp tracker name lookup successful"); +#endif + restart_read_timeout(); + m_target = udp::endpoint(i->endpoint().address(), m_port); + if (has_requester()) requester().m_tracker_address + = tcp::endpoint(i->endpoint().address(), m_port); + m_socket->connect(m_target); + send_udp_connect(); + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; + + void udp_tracker_connection::on_timeout() + { + m_socket.reset(); + m_name_lookup.cancel(); + fail_timeout(); + } + + void udp_tracker_connection::send_udp_connect() + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> UDP_TRACKER_CONNECT [" + + lexical_cast(tracker_req().info_hash) + "]"); + } +#endif + if (!m_socket) return; // the operation was aborted + + char send_buf[16]; + char* ptr = send_buf; + + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + // connection_id + detail::write_uint32(0x417, ptr); + detail::write_uint32(0x27101980, ptr); + // action (connect) + detail::write_int32(action_connect, ptr); + // transaction_id + detail::write_int32(m_transaction_id, ptr); + + m_socket->send(asio::buffer((void*)send_buf, 16), 0); + ++m_attempts; + m_buffer.resize(udp_buffer_size); + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + } + + void udp_tracker_connection::connect_response(asio::error const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.what()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + + const char* ptr = &m_buffer[0]; + int action = detail::read_int32(ptr); + int transaction = detail::read_int32(ptr); + + if (action == action_error) + { + fail(-1, std::string(ptr, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_connect) + { + fail(-1, "invalid action in connect reply"); + return; + } + + if (m_transaction_id != transaction) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (bytes_transferred < 16) + { + fail(-1, "udp_tracker_connection: " + "got a message with size < 16"); + return; + } + // reset transaction + m_transaction_id = 0; + m_attempts = 0; + m_connection_id = detail::read_int64(ptr); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE [" + + lexical_cast(m_connection_id) + "]"); + } +#endif + + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + } + catch (std::exception& e) + { + fail(-1, e.what()); + } + + void udp_tracker_connection::send_udp_announce() + { + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + if (!m_socket) return; // the operation was aborted + + std::vector buf; + std::back_insert_iterator > out(buf); + + tracker_request const& req = tracker_req(); + + // connection_id + detail::write_int64(m_connection_id, out); + // action (announce) + detail::write_int32(action_announce, out); + // transaction_id + detail::write_int32(m_transaction_id, out); + // info_hash + std::copy(req.info_hash.begin(), req.info_hash.end(), out); + // peer_id + std::copy(req.pid.begin(), req.pid.end(), out); + // downloaded + detail::write_int64(req.downloaded, out); + // left + detail::write_int64(req.left, out); + // uploaded + detail::write_int64(req.uploaded, out); + // event + detail::write_int32(req.event, out); + // ip address + detail::write_int32(0, out); + // key + detail::write_int32(req.key, out); + // num_want + detail::write_int32(req.num_want, out); + // port + detail::write_uint16(req.listen_port, out); + // extensions + detail::write_uint16(0, out); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> UDP_TRACKER_ANNOUNCE [" + + lexical_cast(req.info_hash) + "]"); + } +#endif + + m_socket->send(asio::buffer(buf), 0); + ++m_attempts; + + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::announce_response, self(), _1, _2)); + } + + void udp_tracker_connection::send_udp_scrape() + { + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + if (!m_socket) return; // the operation was aborted + + std::vector buf; + std::back_insert_iterator > out(buf); + + // connection_id + detail::write_int64(m_connection_id, out); + // action (scrape) + detail::write_int32(action_scrape, out); + // transaction_id + detail::write_int32(m_transaction_id, out); + // info_hash + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); + + m_socket->send(asio::buffer(&buf[0], buf.size()), 0); + ++m_attempts; + + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::scrape_response, self(), _1, _2)); + } + + void udp_tracker_connection::announce_response(asio::error const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.what()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + char* buf = &m_buffer[0]; + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (action == action_error) + { + fail(-1, std::string(buf, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_announce) + { + fail(-1, "invalid action in announce response"); + return; + } + + if (bytes_transferred < 20) + { + fail(-1, "got a message with size < 20"); + return; + } + + int interval = detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + int complete = detail::read_int32(buf); + int num_peers = (bytes_transferred - 20) / 6; + if ((bytes_transferred - 20) % 6 != 0) + { + fail(-1, "invalid udp tracker response length"); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE"); + } +#endif + + if (!has_requester()) + { + m_man.remove_request(this); + return; + } + + std::vector peer_list; + for (int i = 0; i < num_peers; ++i) + { + peer_entry e; + std::stringstream s; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf); + e.ip = s.str(); + e.port = detail::read_uint16(buf); + e.pid.clear(); + peer_list.push_back(e); + } + + requester().tracker_response(tracker_req(), peer_list, interval + , complete, incomplete); + + m_man.remove_request(this); + return; + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; // msvc 7.1 seems to require this + + void udp_tracker_connection::scrape_response(asio::error const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.what()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + char* buf = &m_buffer[0]; + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (action == action_error) + { + fail(-1, std::string(buf, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_scrape) + { + fail(-1, "invalid action in announce response"); + return; + } + + if (bytes_transferred < 20) + { + fail(-1, "got a message with size < 20"); + return; + } + + int complete = detail::read_int32(buf); + /*int downloaded = */detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + + if (!has_requester()) + { + m_man.remove_request(this); + return; + } + + std::vector peer_list; + requester().tracker_response(tracker_req(), peer_list, 0 + , complete, incomplete); + + m_man.remove_request(this); + } + catch (std::exception& e) + { + fail(-1, e.what()); + } + +} + diff --git a/library/web_peer_connection.cpp b/library/web_peer_connection.cpp new file mode 100755 index 000000000..5be9610f4 --- /dev/null +++ b/library/web_peer_connection.cpp @@ -0,0 +1,455 @@ +/* + +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 +#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" + +using namespace boost::posix_time; +using boost::bind; +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) + : peer_connection(ses, t, s, remote) + , m_url(url) + , m_first_request(true) + { + INVARIANT_CHECK; + + m_max_out_request_queue = ses.settings().urlseed_pipeline_size; + + // since this is a web seed, change the timeout + // according to the settings. + set_timeout(ses.settings().urlseed_timeout); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** web_peer_connection\n"; +#endif + + std::string protocol; + boost::tie(protocol, m_host, m_port, m_path) + = parse_url_components(url); + + m_server_string = "URL seed @ "; + m_server_string += m_host; + } + + web_peer_connection::~web_peer_connection() + {} + + boost::optional + web_peer_connection::downloading_piece_progress() const + { + if (!m_parser.header_finished() || m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + int body_start = m_parser.body_start(); + buffer::const_interval recv_buffer = receive_buffer(); + assert(body_start <= recv_buffer.left()); + piece_block_progress ret; + + ret.piece_index = m_requests.front().piece; + ret.block_index = m_requests.front().start / t->block_size(); + ret.bytes_downloaded = recv_buffer.left() - body_start; + ret.full_block_bytes = m_requests.front().length; + return ret; + } + + void web_peer_connection::on_connected() + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + // this is always a seed + incoming_bitfield(std::vector( + t->torrent_file().num_pieces(), true)); + // it is always possible to request pieces + incoming_unchoke(); + + reset_recv_buffer(512*1024+1024); + } + + void web_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + assert(t->valid_metadata()); + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + torrent_info const& info = t->torrent_file(); + + std::string request; + + m_requests.push_back(r); + + bool using_proxy = false; + if (!m_ses.settings().proxy_ip.empty()) + using_proxy = true; + + if (single_file_request) + { + request += "GET "; + if (using_proxy) request += m_url; + else request += escape_path(m_path.c_str(), m_path.length()); + request += " HTTP/1.1\r\n"; + request += "Host: "; + request += m_host; + if (m_first_request) + { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (using_proxy && !m_ses.settings().proxy_login.empty()) + { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(m_ses.settings().proxy_login + ":" + + m_ses.settings().proxy_password); + } + if (using_proxy) + { + request += "\r\nProxy-Connection: keep-alive"; + } + request += "\r\nRange: bytes="; + request += boost::lexical_cast(r.piece + * info.piece_length() + r.start); + request += "-"; + request += boost::lexical_cast(r.piece + * info.piece_length() + r.start + r.length - 1); + if (m_first_request || using_proxy) + request += "\r\nConnection: keep-alive"; + request += "\r\n\r\n"; + m_first_request = false; + m_file_requests.push_back(0); + } + else + { + std::vector files = info.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.file_at(f.file_index).path.string(); + request += escape_path(path.c_str(), path.length()); + } + else + { + std::string path = m_path; + path += info.file_at(f.file_index).path.string(); + request += escape_path(path.c_str(), path.length()); + } + request += " HTTP/1.1\r\n"; + request += "Host: "; + request += m_host; + if (m_first_request) + { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (using_proxy && !m_ses.settings().proxy_login.empty()) + { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(m_ses.settings().proxy_login + ":" + + m_ses.settings().proxy_password); + } + if (using_proxy) + { + request += "\r\nProxy-Connection: keep-alive"; + } + request += "\r\nRange: bytes="; + request += boost::lexical_cast(f.offset); + request += "-"; + request += boost::lexical_cast(f.offset + f.size - 1); + if (m_first_request || using_proxy) + request += "\r\nConnection: keep-alive"; + request += "\r\n\r\n"; + m_first_request = false; + m_file_requests.push_back(f.file_index); + } + } + + send_buffer(request.c_str(), request.c_str() + request.size()); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + // throws exception when the client should be disconnected + void web_peer_connection::on_receive(const asio::error& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + incoming_piece_fragment(); + + for (;;) + { + buffer::const_interval recv_buffer = receive_buffer(); + int payload; + int protocol; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); + m_statistics.received_bytes(payload, protocol); + + if (m_parser.status_code() != 206 && m_parser.status_code() != -1) + { + // we should not try this server again. + t->remove_url_seed(m_url); + if (m_parser.status_code() == 404) + throw std::runtime_error("File not found on server"); + throw std::runtime_error("HTTP server does not support byte range requests"); + } + + if (!m_parser.finished()) break; + + std::string 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 += ")"; + } + + std::stringstream range_str(m_parser.header("Content-Range")); + size_type range_start; + size_type range_end; + char dummy; + std::string bytes; + range_str >> bytes >> range_start >> dummy >> range_end; + if (!range_str) + { + // we should not try this server again. + t->remove_url_seed(m_url); + throw std::runtime_error("invalid range in HTTP response: " + range_str.str()); + } + // the http range is inclusive + range_end++; + + torrent_info const& info = t->torrent_file(); + + if (m_requests.empty() || m_file_requests.empty()) + throw std::runtime_error("unexpected HTTP response"); + + int file_index = m_file_requests.front(); + m_file_requests.pop_front(); + + peer_request r = info.map_file(file_index, range_start + , range_end - range_start); + + buffer::const_interval http_body = m_parser.get_body(); + + if (r == m_requests.front()) + { + m_requests.pop_front(); + incoming_piece(r, http_body.begin); + cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); + return; + } + + if (!m_piece.empty()) + { + // this is not the first partial request we get + if (m_intermediate_piece.start + m_intermediate_piece.length != r.start + || m_intermediate_piece.piece != r.piece) + { + throw std::runtime_error("invalid range in HTTP response"); + } + } + else + { + // this is the first part of a partial request + if (r.start != m_requests.front().start + || r.piece != m_requests.front().piece) + { + throw std::runtime_error("invalid range in HTTP response"); + } + m_intermediate_piece.piece = r.piece; + m_intermediate_piece.start = r.start; + m_intermediate_piece.length = 0; + } + + m_piece.reserve(info.piece_length()); + std::copy(http_body.begin, http_body.end, back_inserter(m_piece)); + m_intermediate_piece.length += r.length; + if (m_intermediate_piece.length == m_requests.front().length) + { + assert(m_requests.front() == m_intermediate_piece); + assert(int(m_piece.size()) == m_intermediate_piece.length); + m_requests.pop_front(); + incoming_piece(m_intermediate_piece, &m_piece[0]); + m_piece.clear(); + } + else if (m_intermediate_piece.length > m_requests.front().length) + { + throw std::runtime_error("too large HTTP response body"); + } + + cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); + } + } + + // -------------------------- + // SEND DATA + // -------------------------- + + void web_peer_connection::get_peer_info(peer_info& p) const + { + assert(!associated_torrent().expired()); + + 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.total_download = statistics().total_payload_download(); + p.total_upload = statistics().total_payload_upload(); + + if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) + p.upload_limit = -1; + else + p.upload_limit = m_ul_bandwidth_quota.given; + + if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) + p.download_limit = -1; + else + p.download_limit = m_dl_bandwidth_quota.given; + + p.load_balancing = total_free_upload(); + + p.download_queue_length = (int)download_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.flags = 0; + 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 (is_local()) p.flags |= peer_info::local_connection; + 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.pieces = get_bitfield(); + p.seed = is_seed(); + + p.client = m_server_string; + p.connection_type = peer_info::web_seed; + } + + // throws exception when the client should be disconnected + void web_peer_connection::on_sent(asio::error const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + m_statistics.sent_bytes(0, bytes_transferred); + } + + +#ifndef NDEBUG + void web_peer_connection::check_invariant() const + { +/* + assert(m_num_pieces == std::count( + m_have_piece.begin() + , m_have_piece.end() + , true)); +*/ } +#endif + +} +